4

AngularJS UI router named views loading based on user access rather than loading at the time of state route access.

Example:

$stateProvider
.state("login",
{
    url: "/login",
    templateUrl: getTemplateUrl("login/Index")
})    
.state("main",
{
    url: "/main",
    views: 
    {
        '': { templateUrl: getTemplateUrl('home/shell') },
        'test1@main': { templateUrl: 'home/test1' },
        'test2@main': { templateUrl: 'home/test2' },
        'test3@main': { templateUrl:  getTemplateUrl('home/test3') }                     
    }
});

In the above example, when a user accesses the state main the UI-router loads all the named views html from server.

Question:

Can we load named-views when required below? I mean whenever we add new tab dynamically then only loading respected view html from server.

<tab ng-repeat="tab in tabs">    
    <div>     
        <div ui-view='{{tab.view}}'></div>
    </div>
 </tab>
Community
  • 1
  • 1
Venkat
  • 868
  • 1
  • 12
  • 21
  • just to clarify, what you asking is it possible to only load respected view from server without it load everything ? – kwangsa May 21 '15 at 00:01
  • maybe this will help: http://stackoverflow.com/questions/28184616/angularjs-lazy-load-template-and-controller-in-ui-router-requirejs-oclazyload – sirrocco May 22 '15 at 16:54

1 Answers1

5

If you are looking to load your tab content dynamically from template urls based on the value of tabs available to the user as defined in $scope.tabs, you should consider using a simple directive rather than ui-router views.

As you have already discovered ui-router will try and load the subviews regardless of whether they are referenced in the main view for that state.

We can however use our own directive to load templates, and therefore because the directive only runs when present in the main view, the templates load on demand.

template Directive:

We create a template directive, that allows us to pull in a template into an html element.

.directive('template', ['$compile', '$http', function($compile, $http) {
    return {
        restrict: 'A',
        replace: false,
        link: function($scope, element, attrs) {
            var template = attrs['template'];
            var controller = attrs['controller'];
            if(template!==undefined){
                // Load the template
                $http.get(template).success(function(html){
                    // Set the template
                    var e = angular.element(controller === undefined || controller.length === 0 ? html : "<span ng-controller='" + controller + "'>" + html + "</span>");
                    var compiled = $compile(e);
                    element.html(e);
                    compiled($scope);
                });
            }
        }
    };
}]);

So this code uses the $http service to get the template from the server. Then it uses the $compile service to apply the scope to the angular template, and renders it into the target element.

Usage:

Update the format of your main tabs template as below. Note we no longer reference ui-view, instead we call our template directive passing in the url we want to load in the div. With the current content of the div being the loading indicator.

(If the controller attribute is set the template will be wrapped with an <span> having the ng-controller attribute, so a template controller can be applied. This is optional.)

in home/shell:

<tab ng-repeat="(tabName,tab) in tabs">
    <div template='{{tab.template}}' controller="{{tab.controller}}">Loading {{tabName}} ...</div>
 </tab>

Then set the tabs you want to display:

$scope.tabs = {
    'tab1': { template: 'home/tab1'},
    'tab2': { template: 'home/tab2', controller: 'Tab2Controller' },
    'tab3': { template: 'home/tab3'}
};

Full source:

This just puts the code given above, as an example AngularJS app. Assumes the template paths are valid i.e. home/shell, home/tab1 etc.

<!DOCTYPE html>
<html>
<head>
    <script src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.2/angular.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.js"></script>
    <meta charset="utf-8">
    <title>Angular Views</title>
</head>
<body ng-app="myTabbedApp">
    <div ui-view></div>
    <script>
        angular.module('myTabbedApp', ['ui.router'])

            /* Controller for the Main page ie. home/shell */
            .controller('MainPageTabController', ['$scope', function($scope) {
                // Set the page tabs dynamically as required by your code
                $scope.tabs = {
                    'tab1': { template: 'home/tab1'},
                    'tab2': { template: 'home/tab2', controller: 'Tab2Controller' },
                    'tab3': { template: 'home/tab3'}
                };
            }])

            /* Example controller for Tab 2 */
            .controller('Tab2Controller', ['$scope', function($scope) {
                $scope.hello = "world";
            }])

            /* State provider for ui router */
            .config(['$stateProvider', function($stateProvider){
                $stateProvider
                    .state("login",
                    {
                        url: "/login",
                        templateUrl: "login/index"
                    })
                    .state("main",
                    {
                        url: "/main",
                        templateUrl: 'home/shell',
                        controller: 'MainPageTabController'
                    });
            }])

            /* Directive to load templates dynamically */
            .directive('template', ['$compile', '$http', function($compile, $http) {
                return {
                    restrict: 'A',
                    replace: false,
                    link: function($scope, element, attrs) {
                        var template = attrs['template'];
                        if(template!==undefined){
                            // Load the template
                            $http.get(template).success(function(html){
                                // Set the template
                                var e = angular.element(html);
                                var compiled = $compile(e);
                                element.html(e);
                                compiled($scope);
                            });
                        }
                    }
                };
            }]);
    </script>
</body>
</html>

I hope this helps. If you have questions about something just ask.

Scott
  • 21,211
  • 8
  • 65
  • 72
  • By default named views not able to load dynamically after state route, directives working for us. – Venkat May 26 '15 at 14:37
  • Out of curiosity, how does this template directive differ from [ng-include](https://docs.angularjs.org/api/ng/directive/ngInclude)? – Shaun Scovil Oct 15 '15 at 15:35
  • 1
    @ShaunScovil This directive allows you to specify the controller for the view dynamically which as far as I am aware you cannot do for a template loaded solely with `ng-include`. – Scott Oct 15 '15 at 15:45
  • Alternatively, you could use `ng-controller` on the same element as `ng-include`. Example: http://plnkr.co/edit/YaYk9wywKaQyVL17a4bq – Shaun Scovil Oct 15 '15 at 16:49
  • 1
    @ShaunScovil That's true you can use `ng-controller` on the same element, but you can't dynamically populate the value of `ng-controller` and have AngularJS evaluate it on the included content. I did originally try the `ng-controller` and `ng-include` route but found this limitation. Which is why if the controller is set in the code it wraps the content in a `span` with `ng-controller`. Thanks ;) – Scott Oct 15 '15 at 17:07