Utrata zasięgu podczas używania ng-include

181

Mam ten moduł tras:

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

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

W addLinefunkcja $scope.lineTextjest undefined, to może być rozwiązany poprzez dodanie ng-controller="HomeCtrl"do partial1.html, powoduje to jednak kontroler na miano dwukrotnie. Czego tu brakuje?

Shlomi Schwartz
źródło

Odpowiedzi:

83

Jest to spowodowane ng-includetym, że tworzy nowy zakres potomny, więc $scope.lineTextnie ulega zmianie. Myślę, że thisodnosi się to do obecnego zakresu, więc this.lineTextnależy je ustawić.

Renan Tomal Fernandes
źródło
260

Jak wspomniano @Renan, ng-include tworzy nowy zakres potomny. Zakres ten prototypowo dziedziczy (patrz linie przerywane poniżej) z zakresu HomeCtrl. ng-model="lineText"faktycznie tworzy prymitywną właściwość zakresu w zakresie potomnym, a nie w zakresie HomeCtrl. Ten zakres potomny jest niedostępny dla nadrzędnego / HomeCtrl:

ng-include scope

Aby zapisać to, co użytkownik wpisał do tablicy $ scope.lines HomeCtrl, sugeruję przekazać wartość do funkcji addLine:

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

Ponadto, ponieważ lineText jest własnością ngInclude scope / częściowo, uważam, że powinien on być odpowiedzialny za jego wyczyszczenie:

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

Funkcja addLine () stałaby się w ten sposób:

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

Fiddle .

Alternatywy:

  • określić właściwości obiektu na HomeCtrl za $ zakresu i użyć jej w częściowej: ng-model="someObj.lineText; skrzypce
  • nie polecam, to jest bardziej Hack: użyć $ rodziców w częściowym do tworzenia / dostęp do lineTextnieruchomości na HomeCtrl $ zakresie:   ng-model="$parent.lineText"; skrzypce

Nieco wyjaśniono, dlaczego powyższe dwie alternatywy działają, ale zostało to w pełni wyjaśnione tutaj: jakie są niuanse zakresu prototypowego / prototypowego dziedziczenia w AngularJS?

Nie polecam używania thisw funkcji addLine (). Staje się znacznie mniej jasne, który zakres jest dostępny / manipulowany.

Mark Rajcok
źródło
1
Wreszcie rozumiem.
Scott Tesler
1
To samo pytanie @Jess, dlaczego uważa się to za hack?
qbert65536,
13
@ qbert65536, jest to w zasadzie hack / kruche, ponieważ jeśli zrestrukturyzujesz swój HTML, może już nie działać. Np. Może być konieczne użycie $parent.$parent...go, aby działał. Innymi słowy, za pomocą $parentprzyjmuje się założenia dotyczące struktury DOM.
Mark Rajcok,
6
Link @ Jess powyżej został zmieniony na ten Zrozumienie Zakresów ngInclude . Przeczytaj całą stronę, to świetnie.
mraaroncruz
1
To bardzo szczegółowa odpowiedź, ale wypróbowałem wszystkie bezskutecznie. Mam formularz z pewnymi danymi wejściowymi do kontrolera, a wynik kontrolera powinien zostać wyświetlony na innym divie. Po wprowadzeniu danych wejściowych synchroniczność zostanie utracona i będę mieć stałą wartość 0,00 na div widoku podczas działania aplikacji.
zahra
33

Zamiast używać thiszgodnie z sugerowaną odpowiedzią, użyj $parentzamiast tego. Więc w twoim partial1.htmlbędziesz miał:

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

Jeśli chcesz dowiedzieć się więcej o zakresie ng-includelub innych dyrektywach, sprawdź to: https://github.com/angular/angular.js/wiki/Understanding-Scopes#ng-include

ErwinGO
źródło
1
Dla każdego czytelnika oznacza on $scope.$parentzamiast $parentniezdefiniowany według Angulara.
Sebastialonso
1
Ta odpowiedź oszczędza mi dzień! Dziękujemy bardzo za wskazanie użycia $ parent.
Derek Webb
jest $ scope. $ rodzic przekazać przez referencję? czy to tylko kopia rodzica?
OMGPOP,
1
@Sebastiallonso się myli. $ scope. $ parent.lineText jest niezdefiniowany. $ parent.lineText działa, działa także this.lineText lub po prostu lineText
OMGPOP
To $scope.$parentdziała dla mnie w kanciastym 1.3.20
radtek
4

Zrozumiałem, jak obejść ten problem bez mieszania danych rodzica i podzakresu. Ustaw a ng-ifna ng-includeelemencie i ustaw zmienną zasięgu. Na przykład :

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

W swoim kontrolerze, po ustawieniu wszystkich potrzebnych danych w swoim zakresie podrzędnym, ustaw opcję show na true. W ng-includetym momencie skopiuje zestaw danych w twoim zakresie i ustawi go w twoim zakresie podrzędnym.

Zasadą jest ograniczenie danych zakresu głębiej, w przeciwnym razie masz taką sytuację.

Max

wascou
źródło
Używam tego podejścia do podobnego problemu, ale nie jest ono właściwe we wszystkich przypadkach. Zwłaszcza gdy chcesz, aby dołączony element nigdy się nie ukrywał ...
iSpithash,