Двусторонняя привязка, переменная области видимости не определена

Я пишу приложение с OnesnUI и AngularJS, используя ng-model для получения входных данных от элементов DOM.

Вот мой код

<body>
  <ons-screen>
      <ons-page class="center" ng-controller="page1Ctrl">
    <ons-navigator title="Page 1">
        <div class="center">
          <h1>Pharmy</h1>
            <ons-text-input ng-model="medName" placeholder="Enter Medicine Name" style="display:block; width:100%;" id="test"></ons-text-input>
            <div style="position:relative">
                <ons-text-input ng-model="location" placeholder="Enter Location" style="display:block; width:100%; margin-top:10px;"></ons-text-input>
                <ons-icon icon="search" style="position:absolute;right:10px;top:5px;"></ons-icon>
            </div>
            <ons-button ng-click="goToPage2()"
                        style="width:10%; margin-top:10px;">
                <ons-icon icon="search"></ons-icon>
            </ons-button>
        </div>
    </ons-navigator>
          </ons-page>
  </ons-screen>
</body>

и пытаясь получить значение из текстового поля ввода, есть мой app.js:

    (function () {
        'use strict';
        var app = angular.module('myApp', ['onsen.directives']);
        app.controller('page1Ctrl',['$scope','pageService',function($scope,pageService){
            $scope.$watch('medName',function(newVlaue){
                console.log('Watching YOU !' + newVlaue);
            });
            console.log($scope.medName);
            $scope.goToPage2 = function(){
                console.log('Going to page2 !');
                console.log($scope.medName);
                pageService.start($scope.medName);
                $scope.ons.navigator.pushPage("page2.html");
            }
        }]);
    })();

Почему напечатанное значение medName равно undefined ?


person Ammar Lakis    schedule 23.07.2014    source источник
comment
у вас есть console.log($scope.medName); в двух местах, где вы берете undefined?   -  person Mritunjay    schedule 23.07.2014
comment
... сделайте это три места. (в $watch тоже)   -  person xtofl    schedule 23.07.2014
comment
@Mritunjay в каждой команде печати значение не обновляется   -  person Ammar Lakis    schedule 23.07.2014
comment
Что вводится в ons-text-input, связанном с medName?   -  person xtofl    schedule 23.07.2014
comment
@xtofl любой текст, foo bar   -  person Ammar Lakis    schedule 23.07.2014
comment
Попробуйте объявить переменную medName в вашем контроллере первой строкой: $scope.medName = ''; Это также может быть ошибка прототипного наследования, описанная здесь.   -  person Rob J    schedule 23.07.2014
comment
Вероятно, это создает medName в какой-то внутренней области. Я бы сделал $scope.foo = {}, а затем ng-model="foo.medName", и это должно сработать.   -  person Jesus Rodriguez    schedule 23.07.2014
comment
@RobJacobs уже пробовал это, он печатает начальное значение, но не обновляется.   -  person Ammar Lakis    schedule 23.07.2014
comment
@JesusRodriguez, хотя у меня нет других внутренних областей, ваш метод сработал.   -  person Ammar Lakis    schedule 23.07.2014
comment
Хорошо, позвольте мне создать правильный ответ.   -  person Jesus Rodriguez    schedule 23.07.2014


Ответы (4)


Если бы мне нужно было назвать правильную проблему в Angular, это было бы наследование области видимости.

Если у вас, скажем, $scope.foo = "hello" является контроллером, а затем вы создаете дочернюю область этого контроллера, а затем пытаетесь присвоить новое значение foo, которое создаст новый foo внутри этой новой области. Ваш родительский контроллер никогда не увидит изменения.

В вашей проблеме вы не создаете новую область, а OnsenUI делает это, возможно, на ons-screen или ons-navigator. Затем ваш ввод находится внутри новой дочерней области, и если вы попытаетесь назначить newMed, он сделает это в дочерней области.

Правило всегда состоит в том, чтобы иметь объекты, которые называются dot rule (отметьте здесь).

Поэтому, если вы не хотите снова иметь эту проблему, всегда делайте то, что я сказал вам в комментарии, создайте что-то вроде $scope.meds = {}, а затем ng-model="meds.newMed", потому что таким образом он сначала будет искать meds на родителях, и если он найдет его, он будет использовать его, и это то, что вы хотите сделать.

Вот первый пример неправильного выполнения: http://plnkr.co/edit/IJZOa1pXtcQePiEVMzjZ?p=preview

<body ng-controller="MainCtrl">
  <foo>
    <input ng-model="name"> <br />
    Inside new scope: {{ name }}
  </foo>
  <br />
  On the parent scope: {{ name }}

app.controller('MainCtrl', function($scope) {
  $scope.name = 'World';
});

app.directive('foo', function() {
  return {
    restrict: 'E',
    scope: true
  };
});

Здесь мы являемся директивой, создающей новую область видимости foo. Затем в нашем html внутри <foo> мы помещаем ng-model с name. Он читает текущую область, о, не там, поэтому он идет к области действия контроллера и находит ее. Итак, вы видите World на входе. Когда вы меняете ввод, он захватывает его содержимое и назначает новое значение. Как? Он переходит в свою область действия и присваивает ее. Проблема в том, что его область действия — это новая область действия, созданная foo, и поэтому родитель никогда не увидит никаких изменений.

Давайте посмотрим на пример номер два: http://plnkr.co/edit/BSx37PpAEbPnHfs2jr5o?p=preview

<body ng-controller="MainCtrl">
  <foo>
    <input ng-model="user.name"> <br />
    Inside new scope: {{ user.name }}
  </foo>
  <br />
  On the parent scope: {{ user.name }}
</body>

-

app.controller('MainCtrl', function($scope) {
  $scope.user = {
    name: 'World'
  };
});

app.directive('foo', function() {
  return {
    restrict: 'E',
    scope: true
  };
});

Вот тот же пример, но с небольшим изменением. Здесь у нас есть name внутри объекта с именем user. Когда ввод читает его, нет никакой разницы, но когда он собирается назначить новое значение, он делает что-то другое, что-то вроде: Эй, новая область, у тебя есть этот пользовательский объект? Эээ нет... хорошо, родитель, у тебя есть этот пользовательский объект? Я делаю. Хорошо, тогда поместите в него свойство под названием name с этим содержимым.

Вы видите разницу? Раньше он не спрашивал родителя, он просто создавал name во внутренней области, но если вы используете объекты, он сначала попытается найти объект, именно то, что нам нужно.

TL;DR;: всегда используйте объекты, потому что с вами произошло то, что некоторые директивы, которые вы использовали, создают новые области, и ваши новые назначения будут созданный во внутренней области.

person Jesus Rodriguez    schedule 23.07.2014
comment
Я не очень хорошо разбираюсь в angular, но я думаю, что это будет лучше с примером скрипки. - person Jai; 23.07.2014
comment
О, это точно, дай мне пару минут. - person Jesus Rodriguez; 23.07.2014
comment
@Jai готово :). Я надеюсь, что теперь это имеет больше смысла для вас. - person Jesus Rodriguez; 23.07.2014

Создал простой jsfiddle, чтобы показать, как это делается и хорошо работает в Angular.

Первый ваш console.log будет неопределенным, так как вы ничего не установили в контроллере.

Что-то типа

 $scope.medName = '';

помогло бы.

Фрагмент кода контроллера, который я использовал в примере для демонстрации:

function test($scope){
    $scope.medName = '';
    console.log($scope.medName);
    $scope.$watch('medName',function(newValue){
        console.log("new Value:"+newValue);
    });
    $scope.getUpdatedScope = function(){
        console.log($scope.medName);
    }
}
person V31    schedule 23.07.2014

Ваш код действительно работает, вы что-то ввели в фактический ввод? Он пишет undefined в начале, потому что он не определен в области видимости, а ваше входное значение равно null.

Я быстро сделал jsfiddle, вот (в основном ваш) код. HTML:

<div ng-app="myApp">
    <div ng-controller="page1Ctrl">
        <input ng-model="medName" />
        <p ng-if="medName">{{medName}}</p>
    </div>
</div>

JS:

var app = angular.module('myApp', []);
app.controller('page1Ctrl',['$scope', function($scope){
    $scope.$watch('medName',function(newVlaue){
        console.log('Watching YOU !' + newVlaue);
    });
    console.log($scope.medName);
}]);

Вот jsfiddle: http://jsfiddle.net/q3z22/4/ Вы увидите в консоль "undefined" в начале, а дальше все что пишешь в вводе. Если вы этого не хотите, просто определите в области, написав $scope.medName = '';, и у вас будет пустая строка в начале.

person Mimu    schedule 23.07.2014

Вы где-то устанавливаете директиву ng-app? Попробуйте добавить его в тег body, например:

<body ng-app="myApp">
person Cognitronic    schedule 23.07.2014