Losing scope when using ng-include

113,604

Solution 1

This is because of ng-include which creates a new child scope, so $scope.lineText isn’t changed. I think that this refers to the current scope, so this.lineText should be set.

Solution 2

As @Renan mentioned, ng-include creates a new child scope. This scope prototypically inherits (see dashed lines below) from the HomeCtrl scope. ng-model="lineText" actually creates a primitive scope property on the child scope, not HomeCtrl's scope. This child scope is not accessible to the parent/HomeCtrl scope:

ng-include scope

To store what the user typed into HomeCtrl's $scope.lines array, I suggest you pass the value to the addLine function:

 <form ng-submit="addLine(lineText)">

In addition, since lineText is owned by the ngInclude scope/partial, I feel it should be responsible for clearing it:

 <form ng-submit="addLine(lineText); lineText=''">

Function addLine() would thus become:

$scope.addLine = function(lineText) {
    $scope.chat.addLine(lineText);
    $scope.lines.push({
        text: lineText
    });
};

Fiddle.

Alternatives:

  • define an object property on HomeCtrl's $scope, and use that in the partial: ng-model="someObj.lineText; fiddle
  • not recommended, this is more of a hack: use $parent in the partial to create/access a lineText property on the HomeCtrl $scope:  ng-model="$parent.lineText"; fiddle

It is a bit involved to explain why the above two alternatives work, but it is fully explained here: What are the nuances of scope prototypal / prototypical inheritance in AngularJS?

I don't recommend using this in the addLine() function. It becomes much less clear which scope is being accessed/manipulated.

Solution 3

Instead of using this as the accepted answer suggests, use $parent instead. So in your partial1.htmlyou'll have:

<form ng-submit="$parent.addLine()">
    <input type="text" ng-model="$parent.lineText" size="30" placeholder="Type your message here">
</form>

If you want to learn more about the scope in ng-include or other directives, check this out: https://github.com/angular/angular.js/wiki/Understanding-Scopes#ng-include

Solution 4

I've figured out how to work around this issue without mixing parent and sub scope data. Set a ng-if on the the ng-include element and set it to a scope variable. For example :

<div ng-include="{{ template }}" ng-if="show"/>

In your controller, when you have set all the data you need in your sub scope, then set show to true. The ng-include will copy at this moment the data set in your scope and set it in your sub scope.

The rule of thumb is to reduce scope data deeper the scope are, else you have this situation.

Max

Share:
113,604

Related videos on Youtube

Shlomi Schwartz
Author by

Shlomi Schwartz

Updated on September 07, 2020

Comments

  • Shlomi Schwartz
    Shlomi Schwartz over 3 years

    I have this module routes:

    var mainModule = angular.module('lpConnect', []).
        config(['$routeProvider', function ($routeProvider) {
        $routeProvider.
            when('/home', {template:'views/home.html', controller:HomeCtrl}).
            when('/admin', {template:'views/admin.html', controller:AdminCtrl}).
            otherwise({redirectTo:'/connect'});
    }]);
    

    Home HTML:

    <div ng-include src="views.partial1"></div>
    

    partial1 HTML:

    <form ng-submit="addLine()">
        <input type="text" ng-model="lineText" size="30" placeholder="Type your message here">
    </form>
    

    HomeCtrl:

    function HomeCtrl($scope, $location, $window, $http, Common) {
        ...
        $scope.views = {
            partial1:"views/partial1.html"
        };
    
        $scope.addLine = function () {
            $scope.chat.addLine($scope.lineText);
            $scope.lines.push({text:$scope.lineText});
            $scope.lineText = "";
        };
    ...
    }
    

    In the addLine function $scope.lineText is undefined, this can be resolved by adding ng-controller="HomeCtrl" to partial1.html, however it causes the controller to be called twice. What am I missing here?

  • Jess
    Jess over 10 years
    I used objects, but they scope was still masked. I tried $parent. and it worked great. Why do you consider it a hack? (I can see it would add maintenance if you refactored your html)
  • qbert65536
    qbert65536 over 10 years
    Same question @Jess had , why is this considered a hack ?
  • Mark Rajcok
    Mark Rajcok over 10 years
    @qbert65536, it is essentially a hack/fragile because if you restructure your HTML, it might not work anymore. E.g., you might then need to use $parent.$parent... to get it to work. Put another way, using $parent makes assumptions about the DOM structure.
  • Chris Yeung
    Chris Yeung about 10 years
    I was having the same problem! Thanks for the detailed explanation
  • mraaroncruz
    mraaroncruz almost 10 years
    @Jess' link above has been changed to this Understanding Scopes ngInclude. Read the whole page, it's great.
  • grammar
    grammar over 9 years
    Thank you for this. I think this should be the accepted answer. Using this feels like a hack in Angular terms
  • Sebastialonso
    Sebastialonso about 9 years
    For any reader, he means $scope.$parent instead of $parent is undefined according to Angular.
  • Derek Webb
    Derek Webb almost 9 years
    This answer saves the day for me! Thanks so much for pointing out the use of $parent.
  • OMGPOP
    OMGPOP almost 9 years
    is $scope.$parent pass by reference? or it's just a copy of parent?
  • OMGPOP
    OMGPOP almost 9 years
    @Sebastiallonso is wrong. $scope.$parent.lineText is undefined. $parent.lineText is working, this.lineText or simply just lineText are also working
  • Isaias Soares
    Isaias Soares almost 9 years
    I has going crazy about this. Thank for the detailed explanation @MarkRajcok! +1
  • radtek
    radtek about 8 years
    Its $scope.$parent that works for me in angular 1.3.20
  • Zargold
    Zargold about 8 years
    @Sebastialonso is right and so is OMGPOP: in the directive or html $scope.$parent is $parent if you are referring to it inside a controller it is $scope (or in a directive typically 'scope').$parent... Inside html $scope is already passed so it would make it look like $parent is right but officially it is $scope.$parent
  • zahra
    zahra about 8 years
    This is a great detailed answer, but I tried all of them without any success. I have a form with some input to a controller and the result of a controller should be viewed on another div. Once I enter any input the synchronicity will be lost and I will have a constant 0.00 value on the view div while the app is running.
  • iSpithash
    iSpithash over 4 years
    I'm using this approach for a similar issue but it's not proper for all cases. Especially when you want the included element to never get hidden...