Losing scope when using ng-include
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?
Solution 1:
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:
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 2:
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 3:
Instead of using this
as the accepted answer suggests, use $parent
instead. So in your partial1.html
you'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