Chad M
Chad M

Reputation: 1003

Why does mutating a value of a parent directive's scope in link not provide that mutated state to a child directive?

I have a parent directive being passed an array as the value for one of its attributes. The parent directive's job is render some components, and then send a modified version of that array to a child directive. The child directive needs to then render the modified array.

Adjusting this array in the parent directive's link function does not seem to propagate the change to the rendered child directive.

Why is this?

I am only a couple days into AngularJS and may have a fundamental misunderstanding of scope or lifecycle, though I'm not sure which.

The example below modifies an array of [1,2] provided to the parent directive by having the parent directive push 3 into it.

The parent directive renders a template containing the child directive and, what I had hoped to be, the mutated array.

The screen will render the unmutated array (rendered from the child directive) but console.log the mutated array (logged from the parent directive)

https://codesandbox.io/s/trusting-fire-k4so0?fontsize=14&hidenavigation=1&theme=dark

src/index.js

"use_strict";

var angular = require("angular");

angular
  .module("App", [])
  .controller("IgnorableController", [function(){}])
  .directive("parentIsolatedScope", [
    function() {
      return {
        restrict: "E",
        template:
          "<child-isolated-scope mutated-array='mutableArray'></child-isolated-scope>",
        scope: {
          mutableArray: "<"
        },
        link: function(scope) {
          scope.mutableArray.push(3);
          console.log(scope.mutableArray);
        }
      };
    }
  ])
  .directive("childIsolatedScope", [
    function() {
      return {
        restrict: "E",
        template: "<div>{{mutatedArray | json}}</div>",
        scope: {
          mutatedArray: "<"
        }
      };
    }
  ]);

index.html

<!DOCTYPE html>
<html ng-app="App">
  <head>
    <title>Parcel Sandbox</title>
    <meta charset="UTF-8" />
    <script src="src/index.js"></script>
  </head>

  <body ng-controller="IgnorableController">
    <parent-isolated-scope mutable-array="[1,2]"></parent-isolated-scope>
  </body>
</html>

Upvotes: 0

Views: 113

Answers (2)

georgeawg
georgeawg

Reputation: 48968

If you declare a variable for the initial array and use it to pass the array into the directive it will work as expected.

Can you provide an explanation as to why that's the case?

<body ng-controller="IgnorableController">
    <parent-isolated-scope mutable-array="[1,2]"></parent-isolated-scope>
</body>

When the binding is an AngularJS expression that resolves to an array, on every digest cycle the framework evaluates the expression and sets the isolate scope variable to that value. On each digest cycle, the framework sets the value anew replacing any mutations.

With a variable:

<body ng-controller="IgnorableController" ng-init="x = [1,2]">
    <parent-isolated-scope mutable-array="x"></parent-isolated-scope>
</body>

A reference to the variable is assigned to the isolate scope.

Be aware that any mutations to the contents of the reference will change the contents in both the child isolate scope and the parent scope. This can have unintended consequences for some designs.

Upvotes: 3

Bill P
Bill P

Reputation: 3618

If you declare a variable for the initial array and use it to pass the array into the directive it will work as expected:

<body ng-controller="IgnorableController">
    <parent-isolated-scope mutable-array="initialArray"></parent-isolated-scope>
</body>

in IgnorableController, array variable:

$scope.initialArray= [1, 2];

working demo: DEMO

Upvotes: 1

Related Questions