1

I created a custom directive, which shows a list and select-box.

Please follow the code below,

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Template</title>

    <script type="text/javascript" src="/home/rahul/Installers/jquery-3.0.0.js"></script>   
    <link rel="stylesheet" type="text/css" href="/home/rahul/Installers/Bootstrapv3.0.2/css/bootstrap.css">
    <link rel="stylesheet" type="text/css" href="/home/rahul/Installers/Bootstrapv3.0.2/css/bootstrap-theme.css">
    <script type="text/javascript" src="/home/rahul/Installers/Bootstrapv3.0.2/js/bootstrap.js"></script>
    <script type="text/javascript" src="/home/rahul/Installers/angular.js"></script>

    <script type="text/javascript">
        angular.module("app",[]);
        angular.module("app").controller("myctrl",myctrl);
        angular.module("app").controller("childCtrl",childCtrl);
        angular.module("app").directive("loadFilms",loadFilms);

        myctrl.$inject = ["$scope"];
        childCtrl.$inject = ["$scope"];

        function myctrl($scope){
            var vm = this;
            vm.title = "Directive and nested scoping";
            vm.actorName = "Amithabh Bachhan";
            $scope.year = ["70's","80's","90's"];
        }

        function childCtrl($scope){
            var vm = this;

            vm.selectedYear = "";

            vm.moviesObj = {
                "70's" : ["Anand","Lawaris","Kala Pathhar","Deewar","Amar Akbar Anthony","Mili"],
                "80's" : ["Sharabi","Kaalia","Silsila","Satte Pe Satta","Nastik","Shahensha"],
                "90's" : ["Hum","Aaj Ka Arjun", "Ajooba","Khuda Gawah","Ganga Jamuna Saraswati","Lal Badshah"]
            }

            vm.movies = [];

            vm.getMovies = function(){
                vm.movies = vm.moviesObj[vm.selectedYear];
            }
        }

        function loadFilms(){
            return {
                restrict : "EA",
                controller : "childCtrl",
                controllerAs : "vm",
                scope : true,
                template : function(tElem, tAttrs){
                    var str = "<select ng-model='vm.selectedYear' data-ng-options='y as y for y in year' " + "class='form-control' ng-change='vm.getMovies()'>" + 
                    "<option value=''>Select</option>" + 
                    "</select>" +
                    "<br />" + 
                    "<ol class='slide-animate-container'>" + 
                    "   <li class='slide-animate' ng-repeat='m in vm.movies'>{{m}}</li>"+
                    "</ol>";
                    return str;
                }
            } 
        }
    </script>
</head>
<body>
    <div ng-app="app" ng-controller="myctrl as vm" class="container">
        <div class="page-header">
            <h3>{{vm.title}}</h3>
        </div>
        <div class="row">
            <div class="col-md-6">
                <div class="page-header">
                    <h5>{{vm.actorName}}</h5>                       
                </div>                      
            </div>
        </div>
        <div class="row">
            <div class="col-md-6" load-films></div> 
        </div>
    </div>
</body>     

The directive "load-films" has its own scope,

The values to the select-box in the directive are passed from the parent scope variable from the enclosing controller "myctrl".

The working example is in the link Example One

Now, once I change the scope of the directive "load-films" to false

like

 function loadFilms(){
            return {
                restrict : "EA",
                controller : "childCtrl",
                controllerAs : "vm",
                scope : false,
                template : function(tElem, tAttrs){
                    var str = "<select ng-model='vm.selectedYear' data-ng-options='y as y for y in year' " + "class='form-control' ng-change='vm.getMovies()'>" + 
                    "<option value=''>Select</option>" + 
                    "</select>" +
                    "<br />" + 
                    "<ol class='slide-animate-container'>" + 
                    "   <li class='slide-animate' ng-repeat='m in vm.movies'>{{m}}</li>"+
                    "</ol>";
                    return str;
                }
            } 
        }

The values vm.title and vm.actorName of the parent controller don't load in the UI.

Why ?

Ideally vm.title and vm.actorName are in the controller "myCtrl", so how does setting the scope property to false of the directive affect the parent controller variables, as the variable actorName and title are attached to this not to $scope.

The working example using {.., scope = false,..} is here

1
  • Change scope:false to scope: {} Commented Jul 18, 2016 at 10:22

3 Answers 3

4

You are using same name (vm) for both controllerAs. Thus the variable is overwritten.

Sign up to request clarification or add additional context in comments.

Comments

0

LONG ANSWER: Answer lies in name conversion and initialization query. If you debug what is initializing you will detect that:

  1. First is myctrl;
  2. Second is childCtrl.

If you are using scope: true next steps are applied:

  1. Create $scope for myctrl and assign $scope.vm = myctrl (myctrl as vm);
  2. Create $scope for childCtrl and assign $scope.vm = childCtrl (controllerAs: mv);
  3. vm.title and vm.actorName are taken from $scope.vm from 1st step.

What happens if scope: false:

  1. Create $scope for myctrl and assign $scope.vm = myctrl (myctrl as vm);
  2. Use already created $scope for myctrl where $scope.vm already exists;
  3. When initializing directive - rewrite $scope.vm with childCtrl (controllerAs: 'vm');
  4. vm.title and vm.actorName are taken from $scope.vm from 3rd step (from childCtrl isntead of myctrl).

SHORT ANSWER: After initialization of every controller, you still remember relation of controller to $scope.vm BUT in $scope.vm you have childCtrl instead of myctrl, as it was overwritten during directive initialization and $scope is shared. And vm.title and vm.actorName are not part if childCtrl.

Comments

0

Get rid of both controllerAs and scope in your directive.

controllerAs because it is forcing vm. in your view to be an alias to properties on your directive's controller rather than the alias defined in your outer view, and scope because you don't need it when setting it to false.

However, even though that will fix it, it is not the ideal approach. Ideally you should keep your controllerAs and have scope looking like this

scope : {
  title : "@"
}

and then change your view to use the title passed in from

<blah load-films title="vm.title"/>

Directives with isolated scope are the better way to go, so stick with controllerAs and scope but ensure the value you want on the parent is passed in explicitly via a parameter - this way you don't need to rely on the view containing your directive to set up the view with a specific alias like "vm" (magic strings are bad)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.