1

I wasn't sure on how to properly title this question, so here it goes ..

I'm trying to setup a dynamic way to change out templates inside of a directive using the ng-include method. I've set up two Plunker examples and although one should work just like the other, that doesn't seem to be the case.

HTML for both Examples:

<!DOCTYPE html>
<html ng-app="myApp">

  <head>
    <link rel="stylesheet" href="style.css">
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
    <script src="script.js"></script>
  </head>

  <body>
    <main></main>
  </body>

</html>

Example #1: http://plnkr.co/edit/bi3Plrm8xufuN79Nvajj?p=preview

I'm setting up two directives (one main, and one nested as a child):

angular.module('myApp', ['Test']);
angular.module('Test', [])
.directive('main', [
    function () {
        return {
            restrict: 'E',
            template: '<input type="text" ng-model="myModel"><br><br><child></child>'
        };
    }
])
.directive('child', [
    function () {
        return {
            restrict: 'E',
            template: '<input type="text" ng-model="myModel">'
        };
    }
]);

Easy. When running the app both fields populate respectively as the model changes.

Example #2: http://plnkr.co/edit/3ajcTyfJElEzbqvsWwBM?p=preview

The HTML stays the same, but the js is a little different:

angular.module('myApp', ['Test']);
angular.module('Test', [])
.directive('main', [
    function () {
        return {
            restrict: 'E',
            template: '<input type="text" ng-model="myModel"><br><br><child></child>'
        };
    }
])
.directive('child', [
    function () {
        return {
            restrict: 'E',
            controller: function($scope) {
              $scope.myTemplate = 'test-template.html'
            },
            template: "<div ng-include='myTemplate'></div>"
        };
    }
]);

test-template.html:

<input type="text" ng-model="myModel">

This time, if I interact with the first input that is generated, both inputs update respectively as they should. Here's when it gets interesting ... When/if I interact with the second input (the one generated by ng-include) I loose all binding. Forever... Almost as if it's created its own version of the model. Afterwards, changing the first input has no effect on the second.

What is happening here? Is it indeed creating a new instance of myModel? And if so, how can this be avoided when using this ng-include method?

spez86
  • 732
  • 1
  • 11
  • 24
  • 2
    ng-include creates a child scope. Change it to `ng-model="myModel.value"` in both and see it working.http://jsbin.com/tehiranopi/3/edit – PSL Oct 30 '14 at 19:13
  • http://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs – PSL Oct 30 '14 at 19:16
  • http://stackoverflow.com/questions/15705532/angularjs-include-and-scope-inheritance-broken-bindings – PSL Oct 30 '14 at 19:21
  • Thank you, I completely forgot about the child scope. – spez86 Oct 30 '14 at 19:24

1 Answers1

1

This is no weird, as PSL said, ng-include creates new scope.

If you want to create the behaviour that keeps those models attached, You should change

<input type="text" ng-model="myModel">

To:

<input type="text" ng-model="$parent.myModel">

Linial
  • 1,154
  • 9
  • 22
  • So if you have 2 more child scopes wrapping you will do `$parent.$parent.$parent.myModel` ? weird no? – PSL Oct 30 '14 at 19:25
  • Yup, it's weird, but I could not imagine that you would ever want to create 3 nested child scopes. what are you suggesting? – Linial Oct 30 '14 at 19:29
  • 1
    DO you think your html structure is always going to be simple.? There could be numerous example it is not. what if you are ng-including the content inside an ng-repeat which is probably inside an ng-if.. just saying.. :) Point is `$parent` is the worst way to resolve the issue. – PSL Oct 30 '14 at 19:30
  • Definitely not, but this is why I'm asking you, what is the most suitable solution for that scenario? – Linial Oct 30 '14 at 19:31
  • See the link i have attached and the jsbin on the question's comments – PSL Oct 30 '14 at 19:32
  • Nice! will it work the same way if there are more nested scopes? – Linial Oct 30 '14 at 19:34
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/63959/discussion-between-linial-and-psl). – Linial Oct 30 '14 at 19:37
  • http://jsbin.com/roxecupila/1/edit and this answer should clear up http://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs. Sorry i can't use chat here :( – PSL Oct 30 '14 at 19:40
  • Are there cases when you have to use $parent? I mean in my code I have ng-click nested in ng-repeat, only $parent is working for me there, is that normal? – Linial Oct 30 '14 at 19:44
  • I have never come across a case where i do not have a better alternative than to use parent. Here is an answer i answered few days back with other alternatives. http://stackoverflow.com/questions/26555720/how-can-i-get-rid-of-parent-in-angular/26555867#26555867 Question was "how to get rid of $parent" :D Problem why i hate using parent is that whoever is using $parent is making lot of assumptions on how its outer scopes are structures, which could cause maintainability issues.. – PSL Oct 30 '14 at 19:46
  • Wow, I just changed two lines of code in my project and it's not using $parent anymore, however, I had to declare few lines on the controller. without those I could not refer to the same object in nested scope. so I guess, $parent is making an assumption, and defining an object on the controller is creating the possibility for a change. NEAT. – Linial Oct 30 '14 at 20:31
  • Yup. And you are just dealing with your predefined view model so nothing unpredictable, no $parent or anything.. :) There was another question today here http://stackoverflow.com/questions/26658679/ng-if-causing-parent-index-to-be-set-to-index/26658945#26658945, you can get around with a legit usage of ng-init. – PSL Oct 30 '14 at 20:33