1
votes

I have followed the documentation on the UI-Router site to the point that my code for configuring the $StateProvider is practically identical, but I cannot get my Components to load. I am able to see the html pages referenced in my templateUrl but the controllers are not being accessed. Also, when I provide a Controller reference in the state route it errors suggesting that the controller is not register

The first component on the index page:

<supply-chain-admin ng-cloak></supply-chain-admin>

(function () {
"use strict"

var module = angular.module("SCAdmin");

function AppController($timeout) {

    var vm = this; // vm = ViewModel
    vm.isNewBlankLoad = false;
    vm.selectedMenuItem;
    vm.changeMenu = true;

    vm.$onInit = function () {
        vm.changeMenu = true;
    }

    vm.collapseMainMenu = function (isCollapsed) {
        vm.changeMenu = isCollapsed;
        $timeout(function () { equalheight('.menuListItem'); }, 10);
    };
}

module.component("supplyChainAdmin", {
    templateUrl: "/Scripts/SupplyChainAdmin/SCAdminApp/SupplyChainAdmin.html",
    bindings: {
    },
    controllerAs: "vm",
    controller: ['$timeout', AppController]
});

})();

The inside this components html file:

<main-menu show-menu="vm.changeMenu"></main-menu>
<main-content show-menu="vm.changeMenu"></main-content>

Inside the main-menu component:

<div id="menuleft" ng-class="{hideMenu:!vm.showMenu,slide:vm.showMenu}">
<div id="mainMenuHeader">
    <a class="pull-right" id="mainMenuToggleLink" ng-click="vm.toggleMenu()">
        <img src="/Content/images/menu.png" />
    </a>
</div>
<div id="mainMenu">
    <ul class="menuList" ng-class="{hideMenu:vm.showMenu,slide:!vm.showMenu}"><!-- the :: symbol represents one time binding -->
        <li title="{{ ::menuItem.ToolTip }}" class="menuListItem" ng-repeat="menuItem in vm.menuItems" ng-class="{ active: menuItem.Link == vm.selectedMenuItem }" resize-normal>
            <button class="menuNotifications pull-right" ng-show="menuItem.Notification.length" popover="{{ ::menuItem.NotificationMessage }}" popover-trigger="focus" popover-placement="right" popover-append-to-body="true">{{ ::menuItem.Notification }}</button>
            <a ng-click="vm.menuItemChanged(menuItem.Link)" ui-sref="{{ ::menuItem.Link }}">
                <h1 ng-show="vm.showMenu"><span class="{{ ::menuItem.MenuIconClass }}"></span> {{ ::menuItem.Title }} <i class="line"></i></h1>
                <p ng-hide="vm.showMenu"><span class="{{ ::menuItem.MenuIconClass }}"></span> <i class="line"></i></p>

                <div class="menuItemContent" ng-class="{hide:!vm.showMenu}">
                    <ul class="list-unstyled list-inline">
                        <li ng-repeat="pointItem in menuItem.MenuItemPoints">
                            <div class="fa large-glyphicon {{ ::pointItem.IconClass }}" aria-hidden="true"></div>
                            <div class="menuSubItemText">&nbsp;{{ ::pointItem.DisplayText }}&nbsp;</div>
                            <div class="menuSubItemValue">&nbsp;{{ ::pointItem.DisplayValue }}&nbsp;</div>
                        </li>
                    </ul>
                </div>
            </a>
        </li>
    </ul>
</div>

(function () {
"use strict"

var module = angular.module("SCAdmin");

module.component("mainMenu", {
    templateUrl: "/Scripts/SupplyChainAdmin/MainMenu/MainMenu.html",
    bindings: {
        showMenu: "="
    },
    controllerAs: "vm",
    controller: ["$http", '$timeout', '$state', mainMenuController]
});

function mainMenuController($http, $timeout, $state) {

    var vm = this; // vm = ViewModel

    vm.isNewBlankLoad = false;
    vm.menuItems = [];

    vm.selectedMenuItem;

    vm.$onInit = function () {
        fetchMenuItems($http, $state).then(function (data) {
            if (data.length) {

                for (var i = 0, l = data.length; i < l; i++) {
                    if (data[i].MenuItemType === 1) {

                        vm.menuItems.push({ Link: 'Items', ToolTip: 'Items', Title: 'My Items', MenuItemType: 1, Notification: data[i].Notification, NotificationMessage: data[i].NotificationMessage, MenuItemPoints: data[i].MenuItemPoints, MenuIconClass: 'fa fa-tags' });
                    }
                    if (data[i].MenuItemType === 2) {
                        vm.menuItems.push({ Link: 'Vendors', ToolTip: 'Vendor', Title: 'My Vendors', MenuItemType: 2, Notification: data[i].Notification, NotificationMessage: data[i].NotificationMessage, MenuItemPoints: data[i].MenuItemPoints, MenuIconClass: 'fa fa-id-card' });
                    }
                    if (data[i].MenuItemType === 3) {
                        vm.menuItems.push({ Link: 'Facilities', ToolTip: 'Facilities', Title: 'My Facilities', MenuItemType: 2, Notification: data[i].Notification, NotificationMessage: data[i].NotificationMessage, MenuItemPoints: data[i].MenuItemPoints, MenuIconClass: 'fa fa-university' });
                    }
                    if (data[i].MenuItemType === 4) {
                        vm.menuItems.push({ Link: 'Departments', ToolTip: 'Departments', Title: 'My Departments', MenuItemType: 2, Notification: data[i].Notification, NotificationMessage: data[i].NotificationMessage, MenuItemPoints: data[i].MenuItemPoints, MenuIconClass: 'fa fa-bed' });
                    }
                    if (data[i].MenuItemType === 5) {
                        vm.menuItems.push({ Link: 'Pars', ToolTip: 'Pars', Title: 'My PARs', MenuItemType: 2, Notification: data[i].Notification, NotificationMessage: data[i].NotificationMessage, MenuItemPoints: data[i].MenuItemPoints, MenuIconClass: 'fa fa-stethoscope' });
                    }
                    if (data[i].MenuItemType === 6) {
                        vm.menuItems.push({ Link: 'Maintenance', ToolTip: 'Maintenance', Title: 'Maintenance', MenuItemType: 2, Notification: data[i].Notification, NotificationMessage: data[i].NotificationMessage, MenuItemPoints: data[i].MenuItemPoints, MenuIconClass: 'fa fa-wrench' });
                    }
                }

                vm.selectedMenuItem = $state.current.name != null ? $state.current.name : vm.menuItems[0].ToolTip;
            }
        });
    };

    vm.menuItemChanged = function (selectMenuItem) {
        vm.selectedMenuItem = selectMenuItem;
    }

    vm.menuItems = vm.menuItems;

    vm.toggleMenu = function () {
        vm.showMenu = (vm.showMenu) ? false : true;
        $timeout(function () { equalheight('.menuListItem'); }, 10);
    }
}

function fetchMenuItems($http) {
    return $http.get(SupplyChainAdminUrl + 'Menu/GetMenuItems/', { params: { domain: CurrentUserDomain, userName: CurrentUserUserName } }).then(function (response) {
        return response.data;
    });
}

})();

Inside the main-content component:

<div id="contentFromMenu" ng-class="{hideMenu:vm.showMenu,slide:!vm.showMenu}">
<div class="overlay" ng-class="{show:vm.showMenu}" ng-click="vm.collapseMenu()"></div>
<div id="menuContentContainer">
    <ui-view></ui-view>
</div>

(function () {

"use strict"

var module = angular.module("SCAdmin");

module.component("mainContent", {
    templateUrl: "/Scripts/SupplyChainAdmin/MainContent/MainContent.html",
    require: {
        'parent': '^supplyChainAdmin'
    },
    bindings: {
        showMenu: "<"
    },
    controllerAs: "vm",
    controller: ["$http", '$timeout', mainContentController]

});

function mainContentController($timeout) {

    var vm = this; // vm = Viewvm

    vm.$onInit = function () {

    }

    vm.collapseMenu = function () {
        //the parameter sets the show menu property to false;
        vm.parent.collapseMainMenu(false);
    };
};

})();

The router which is inside the app.js module file:

(function () {
"use strict";

var app = angular.module("SCAdmin", ['ui.router', 'ui.bootstrap', 'jQueryScrollbar', 'ngSanitize', 'angular.vertilize']);

app.config(function ($stateProvider, $urlMatcherFactoryProvider, $urlRouterProvider, $locationProvider) {

    $urlRouterProvider.otherwise('/Items');
    $urlMatcherFactoryProvider.caseInsensitive(true);
    $locationProvider.html5Mode(true);

    $urlRouterProvider.rule(function ($injector, $location, $window, $timeout) {

        //We can write authorization / part access checks here.

        //what this function returns will be set as the $location.url
        var path = $location.path(), normalized = path.toLowerCase();

        if (path != normalized) {

            //alert(path);

            //instead of returning a new url string, I'll just change the $location.path directly so I don't have to worry about constructing a new url string and so a new state change is not triggered
            //$location.replace().path(normalized);
        }

        // because we've returned nothing, no state change occurs
    });

    $stateProvider
        .state('Items', {
            url: '/Items',
            component: 'items',
            templateUrl: '/Scripts/SupplyChainAdmin/Items/Items.html',
            //onEnter: function(){
            //    alert('entering the Items state');
            //}
        })
        .state('Vendors', {
            url: '/Vendors',
            component: 'Vendors',
            templateUrl: '/Scripts/SupplyChainAdmin/Vendors/Vendors.html',
        })
        .state('Vendors.VendorList', {
            url: '/List',
            templateUrl: '/Scripts/SupplyChainAdmin/Vendors/List/VendorList.html'
        })
        .state('Facilities', {
            url: '/Facilities',
            component: 'facilities',
            templateUrl: '/Scripts/SupplyChainAdmin/Facilities/Facilities.html',
        })
        .state('Departments', {
            url: '/Departments',
            component: 'departments',
            templateUrl: '/Scripts/SupplyChainAdmin/Departments/Departments.html',
        })
        .state('Pars', {
            url: '/Pars',
            component: 'pars',
            templateUrl: '/Scripts/SupplyChainAdmin/Pars/Pars.html',
        })
        .state('Maintenance', {
            url: '/Maintenance',
            component: 'maintenance',
            templateUrl: '/Scripts/SupplyChainAdmin/Maintenance/Maintenance.html',
        });
})

app.run(function ($rootScope, $timeout) {
    $rootScope.$on('$stateNotFound', function (event, unfoundState, fromState, fromParams) {
        event.preventDefault();
    });
    $rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams, options) {
        $rootScope.stateIsLoading = true;
    });
    $rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams, options) {
        $timeout(function () { $rootScope.stateIsLoading = false; }, 300);
    });
    $rootScope.$on('$stateChangeError', function (event, toState, toParams, fromState, fromParams, error) {
        event.preventDefault();
        $timeout(function () { $rootScope.stateIsLoading = false; }, 300);
    });
    $rootScope.$on('$viewContentLoading', function (event, viewConfig) {
        //$rootScope.stateIsLoading = true;
    });
    $rootScope.$on('$viewContentLoaded', function (event, viewConfig) {
        //$rootScope.stateIsLoading = false;
    });
})

}());

I am aware that the nested routes are not correctly configured. My focus is on the "Items" state for this post. The html loads, but the component does not.

1
So according to the UI-Router documentation the state property "Component" is how it identifies what component to load. I have found that in previous version what you say as to be true, but they had updated the code base to make it possible. I really think that I may be doing something incorrect. You can find the information I speak of here: ui-router.github.io/guide/ng1/route-to-component - Troy Crowe
can you show a sample of your route? yes, in the new ui-router, you can route to a component (not Component, btw). Also, components manage their own controller, so you can't reference a controller intended for a component directly. - Claies
@Claies You are correct, you provide a controller directly in the "component" which I am doing. That is why I know that the component is not being loaded, besides it does not show attached to the "ui-view" element when I inspect the page. I know this should happen because I see other components, that are not being loaded be the router attached to a "ui-view" when I inspect it. Another reason is that my "$onInit()" function, or any other code on the controller, is not processing. - Troy Crowe
A component is basically a controller and a template. So you shouldn't specify the templateUrl in the state definition: the component defines it already. - JB Nizet
what does the definition for the component in question look like? you went from showing no code to showing so much code it's not obvious what you are trying to work with. You say that the Items state is the route you are trying to solve, which implies the items component is the problem, but I don't see a definition for an items component in the code you posted.... - Claies

1 Answers

-1
votes

Seems like problem is in your state definitions with components, for example:

.state('Vendors', {
        url: '/Vendors',
        component: 'Vendors',
        templateUrl: '/Scripts/SupplyChainAdmin/Vendors/Vendors.html',
    })

As of latest ui-router you need to specify routes with components providing route name, url, component and resolver(optionally)

$stateProvider.state({
  name: 'Vendors',
  url: '/Vendors',
  component: 'Vendors'
});

You don't need to provide templateUrl in state definition as you've already set this inside your component's definition. Also if you change URL but don't want to reload page you can supply additional state definition param reloadOnSearch : false Like:

$stateProvider.state({
  name: 'Vendors',
  url: '/Vendors',
  component: 'Vendors',
  reloadOnSearch : false
});

Hope this helps.