BeingSuman
BeingSuman

Reputation: 3323

Weird behavior of Directive Scope when set to true

Am new to Angular and came across this weird behavior of Custom Directive when its scope is set to true. In this condition, AngularJS will create a new scope by inheriting parent scope ( usually controller scope, otherwise the application’s rootScope ). So any changes made in Parent will be reflected to child and vice-versa. This behavior is inconsistent in this JSFiddle link

HTML :

<div ng-app="schoolApp">

  <div ng-controller="schoolCtrl">
    <h2 ng-click="reverseName()">{{schoolName}}, Click me to reverse school name</h2>

    <h2>Student Name : {{student.firstName}} {{student.lastName}}<br>
            Student Contact Num : {{student.mobileNum}}
        </h2>
    <div>Edit in parent :
      <input type='text' ng-model='student.firstName'>
      <input type='text' ng-model='student.lastName'>
    </div>
    <div my-directive class='directive'></div>
  </div>
</div>

JS (Controller n Directive) :

var app = angular.module("schoolApp", []);

app.controller("schoolCtrl", function($scope) {
  $scope.schoolName = 'Oxford Academy';
  $scope.reverseName = function() {
    $scope.schoolName = $scope.schoolName.split('').reverse().join('');
  };
  $scope.student = {
    firstName: 'Chris',
    lastName: 'Johnson',
    mobileNum: 123456
  }
});

app.directive("myDirective", function() {
  return {
    restrict: "EA",
    scope: true,
    template: "<strong>Inside Directive Scope</strong>" +
      "<div>School Name is : {{schoolName}}</div>" +
      "Change School name : <input type='text' ng-model='schoolName' />" +
      "<br><br>" +
      "<div> Student Details :</div>" +
      "Student Name : {{student.firstName}} {{student.lastName}}<br>" +
      "Student Contact Num : {{student.mobileNum}}" +
      "<br><br>" +
      "Change Student First Name : <input type='text' ng-model='student.firstName'/><br>" +
      "Change Student Last Name : <input type='text' ng-model='student.lastName'/><br>" +
      "Change Student Contact Number : <input type='text' ng-model='student.mobileNum'/>"
  };
});

Issue Scenario : As long as am changing School Name in Controller (Parent Scope) its reflecting in Custom Directive (my-directive) but once i change School Name in Child Scope looks like there is some disconnection. Neither changes in Child Scope are reflecting in Parent nor is vice-versa working now.

But following same steps to Change Student First name or Last name in Controller (Parent Scope) will show up in Custom Directive (my-directive) and Changing Student First name or Last name in Child Scope will reflect in Parent scope. No matter how many iterations of changes you do for Student First name n Last name it works fine.

Can someone tell me what wrong am doing or is there something i need to understand that's working under the hood ?

Upvotes: 2

Views: 60

Answers (3)

Divyesh Rupawala
Divyesh Rupawala

Reputation: 1221

  1. scope : true ( Directive gets a new scope ) There is nothing that Angular is doing here but the weird behavior is because of JavaScript Prototypical inheritance. What will happen when a child is changing primitive values (string, number, boolean) on a parent, it will end up creating a copy of the value in the child, and break the inheritance chain. That's the reason when schoolName is changed in Directive (Child Scope) its getting a copy of its own and inheritance chain with Parent is gone for toss.

But this is not the case when you change a JS Object (which is student object attached to $scope in your case), inheritance chain will remain intact no matter what.

  1. scope : false ( Directive uses its parent scope )

On other hand if you had set scope to false, your child would have always used Parent scope and there will never be a chance of breaking the inheritance chain.

Hope this helps :-)

Upvotes: 3

fodma1
fodma1

Reputation: 3535

This is because of prototypical inheritance. Your directive inherits the properties of the parent scope which means, directive scope's schoolName will have the same value as the parent scope's schoolName, and the student property will point to the same object as the parent scope's student property.

When you change the value of schoolName, you just changing a string property on the scope. When you change the student.firstName, you change a string property on an object which referenced from both the parent and the directive scope.

Upvotes: 1

Bruce
Bruce

Reputation: 11

You are missing a $scope.$digest(); in your method reverseSchoolName e.g.:

$scope.reverseSchoolName = function() {
    $scope.schoolName = $scope.schoolName.split('').reverse().join('');
    $scope.$digest();
};

One advice: don't use scope: true. Try to pass every parameter itself e.g. scope: {schoolName: '=', student: '='} to get an isolated scope, see https://docs.angularjs.org/guide/directive#isolating-the-scope-of-a-directive

Upvotes: -1

Related Questions