Model Ng nie aktualizuje wartości kontrolera

270

Prawdopodobnie głupie pytanie, ale mam formularz HTML z prostym wejściem i przyciskiem:

<input type="text" ng-model="searchText" />
<button ng-click="check()">Check!</button>
{{ searchText }}

Następnie w kontrolerze (szablon i kontroler są wywoływane z routeProvider):

$scope.check = function () {
    console.log($scope.searchText);
}

Dlaczego po kliknięciu przycisku widok jest poprawnie aktualizowany, ale niezdefiniowany w konsoli?

Dzięki!

Aktualizacja: Wydaje się, że faktycznie rozwiązałem ten problem (wcześniej musiałem wymyślić kilka obejść) z: Musiałem tylko zmienić nazwę mojej właściwości z searchTextna search.text, a następnie zdefiniować pusty $scope.search = {};obiekt w kontrolerze i voila ... Nie mam pojęcia, dlaczego to działa chociaż ;]

alchemikacja
źródło
czy na pewno używasz tego kontrolera w tej części dokumentu? czy możesz opublikować minimalny przykład niepowodzenia?
wroniasty
1
Tak, 100% pewności, że kontroler jest w porządku, ten problem wydaje mi się znany ... Zaskakująco działa, kiedy zmieniam nazwę właściwości z searchTextna search.text, jakiś pomysł, dlaczego?
alchemikacja
4
@Arthur: To trochę nie jest oczywiste, ale ng-model tworzy tylko rodzaj lokalnej zmiennej speak w twoim widoku, dlatego jeśli chcesz ją zachować w ten sposób, musisz przekazać ją do funkcji check (), na przykład: check ( searchText), a kontroler go rozpozna. Mam nadzieję, że to pomaga
alchemikacja
3
Dla przypomnienia, jest napisane voila, nie vuala, wollaitp
Dan Bechard
3
Myślę, że odpowiedź, której szukasz, znajduje się na stackoverflow.com/a/14049482/1217913
Willa

Odpowiedzi:

71

Kontroler jako wersja (zalecane)

Tutaj szablon

<div ng-app="example" ng-controller="myController as $ctrl">
    <input type="text" ng-model="$ctrl.searchText" />
    <button ng-click="$ctrl.check()">Check!</button>
    {{ $ctrl.searchText }}
</div>

JS

angular.module('example', [])
  .controller('myController', function() {
    var vm = this;
    vm.check = function () {
      console.log(vm.searchText);
    };
  });

Przykład: http://codepen.io/Damax/pen/rjawoO

Najlepiej będzie użyć komponentu z Angular 2.x lub Angular 1.5 lub wyższym

########

Stary sposób (niezalecany)

NIE jest to zalecane, ponieważ ciąg znaków jest prymitywnym, wysoce zalecane jest użycie zamiast niego obiektu

Spróbuj tego w swoim znaczniku

<input type="text" ng-model="searchText" />
<button ng-click="check(searchText)">Check!</button>
{{ searchText }}

i to w twoim kontrolerze

$scope.check = function (searchText) {
    console.log(searchText);
}
Damax
źródło
17
Działa to tylko w jedną stronę ... co jeśli chcesz zmienić wartość searchText?
cdmckay
199
To również nie odpowiada na pytanie „dlaczego?” pytanie.
cdmckay
4
co jeśli nie chcę używać żadnego przycisku? Na przykład muszę złożyć przy wejściu
Danikoren
2
Saniko => Aby przesłać przy wejściu, musisz użyć tagu formularza i ng-przesłać na nim ( docs.angularjs.org/api/ng.directive:ngSubmit )
Damax
1
@ HP's411 to dlatego, że łańcuch jest prymitywny. Kiedy Angular przypisuje wartość, zmienia wskaźnik na wartość, więc kontroler patrzy na starą wartość, ponieważ ma stary wskaźnik na wartość.
Damax
620

„Jeśli używasz modelu ng, musisz mieć tam kropkę”.
Wskaż swój model na object.property, a będziesz gotowy.

Kontroler

$scope.formData = {};
$scope.check = function () {
  console.log($scope.formData.searchText.$modelValue); //works
}

Szablon

<input ng-model="formData.searchText"/>
<button ng-click="check()">Check!</button>

Dzieje się tak, gdy w zasięgu znajdują się zakresy potomne - takie jak trasy potomne lub powtórzenia ng. Zasięg potomny tworzy własną wartość i rodzi się konflikt nazw, jak pokazano tutaj :

Zobacz ten klip wideo, aby uzyskać więcej informacji: https://www.youtube.com/watch?v=SBwoFkRjZvE&t=3m15s

Will Stern
źródło
94
To jest prawdziwa odpowiedź.
keslert
16
@Catfish Dzięki prototypowemu dziedziczeniu za każdym razem, gdy piszesz do właściwości potomnej, odwołanie / połączenie z właściwością macierzystą przestaje istnieć. Sposób modelowania zakresów kątowych, jeśli nie masz kropki, tworzona jest nowa właściwość w zasięgu potomnym. Ten film wyjaśnia to bardziej szczegółowo: youtube.com/watch?v=ZhfUv0spHCY&feature=youtu.be&t=30m
Will Stern
1
Gracias Boss. To prawda! Nie wydaje się to jednak konsekwentnym dziwactwem, ponieważ problem pojawia się tylko w przypadku korzystania z frameworku jonowego
Don Barry
6
@ user3677331 Działa dobrze bez kropki, dopóki nie pojawi się zakres potomny, który próbuje z nim porozmawiać (na przykład jak element w powtórzeniu ng). Powiedzmy, że nazwa twojego modelu brzmiała „telefon”. Twój zakres potomny tworzy „telefon”, a następnie występuje konflikt zasięgu, ponieważ zakres potomny ma zmienną „telefon”, a zatem nie może uzyskać dostępu do „telefon” w zakresie nadrzędnym. Podczas gdy zakres potomny tworzy user.phone, zostanie on dodany do obiektu użytkownika rodzica, więc oba zakresy wskazują ten sam obiekt
Will Stern
3
Bardzo pomocne. Nie byłam pewna, czy znajdę odpowiedź na stosunkowo niejasne pytanie, ale było to martwe.
CodeManiak,
57

W Mastering Development Application Development with AngularJS, s.19, jest napisane, że

Unikaj bezpośrednich powiązań z właściwościami zakresu. Preferowanym podejściem jest dwukierunkowe wiązanie danych z właściwościami obiektu (eksponowanymi w zakresie). Zasadniczo powinieneś mieć kropkę w wyrażeniu podanym w dyrektywie ng-model (na przykład ng-model = "thing.name").

Zakresy są tylko obiektami JavaScript i naśladują hierarchię domen. Zgodnie z JavaScript Prototype Inheritance właściwości zakresów są rozdzielane przez zakresy. Aby tego uniknąć, do łączenia modeli ng należy używać notacji kropkowej .

efirat
źródło
2
dzięki za to odniesienie. Uważam to za interesujący punkt związany z modelem ng.
Kings
44

Używanie thiszamiast $scopeprac.

function AppCtrl($scope){
  $scope.searchText = "";
  $scope.check = function () {
    console.log("You typed '" + this.searchText + "'"); // used 'this' instead of $scope
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app>
  <div ng-controller="AppCtrl">
    <input ng-model="searchText"/>
    <button ng-click="check()">Write console log</button>
  </div>
</div>

Edycja: W momencie pisania tej odpowiedzi miałem znacznie bardziej skomplikowaną sytuację niż ta. Po komentarzach próbowałem go odtworzyć, aby zrozumieć, dlaczego to działa, ale bez powodzenia. Myślę, że jakoś (naprawdę nie wiem dlaczego) generowany jest nowy zakres potomny i thisodnosi się do tego zakresu. Ale jeśli $scopezostanie użyty, to tak naprawdę odnosi się do nadrzędnego zakresu $ z powodu javascript funkcję zakresu leksykalnego .

Byłoby wspaniale, gdyby ktoś mający ten problem przetestował w ten sposób i poinformował nas.

Feyyaz
źródło
Bezbłędnie, jesteś geniuszem
fjcero,
1
Wygląda na to, że funkcje dołączone do $scopetworzą nowy zakres (tj. W funkcji check(), thisodnosi się do zakresu, który jest zakresem potomnym $scope). Dlaczego tak jest Czy możesz podać jakieś wyjaśnienia?
Xun Yang
1
dlaczego w takim przypadku powinienem używać „tego” zamiast „$ scope”? ponieważ działało przy użyciu $ scope w moim poprzednim projekcie, ale tym razem to samo nie działa przy użyciu $ scope. Ale „to” zadziałało. Dlaczego?
Rashedul.Rubel
to działa, ale jeśli powiesz this.searchText = "something else"lub nawet $scope.searchText = "something else"nie, nie zaktualizuje widoku. czy możesz wyjaśnić ten problem?
Shri
powinno. Próbowałem tego przy użyciu powyższego kodu, zaktualizowałem widok.
Feyyaz
13

Miałem ten sam problem i było to spowodowane tym, że najpierw nie deklarowałem pustego obiektu na górze mojego kontrolera:

$scope.model = {}

<input ng-model="model.firstProperty">

Mam nadzieję, że to zadziała dla Ciebie!

Millsionaire
źródło
10

Ten sam problem natrafiłem na nietradycyjny widok (istnieją zakresy zagnieżdżone). I w końcu odkryłem, że jest to znana trudna rzecz podczas tworzenia aplikacji AngularJS ze względu na charakter prototypowego dziedziczenia skryptu Java. Za pomocą tego mechanizmu tworzone są zagnieżdżone zakresy AngularJS. A wartość utworzona z modelu ng jest umieszczana w zakresie potomnym, nie mówiąc o tym, że zakres macierzysty (być może ten wstrzyknięty do kontrolera) nie zobaczy wartości, wartość spowoduje również cień każdej właściwości o tej samej nazwie zdefiniowanej w zakresie macierzystym, jeśli nie zostanie użyta kropka w celu wymuszenia dostępu do referencyjnego prototypu. Aby uzyskać więcej informacji, zapoznaj się z konkretnym filmem online ilustrującym ten problem, http://egghead.io/video/angularjs-the-dot/ i komentarzami po nim.

Roger Jin
źródło
6

Spójrz na to skrzypce http://jsfiddle.net/ganarajpr/MSjqL/

Mam (zakładam!) Dokładnie to, co robiłeś i wydaje się, że działa. Czy możesz sprawdzić, co tu nie działa?

ganaraj
źródło
Hmmmm .. dziękuję za przyjrzenie się temu, zakładam, że to powinno działać ... dlaczego to nie działa dla mnie? A co ważniejsze: dlaczego działa z obiektami, a nie ze zmiennymi zwykłego łańcucha? Może dlatego, że odsyłam moje kontrolery w niewłaściwy sposób w routeProvider? Próbowałem uniknąć globałów i umieściłem moje kontrolery jako modulename.ctrlName w pliku controllerers.js. Czy to może powodować ból głowy?
alchemikacja
Nie jestem do końca pewien, dlaczego to dla ciebie nie działa. Gdybyś mógł wyizolować ten problem na skrzypcach, myślę, że ktoś mógłby dać ci lepszą odpowiedź :)
ganaraj
Ok, zrobię, właśnie kończę pracę, ale zrobię to na pewno jutro lub później wieczorem, zdrowie! Może być problem ze sposobem, w jaki ustawiłem przestrzeń nazw moich klawiszy Ctrl, zobaczymy ...
alchemication
6

Ponieważ nikt o tym nie wspominał, problemu można rozwiązać, dodając $parentdo powiązanej właściwości

<div ng-controller="LoginController">
    <input type="text" name="login" class="form-control" ng-model="$parent.ssn" ng-pattern="/\d{6,8}-\d{4}|\d{10,12}/" ng-required="true" />
    <button class="button-big" type="submit" ng-click="BankLogin()" ng-disabled="!bankidForm.login.$valid">Logga in</button>
</div>

I kontroler

app.controller("LoginController", ['$scope', function ($scope) {
    $scope.ssn = '';

    $scope.BankLogin = function () {
        console.log($scope.ssn); // works!
    };
}]);
Eric Herlitz
źródło
dziękuję za odpowiedź, ale czy to działa? powinien idealnie działać na tym samym elemencie zakresu, a nie nadrzędnym?
V31,
5

Dla mnie problem został rozwiązany przez gromadzenie moich danych w obiekcie (tutaj „dane”).

NgApp.controller('MyController', function($scope) {

   $scope.my_title = ""; // This don't work in ng-click function called

   $scope.datas = {
      'my_title' : "",
   };

   $scope.doAction = function() {
         console.log($scope.my_title); // bad value
         console.log($scope.datas.my_title); // Good Value binded by'ng-model'
   }
   

});

Mam nadzieję, że to pomoże

Plici Stéphane
źródło
3
Nie ma tu
nic
@thethakuri i „datum” w języku węgierskim to „data”, więc na pewno nie
użyłbym
Wolę gofrownicę od IHOP
Nico Westerdale
2

Właśnie miałem ten problem przy użyciu root_controller powiązanego z elementem body. Następnie korzystałem z widoku ng z routerem kątowym. Problem polega na tym, że kątowe ZAWSZE tworzy nowy zakres, gdy wstawia HTML do elementu ng-view. W konsekwencji moja funkcja „sprawdź” została zdefiniowana w zakresie nadrzędnym zakresu, który został zmodyfikowany przez mój element modelu ng.

Aby rozwiązać problem, wystarczy użyć dedykowanego kontrolera w treści HTML ładowanej z trasy.

Moritz
źródło
2

Możesz to zrobić, aby włączyć wyszukiwanie w ng-keypressEnter dla tekstu wejściowego i ng-clickdla ikony:

<input type="text" ng-model="searchText" ng-keypress="keyEnter(this,$event)" />
<button ng-click="check(searchText)">Check!</button>

in the controller
$scope.search = function (searchText) {
        console.log(searchText);
    }
    $scope.keyEnter = function (serachText,$event) {
        var keyCode = $event.which || $event.keyCode;
        if (keyCode === 13) {//KeyCode for Enter key
           console.log(searchText);
        }
    }
Naoufal Gaffa
źródło
2

Miałem ten sam problem.
Właściwym sposobem byłoby ustawienie „searchText” na właściwość wewnątrz obiektu.

Ale co jeśli chcę zostawić taki, jaki jest, ciąg? cóż, próbowałem każdej wspomnianej tutaj metody, nic nie działało.
Ale potem zauważyłem, że problem dotyczy tylko inicjacji, więc właśnie ustawiłem atrybut wartości i zadziałało.

<input type="text" ng-model="searchText" value={{searchText}} />

W ten sposób wartość jest ustawiona na wartość „$ scope.searchText” i jest aktualizowana, gdy zmienia się wartość wejściowa.

Wiem, że to obejście, ale zadziałało dla mnie ..

Wycieczka Nalbandyan
źródło
2

Miałem ten sam problem ... Rozwiązaniem, które działało dla mnie, było użycie tego słowa kluczowego ...

alert (this.ModelName);

SnktJavaMaster
źródło