Ng-модель не обновляет значение контроллера

270

Возможно глупый вопрос, но у меня есть HTML-форма с простым вводом и кнопкой:

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

Затем в контроллере (шаблон и контроллер вызываются из routeProvider):

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

Почему при нажатии кнопки вид обновляется, но не отображается в консоли?

Спасибо!

Обновление: Похоже, что я действительно решил эту проблему (прежде чем приходилось придумывать какие-то обходные пути) с помощью: нужно было только изменить имя моего свойства с searchTextна search.text, затем определить пустой $scope.search = {};объект в контроллере и вуаля ... Понятия не имею, почему это работает хотя ;]

alchemication
источник
Вы уверены, что используете этот контроллер в этой части документа? Вы можете опубликовать минимальный неудачный пример?
Врониастия
1
Да, на 100% уверен, что с контроллером все в порядке, мне кажется, что этот вопрос мне знаком ... Удивительно, но он работает, когда я изменяю имя свойства с searchTextна search.text, любая идея почему ??
alchemication
4
@Arthur: Это вроде не очевидно, но ng-model создает только своего рода локальную переменную по вашему мнению, поэтому, если вы хотите сохранить ее таким образом, вам нужно будет передать ее в функцию check (), например: check ( searchText), и ваш контроллер распознает его. Надеюсь, это поможет
алхимика
3
Для записи, это пишется voila, а не vuala, wollaи т. Д.
Дэн Бешард
3
Я думаю, что ответ, который вы ищете, находится на stackoverflow.com/a/14049482/1217913
Willa

Ответы:

71

Контроллер как версия (рекомендуется)

Вот шаблон

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

Пример: http://codepen.io/Damax/pen/rjawoO

Лучше всего будет использовать компонент с Angular 2.x или Angular 1.5 или выше

########

Старый способ (НЕ рекомендуется)

Это НЕ рекомендуется, потому что строка является примитивной, настоятельно рекомендуется использовать объект вместо

Попробуйте это в вашей разметке

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

и это в вашем контроллере

$scope.check = function (searchText) {
    console.log(searchText);
}
Damax
источник
17
Это работает только в одну сторону ... что делать, если вы хотите изменить значение searchText?
cdmckay
199
Это также не отвечает "почему?" вопрос.
cdmckay
4
Что делать, если я не хочу использовать любую кнопку?
Мне
2
Saniko => Чтобы отправить при входе, вы должны использовать тег формы и ng-submit для него ( docs.angularjs.org/api/ng.directive:ngSubmit )
Damax
1
@ HP's411 это потому, что строка примитивна. Когда Angular присваивает значение, он меняет указатель на значение, поэтому контроллер смотрит на старое значение, потому что у него есть старый указатель на значение.
Дамакс
620

«Если вы используете ng-модель, у вас должна быть точка».
Сделайте так, чтобы ваша модель указывала на object.property, и вам будет хорошо.

контроллер

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

шаблон

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

Это происходит, когда дочерние области находятся в игре - как дочерние маршруты или ng-повторы. Дочерняя область создает свое собственное значение, и возникает конфликт имен, как показано здесь :

Подробнее об этом видео: https://www.youtube.com/watch?v=SBwoFkRjZvE&t=3m15s.

Уилл Стерн
источник
94
Это настоящий ответ.
keslert
16
@Catfish При наследовании прототипа, когда вы пишете в дочернее свойство, ссылка / связь с родительским свойством перестает существовать. Способ, которым моделирует угловые области, если у вас нет точки, то создается новое свойство в вашей дочерней области. Это видео объясняет это более подробно: youtube.com/watch?v=ZhfUv0spHCY&feature=youtu.be&t=30m
Уилл Стерн,
1
Грасиас Босс. Это верно! Тем не менее, это не кажется странной причудой, так как я сталкиваюсь с проблемой только при использовании с Ionic Framework
Дон Барри
6
@ user3677331 Он работает нормально без точки, пока у вас не появится дочерняя область, которая пытается с ней поговорить (например, как элемент в ng-repeat). Скажите, что ваша модель называлась «телефон». Ваша дочерняя область создает «телефон», тогда вы получаете конфликт области, потому что дочерняя область имеет переменную «телефон» и, следовательно, не может получить доступ к «телефону» в родительской области. Принимая во внимание, что если дочерняя область создает user.phone, он будет добавлен к пользовательскому объекту родителя, поэтому обе области указывают на один и тот же объект
Уилл Стерн
3
Очень полезно. Я не был уверен, что найду ответ на относительно неопределенный вопрос, но он был мертв.
CodeManiak
57

В книге «Освоение разработки веб-приложений с помощью AngularJS» с.19 написано, что

Избегайте прямых привязок к свойствам области. Двусторонняя привязка данных к свойствам объекта (представленная в области) является предпочтительным подходом. Как правило, в выражении, указанном в директиве ng-model, должна быть точка (например, ng-model = "thing.name").

Области действия - это просто объекты JavaScript, и они имитируют иерархию DOM. Согласно JavaScript Prototype Inheritance , свойства областей разделяются через области. Чтобы избежать этого, точечные обозначения следует использовать для привязки ng-моделей.

efirat
источник
2
спасибо за эту ссылку. Я считаю это интересным моментом, связанным с ng-моделью.
Короли
44

Использование thisвместо $scopeработ.

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>

Редактировать: во время написания этого ответа у меня была гораздо более сложная ситуация, чем эта. После комментариев я попытался воспроизвести его, чтобы понять, почему он работает, но не повезло. Я думаю, что каким-то образом (не знаю почему) генерируется новая дочерняя область, которая thisссылается на эту область. Но если $scopeон используется, он на самом деле ссылается на родительский $ scope из-за функции лексической области видимости javascript .

Было бы здорово, если бы кто-то, имеющий эту проблему, проверил этот способ и сообщил нам.

Feyyaz
источник
Безупречно, ты гений
fjcero
1
Похоже, функции, прикрепленные к, $scopeсоздают новую область (то есть в функции check(), thisссылаются на область, которая является дочерней областью $scope). Почему это так? Можете ли вы дать некоторые объяснения?
Сюнь Ян
1
почему я должен использовать 'this' вместо '$ scope' в этом случае? потому что он работал с использованием $ scope в моем предыдущем проекте, но на этот раз то же самое не работает с использованием $ scope. Но «это» сработало. Это почему?
Rashedul.Rubel
это работает, но если вы говорите this.searchText = "something else"или даже если $scope.searchText = "something else", это не обновляет представление. Вы можете объяснить эту проблему?
Шри
должно. Я попробовал это используя код выше, это обновило представление.
Фейяз
13

У меня была та же проблема, и это было из-за того, что я сначала не объявил пустой объект в верхней части моего контроллера:

$scope.model = {}

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

Надеюсь, это будет работать для вас!

Millsionaire
источник
10

Я столкнулся с той же проблемой, когда имел дело с нетривиальным представлением (есть вложенные области видимости). И наконец обнаружил, что это известная хитрая вещь при разработке приложения AngularJS из-за природы наследования java-скриптов на основе прототипов. Вложенные области AngularJS создаются с помощью этого механизма. И значение, созданное из ng-модели, помещается в дочернюю область, не говоря, что родительская область (может быть, введенная в контроллер) не увидит значение, значение также будет затенять любое свойство с таким же именем, определенное в родительской области, если не использовать точку для обеспечения доступа к прототипу ссылки. Для получения более подробной информации просмотрите онлайн-видео, посвященное этой проблеме, http://egghead.io/video/angularjs-the-dot/ и комментарии по нему.

Роджер Джин
источник
6

Посмотрите на эту скрипку http://jsfiddle.net/ganarajpr/MSjqL/

Я (я предполагаю!) Сделал именно то, что вы делали, и, похоже, работает. Можете ли вы проверить, что здесь не работает для вас?

ganaraj
источник
Хмммм .. спасибо, что заглянули в это, я бы предположил, что это должно работать ... почему это не работает для меня? И что более важно: почему он работает с объектами, а не с простыми строковыми переменными ?? Может быть потому, что я соответствующим образом направляю свои контроллеры в routeProvider ?? Пытался избежать глобалов и поместил мои контроллеры как modulename.ctrlName в файл controllers.js. Может ли это вызвать головную боль?
алхимическая обработка
Я не совсем уверен, почему это не работает для вас. Если бы вы могли выделить эту проблему на скрипке, я думаю, кто-то сможет дать вам лучший ответ :)
ganaraj
Хорошо, сделаю, просто заканчиваю работу сейчас, но сделаю это наверняка завтра или позже вечером, ура! Может быть проблема с тем, как я распределил имена по моим ctrl'ам, посмотрим ...
Алхимикация
6

Поскольку никто не упомянул об этом, проблему можно решить, добавив $parentк связанному свойству

<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>

И контроллер

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

    $scope.BankLogin = function () {
        console.log($scope.ssn); // works!
    };
}]);
Эрик Херлиц
источник
спасибо за ответ, однако у это работает? в идеале он должен работать на том же элементе видимости, а не на родительском элементе?
V31
5

Для меня проблема была решена путем складирования моих данных в объект (здесь "данные").

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'
   }
   

});

Надеюсь, это поможет

Пличи Стефан
источник
3
Немного не по теме здесь, но «данные» - это множественное число для термина «датум»
thethakuri
@thethakuri и «datum» на венгерском языке - это «дата», так что я уверен, что тоже не буду использовать это: P
EpicPandaForce
Я предпочитаю вафельницу IHOP
Нико
2

У меня только что была эта проблема с использованием root_controller, привязанного к элементу body. Тогда я использовал ng-view с угловым роутером. Проблема в том, что angular ВСЕГДА создает новую область видимости при вставке html в элемент ng-view. Как следствие, моя функция «check» была определена в родительской области видимости, которая была изменена моим элементом ng-model.

Чтобы решить проблему, просто используйте выделенный контроллер в загруженном по маршруту HTML-содержимом.

Moritz
источник
2

Вы можете сделать это, чтобы включить поиск по ng-keypressвводу для ввода текста и ng-clickпо значку:

<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);
        }
    }
Науфал Гаффа
источник
2

У меня такая же проблема.
Правильным способом было бы установить searchText как свойство внутри объекта.

Но что, если я захочу оставить строку как есть? ну, я попробовал каждый упомянутый здесь метод, ничего не получалось.
Но потом я заметил, что проблема только в инициации, поэтому я просто установил атрибут значения, и это сработало.

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

Таким образом, значение просто устанавливается в значение «$ scope.searchText», и оно обновляется при изменении входного значения.

Я знаю, что это обходной путь, но он работал для меня ..

Поход Налбандян
источник
2

Я столкнулся с той же проблемой ... Решение, которое работало для меня, это использовать это ключевое слово ..........

оповещения (this.ModelName);

SnktJavaMaster
источник