Perder escopo ao usar ng-include

181

Eu tenho este módulo de rotas:

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

HTML inicial:

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

Na addLinefunção $scope.lineTexté undefined, isso pode ser resolvido através da adição ng-controller="HomeCtrl"de partial1.html, no entanto, faz com que o controlador a ser chamado duas vezes. O que estou perdendo aqui?

Shlomi Schwartz
fonte

Respostas:

83

Isso ocorre porque ng-includecria um novo escopo filho, portanto $scope.lineTextnão é alterado. Eu acho que isso thisse refere ao escopo atual, então this.lineTextdeve ser definido.

Renan Tomal Fernandes
fonte
260

Como o @Renan mencionou, o ng-include cria um novo escopo filho. Esse escopo herda prototipicamente (consulte linhas tracejadas abaixo) do escopo HomeCtrl. ng-model="lineText"na verdade, cria uma propriedade de escopo primitivo no escopo filho, não no escopo do HomeCtrl. Este escopo filho não está acessível para o escopo pai / HomeCtrl:

escopo ng-include

Para armazenar o que o usuário digitou na matriz $ scope.lines do HomeCtrl, sugiro que você passe o valor para a função addLine:

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

Além disso, como o lineText pertence ao escopo ngInclude / parcial, acho que ele deve ser responsável por limpá-lo:

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

A função addLine () se tornaria assim:

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

Violino .

Alternativas:

  • definir uma propriedade do objeto em $ âmbito do HomeCtrl, e usar isso na parcial: ng-model="someObj.lineText; violino
  • não recomendado, este é mais um truque: use $ pai na parcial para criar / acesso a lineTextpropriedade no $ âmbito HomeCtrl:   ng-model="$parent.lineText"; violino

É um pouco complicado explicar por que as duas alternativas acima funcionam, mas é totalmente explicado aqui: Quais são as nuances da herança prototípica / prototípica do escopo no AngularJS?

Eu não recomendo usar thisna função addLine (). Torna-se muito menos claro qual escopo está sendo acessado / manipulado.

Mark Rajcok
fonte
1
Finalmente eu entendo.
Scott Tesler
1
Mesma pergunta que @Jess teve, por que isso é considerado um hack?
qbert65536
13
@ qbert65536, é essencialmente um hack / frágil, porque se você reestruturar seu HTML, ele pode não funcionar mais. Por exemplo, você pode precisar usá $parent.$parent...-lo para fazê-lo funcionar. Em outras palavras, usar $parentfaz suposições sobre a estrutura DOM.
Mark Rajcok
6
O link de @Jess acima foi alterado para este artigo Entendendo os escopos da inclusão . Leia a página inteira, é ótimo.
Mcdd # 06
1
Essa é uma ótima resposta detalhada, mas tentei todas sem sucesso. Eu tenho um formulário com alguma entrada para um controlador e o resultado de um controlador deve ser exibido em outra div. Depois de inserir qualquer entrada, a sincronicidade será perdida e terei um valor constante de 0,00 na div view enquanto o aplicativo estiver em execução.
Zahra
33

Em vez de usar thiscomo a resposta aceita sugere, use em seu $parentlugar. Então no seu partial1.htmlvocê terá:

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

Se você quiser saber mais sobre o escopo ng-includeou outras diretivas, verifique isso: https://github.com/angular/angular.js/wiki/Understanding-Scopes#ng-include

ErwinGO
fonte
1
Para qualquer leitor, ele quer dizer que, em $scope.$parentvez de, $parenté indefinido, de acordo com Angular.
Sebastialonso
1
Esta resposta salva o dia para mim! Muito obrigado por apontar o uso de $ parent.
Derek Webb
é $ scope. $ parent pass por referência? ou é apenas uma cópia dos pais?
OMGPOP 25/06
1
@Sebastiallonso está errado. $ scope. $ parent.lineText é indefinido. $ parent.lineText está funcionando, this.lineText ou simplesmente lineText também estão funcionando
OMGPOP
Sua $scope.$parentque funciona para mim em angular 1.3.20
radtek
4

Eu descobri como solucionar esse problema sem misturar dados pai e sub escopo. Defina um ng-ifno ng-includeelemento e defina-o como uma variável de escopo. Por exemplo :

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

No seu controlador, quando você definir todos os dados necessários no seu sub escopo, defina show como true. O ng-includecopiará, neste momento, o conjunto de dados em seu alcance e fixando-o em seu sub escopo.

A regra geral é reduzir os dados do escopo mais profundamente do escopo, caso contrário, você terá essa situação.

Máx.

wascou
fonte
Estou usando essa abordagem para um problema semelhante, mas não é adequado para todos os casos. Especialmente quando você quer o elemento incluído para nunca ter escondido ...
iSpithash