Rahul Shivsharan Rahul Shivsharan - 4 months ago 24
AngularJS Question

Why Angular custom directive scope effects parent controller scope?

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

Answer

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