Действительно ли моя ng-модель должна иметь точку, чтобы избежать проблем с дочерними $scope?

Согласно https://github.com/angular/angular.js/wiki/Understanding-Scopes, привязать данные к примитивам, прикрепленным к вашему $scope, проблематично:

Наследование области действия обычно является простым, и вам часто даже не нужно знать, что это происходит... пока вы не попробуете двустороннюю привязку данных (например, элементы формы, ng-модель) к примитиву (например, числу, строке, логический), определенный в родительской области внутри дочерней области. Он не работает так, как большинство людей ожидает, что он должен работать.

Рекомендация

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


Теперь у меня есть очень простая установка, которая нарушает эти правила:

HTML:

<input type="text" ng-model="theText" />
<button ng-disabled="shouldDisable()">Button</button>

JS:

function MyController($scope) {
    $scope.theText = "";
    $scope.shouldDisable = function () {
         return $scope.theText.length >= 2;
    };
}

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


Мне нужно изменить его на что-то вроде

function MyController($scope) {
    $scope.theText = { value: "" };
    $scope.shouldDisable = function () {
         return $scope.theText.value.length >= 2;
    };
}

а также

<input type="text" ng-model="theText.value" />
<button ng-disabled="shouldDisable()">Button</button>

так что я следую передовой практике? Какой конкретный пример вы можете мне привести, где последнее спасет меня от каких-то ужасных последствий, которые имело бы место в первом?


person Domenic    schedule 18.06.2013    source источник


Ответы (2)


множество новых возможностей. Допустим, вы действительно хотите добавить в свои контроллеры вкладки: первая вкладка — это фактический рендеринг, вторая вкладка — это форма (чтобы у вас был предварительный просмотр в реальном времени).

Вы решаете использовать для этого директиву:

<tabs>
  <tab name="view">
    <pre>{{theText|formatInSomeWay}}</pre>
  </tab>
  <tab name="edit" focus="true">
    <input type="text" ng-model="theText" />
  </tab>
</tabs>

Ну знаете что? <tabs> имеет свою собственную область видимости и сломала ваш контроллер! Итак, когда вы редактируете, angular будет делать что-то вроде этого в js:

$scope.theText = element.val();

который не будет пересекать цепочку прототипов, чтобы попытаться установить theText для родителей.

РЕДАКТИРОВАТЬ: просто чтобы быть ясным, я использую только «вкладки» в качестве примера. Когда я говорю «множество новых возможностей», я имею в виду следующее: ng-include, ng-view, ng-switch, ng-controller (конечно) и т. д.

Итак: это может быть не нужно сейчас, потому что у вас еще нет дочерних областей в этом представлении, но вы не знаете, собираетесь ли вы добавлять дочерние шаблоны или нет, что со временем могут изменить theText сами, что вызовет проблему. Чтобы сохранить свой дизайн в будущем, всегда следуйте правилу, и тогда вы не будете удивлены;).

person Ven    schedule 19.06.2013
comment
Итак, предварительные условия для того, чтобы облажаться, похоже, заключаются в том, что есть дочерняя область, которая также ссылается на theText. Это правильно? - person Domenic; 19.06.2013
comment
Это изменяет его *. Просто ссылаться на это нормально. - person Ven; 19.06.2013
comment
Итак, в моем примере из исходного поста все должно быть в порядке, и я не облажаюсь, поскольку мой шаблон не содержит никаких дочерних директив, ссылающихся на theText? - person Domenic; 19.06.2013
comment
Точно. Правило точек (имхо) все еще применяется, потому что вы не знаете, собираетесь ли вы добавлять дочерние шаблоны в будущем. - person Ven; 19.06.2013
comment
Я собирался добавить ответ на этот вопрос, но последний комментарий касается того, что я хотел добавить: использование правила точек в будущем подтверждает ваш дизайн. - person Mark Rajcok; 19.06.2013
comment
Хорошо, это имеет смысл. Если вы хотите обновить ответ, чтобы поговорить о будущем и о том, как все это зависит от того, добавите ли вы дочерние шаблоны позже, это поможет вернуться к ответу на исходный вопрос, который должен был быть я что-то делаю супер-просто, правило точки все еще применяется? - person Domenic; 19.06.2013
comment
В этом умном видео есть фантастический пример этой проблемы в действии: egghead.io/lessons/angularjs-the -точка - person Ben Barreth; 29.10.2014

Допустим, у вас есть области M, A и B, где M является родителем как A, так и B.

Если один из (A,B) попытается записать в область M, он будет работать только с непримитивными типами. Причина этого в том, что непримитивные типы передаются по ссылке.

С другой стороны, примитивные типы не являются таковыми, поэтому попытка записи в theText в области M создаст новое свойство с тем же именем в области A или B соответственно, вместо записи в M. Если и A, и B зависят от этого свойства, будут возникать ошибки, потому что ни один из них не будет знать, что делает другой.

person holographic-principle    schedule 18.06.2013
comment
Да, я вроде как понимаю проблему теоретически, но не вижу, как это повлияет на этот простой пример на практике. Например. Если и A, и B зависят от этого свойства — когда это будет верно? Пример, основанный на моем, был бы превосходным. - person Domenic; 19.06.2013
comment
Допустим, у вас был контроллер уровня приложения, а внутри него был ng-view, куда вы загружали отдельные контроллеры в зависимости от маршрутов. Затем вы можете захотеть сохранить какое-то состояние на главном контроллере после переключения маршрутов. Это один пример. - person holographic-principle; 19.06.2013
comment
Некоторые директивы, такие как ng-include, создают свою собственную область видимости. Еще одно место, где что-то может пойти не так. - person holographic-principle; 19.06.2013
comment
Итак, в этом примере мне нужно было бы иметь два отдельных маршрута, каждый из которых хочет изменить theText, чтобы возникла эта проблема? - person Domenic; 19.06.2013
comment
Достаточно того, что один должен изменить его, если оба должны его прочитать. - person holographic-principle; 19.06.2013
comment
Похоже, это не совсем применимо к моему случаю <input> и <button>, но больше касается состояния, которое предназначено для совместного использования между большими подкомпонентами приложения. - person Domenic; 19.06.2013
comment
Если вы вообще не используете дочерние области видимости, то вас это вообще не касается. Но в реальных ситуациях вы будете их использовать :). - person holographic-principle; 19.06.2013
comment
Также известно, что ng-repeat терпит неудачу при работе с массивами примитивов. Я не знаю, было ли это каким-то образом исправлено, но важно знать, что проблема возникает из того же места. (ng-repeat создает отдельную область) - person holographic-principle; 19.06.2013
comment
@Доменик, меня это укусило. В моем примере foo scope внутри контроллера, который хранит список вещей, отображаемых с помощью ng-repeat. Затем также пытаемся иметь представление редактирования, которое является дочерней областью foo, которая хочет редактировать определенный элемент. Привязка показывает информацию из-за наследования, но затем, когда я пытался что-то редактировать, она просто создавала локальную версию свойств и фактически не меняла список в foo - person Chris Matheson; 19.06.2013
comment
Наконец-то я решил применить некоторые концепции здесь - у меня были проблемы с доступом к значению даты из календаря, и объявление модели как $parent.datetimeValue решило все мои проблемы с доступом к переменной, и доступ к ним как $scope.$parent.datetimeValue был в порядке из всех дочерних областей. - person PayToPwn; 06.05.2016