Perder alcance cuando se usa ng-include

181

Tengo este módulo de rutas:

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'});
}]);

Inicio 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 = "";
    };
...
}

En la addLinefunción $scope.lineTextes undefined, esto se puede resolver agregando ng-controller="HomeCtrl"a partial1.html, sin embargo, hace que se llame dos veces al controlador. ¿Que me estoy perdiendo aqui?

Shlomi Schwartz
fuente

Respuestas:

83

Esto se debe a ng-includeque crea un nuevo ámbito secundario, por $scope.lineTextlo que no se modifica. Creo que eso se thisrefiere al alcance actual, por lo que this.lineTextdebe establecerse.

Renan Tomal Fernandes
fuente
260

Como @Renan mencionó, ng-include crea un nuevo ámbito secundario. Este alcance hereda prototípicamente (ver líneas discontinuas a continuación) del alcance HomeCtrl. ng-model="lineText"en realidad crea una propiedad de ámbito primitiva en el ámbito secundario, no en el ámbito de HomeCtrl. Este ámbito secundario no es accesible para el ámbito primario / HomeCtrl:

alcance ng-include

Para almacenar lo que el usuario escribió en la matriz $ scope.lines de HomeCtrl, le sugiero que pase el valor a la función addLine:

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

Además, dado que lineText es propiedad de ngInclude scope / partial, creo que debería ser responsable de borrarlo:

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

La función addLine () se convertiría así en:

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

Violín .

Alternativas:

  • defina una propiedad de objeto en $ alcance de HomeCtrl y utilícela en el parcial: ng-model="someObj.lineText ; violín
  • no recomendado, esto es más un truco: use $ parent en el parcial para crear / acceder a una lineTextpropiedad en el alcance $ HomeCtrl:  ng-model="$parent.lineText" ; violín

Es un poco complicado explicar por qué funcionan las dos alternativas anteriores, pero aquí se explica completamente: ¿Cuáles son los matices del alcance prototípico / herencia prototípica en AngularJS?

No recomiendo usar thisen la función addLine (). Se vuelve mucho menos claro a qué alcance se está accediendo / manipulando.

Mark Rajcok
fuente
1
Finalmente lo entiendo.
Scott Tesler
1
La misma pregunta que tuvo @Jess, ¿por qué se considera esto un truco?
qbert65536
13
@ qbert65536, es esencialmente un hack / fragile porque si reestructura su HTML, es posible que ya no funcione. Por ejemplo, es posible que deba usar $parent.$parent...para que funcione. Dicho de otra manera, el uso $parenthace suposiciones sobre la estructura DOM.
Mark Rajcok
66
El enlace de @Jess arriba se ha cambiado a este alcance de comprensión ngInclude . Lea toda la página, es genial.
mraaroncruz
1
Esta es una gran respuesta detallada, pero las probé todas sin éxito. Tengo un formulario con alguna entrada a un controlador y el resultado de un controlador debe verse en otro div. Una vez que ingrese cualquier entrada, la sincronicidad se perderá y tendré un valor constante de 0.00 en el div de vista mientras se ejecuta la aplicación.
zahra
33

En lugar de usar thiscomo sugiere la respuesta aceptada, use $parenten su lugar. Entonces en tu partial1.htmltendrás:

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

Si desea obtener más información sobre el alcance ng-includeu otras directivas, consulte esto: https://github.com/angular/angular.js/wiki/Understanding-Scopes#ng-include

ErwinGO
fuente
1
Para cualquier lector, quiere decir que en $scope.$parentlugar de $parentestá indefinido según Angular.
Sebastialonso
1
¡Esta respuesta me salva el día! Muchas gracias por señalar el uso de $ parent.
Derek Webb
es $ scope. $ parent pass por referencia? o es solo una copia de los padres?
OMGPOP
1
@Sebastiallonso está equivocado. $ scope. $ parent.lineText no está definido. $ parent.lineText está funcionando, this.lineText o simplemente lineText también están funcionando
OMGPOP
Es lo $scope.$parentque funciona para mí en angular 1.3.20
radtek
4

He descubierto cómo solucionar este problema sin mezclar datos primarios y secundarios. Establezca a ng-ifen el ng-includeelemento y configúrelo en una variable de ámbito. Por ejemplo :

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

En su controlador, cuando haya configurado todos los datos que necesita en su sub ámbito, luego configure show en true. En ng-includeeste momento, copiará el conjunto de datos en su ámbito y lo establecerá en su sub ámbito.

La regla general es reducir los datos del alcance más profundamente, de lo contrario, tiene esta situación.

Max

wascou
fuente
Estoy usando este enfoque para un problema similar, pero no es adecuado para todos los casos. Especialmente cuando quieres que el elemento incluido nunca se oculte ...
iSpithash