'this' vs $ scope в контроллерах AngularJS

912

В разделе "Создать компоненты" на главной странице AngularJS приведен пример:

controller: function($scope, $element) {
  var panes = $scope.panes = [];
  $scope.select = function(pane) {
    angular.forEach(panes, function(pane) {
      pane.selected = false;
    });
    pane.selected = true;
  }
  this.addPane = function(pane) {
    if (panes.length == 0) $scope.select(pane);
    panes.push(pane);
  }
}

Обратите внимание, что метод select добавлен в $scope, но метод addPane добавлен в this. Если я изменю его на $scope.addPane, код сломается.

В документации говорится, что на самом деле это разница, но в ней не упоминается, в чем разница:

Предыдущие версии Angular (pre 1.0 RC) позволили использовать this взаимозаменяемо с методом $scope, но это уже не так. Внутри методов, определенных в области this и $scope, взаимозаменяемы (angular устанавливает this в $scope), но не внутри внутри вашего конструктора контроллера.

Как работают this и $scope в контроллерах AngularJS?

  • 0
    Я нахожу это смущающим также. Когда представление задает контроллер (например, ng-controller = '...'), $ scope, связанный с этим контроллером, кажется, идет вместе с ним, потому что представление может получить доступ к свойствам $ scope. Но когда директива 'требует другого контроллера (а затем использует его в своей функции связывания), $ scope, связанный с этим другим контроллером, не приходит вместе с ним?
  • 0
    Эта запутанная цитата о "Предыдущих версиях ..." уже удалена? Тогда может быть обновление будет на месте?
Показать ещё 1 комментарий
Теги:
this
angularjs-scope

6 ответов

942
Лучший ответ

"Как работают this и $scope в контроллерах AngularJS?"

Короткий ответ:

  • this
    • Когда вызывается функция конструктора контроллера, this является контроллером.
    • Когда вызывается функция, определенная в объекте $scope, this - это "область действия, когда функция была вызвана". Это может (или не может быть!) Быть $scope, что функция определена. Таким образом, внутри функции this и $scope может не быть одинаковым.
  • $scope
    • Каждый контроллер имеет связанный объект $scope.
    • Функция контроллера (конструктора) отвечает за настройку свойств модели и функций/поведения в ее связанной $scope.
    • Доступны только методы, определенные в этом объекте $scope (и объекты родительской области, если прототипное наследование находится в игре) доступны из HTML/представления. Например, из ng-click, фильтров и т.д.

Длинный ответ:

Функция контроллера - это функция конструктора JavaScript. Когда функция конструктора выполняется (например, при загрузке представления), this (то есть, "контекст функции" ) устанавливается в объект контроллера. Таким образом, в функции конструктора контроллера "tabs", когда создается функция addPane

this.addPane = function(pane) { ... }

он создается на объекте контроллера, а не на $scope. Представления не видят функцию addPane - они имеют доступ только к функциям, определенным в области $scope. Другими словами, в HTML это не сработает:

<a ng-click="addPane(newPane)">won't work</a>

После выполнения функции конструктора контроллера "tabs" мы имеем следующее:

Изображение 3639

Пунктирная черная линия указывает на прототипное наследование - изоляционная область прототипа наследует от Scope. (Он не прототипически наследуется от области действия, в которой директива встречается в HTML.)

Теперь функция ссылки на панели управления должна связываться с директивой tabs (что на самом деле означает, что она должна каким-то образом повлиять на вкладки, изолировать $scope). Можно использовать события, но другим механизмом является наличие указателя панели require контроллера табуляции. (Кажется, нет механизма для директивы панели для require вкладки $scope.)

Итак, это ставит вопрос: если у нас есть только доступ к контроллеру табуляции, как мы получим доступ к вкладкам, изолируем $scope (что мы действительно хотим)?

Ну, красная пунктирная линия - это ответ. Функция "scope" функции addPane() (я имею в виду область функций/закрытия JavaScript здесь) дает функции доступа к вкладкам изолировать $scope. I.e., addPane() имеет доступ к "вкладкам IsolateScope" на диаграмме выше из-за закрытия, которое было создано, когда была определена addPane(). (Если мы вместо этого определяем addPane() в объекте облака tabs $, директива панели не будет иметь доступа к этой функции, и, следовательно, у нее не будет возможности связываться с областью вкладок $.)

Чтобы ответить на другую часть вашего вопроса: how does $scope work in controllers?:

В функциях, определенных в области $scope, this устанавливается значение "$ scope", в котором/когда функция была вызвана ". Предположим, что мы имеем следующий HTML:

<div ng-controller="ParentCtrl">
   <a ng-click="logThisAndScope()">log "this" and $scope</a> - parent scope
   <div ng-controller="ChildCtrl">
      <a ng-click="logThisAndScope()">log "this" and $scope</a> - child scope
   </div>
</div>

И ParentCtrl (Solely) имеет

$scope.logThisAndScope = function() {
    console.log(this, $scope)
}

Щелчок по первой ссылке показывает, что this и $scope совпадают, так как "область действия, действующая при вызове функции", - это область, связанная с ParentCtrl.

Щелчок по второй ссылке покажет, что this и $scope являются не тем же, поскольку "область действия, когда функция была вызвана", - это область, связанная с ChildCtrl. Итак, здесь this установлен на ChildCtrl $scope. Внутри метода $scope по-прежнему остается ParentCtrl $scope.

Fiddle

Я пытаюсь не использовать this внутри функции, определенной в области $scope, так как это запутывает, на какую область видимости влияет, особенно учитывая, что ng-repeat, ng-include, ng-switch и директивы могут все создать свои собственные дочерние области.

  • 1
    Когда вызывается функция, определенная для объекта $ scope, это «область действия, действующая при вызове функции». Это может (или не может!) Быть областью $, в которой определена функция. Таким образом, внутри функции this и $ scope могут не совпадать. Но в официальной документации AngularJS говорится, что внутри методов, определенных в области видимости this и $ scope, являются взаимозаменяемыми (angular устанавливает это в $ scope), но не иначе внутри конструктора контроллера.
  • 6
    @tamakisquare, я полагаю, что текст, который вы цитируете жирным шрифтом, применяется при вызове функции конструктора контроллера, т. е. при создании контроллера, связанного с областью $. Это не применяется позже, когда произвольный код JavaScript вызывает метод, определенный для объекта $ scope.
Показать ещё 24 комментария
46

Причиной "addPane" назначается это из-за директивы <pane>.

Директива pane выполняет require: '^tabs', которая помещает объект контроллера таблеток из родительской директивы в функцию ссылки.

addPane присваивается this, так что функция ссылки pane может его видеть. Затем в функции pane link, addPane является просто свойством контроллера tabs, и он просто tabsControllerObject.addPane. Таким образом, функция связывания с указателем панели может обращаться к объекту контроллера таблеток и, следовательно, обращаться к методу addPane.

Надеюсь, мои объяснения достаточно ясны. Это трудно объяснить.

  • 3
    Спасибо за объяснение. Документы создают впечатление, что контроллер - это просто функция, которая устанавливает область действия. Почему контроллер обрабатывается как объект, если все действие происходит в области видимости? Почему бы просто не передать родительскую область в функцию связывания? Изменить: Чтобы лучше сформулировать этот вопрос, если методы контроллера и методы области действия работают с одной и той же структурой данных (область действия), почему бы не разместить их все в одном месте?
  • 0
    Похоже, родительская область не передается в функцию lnk из-за желания поддерживать «повторно используемые компоненты, которые не должны случайно читать или изменять данные в родительской области». Но если директива действительно хочет / нуждается в чтении или изменении НЕКОТОРЫХ СПЕЦИФИЧЕСКИХ данных в родительской области (как это делает директива pane), она требует определенных усилий: «требуют» контроллера, где находится желаемая родительская область, а затем определяют метод на этом контроллере (используйте 'this', а не $ scope) для доступа к конкретным данным. Поскольку требуемая родительская область не вводится в функцию lnk, я полагаю, что это единственный способ сделать это.
Показать ещё 2 комментария
22

Я просто прочитал довольно интересное объяснение различий между ними и растущее предпочтение прикреплять модели к контроллеру и псевдоним контроллера для привязки моделей к представлению. http://toddmotto.com/digging-into-angulars-controller-as-syntax/ - статья. Он не упоминает об этом, но при определении директив, если вам нужно поделиться чем-то между несколькими директивами и не хотеть службы (есть законные случаи, когда сервисы - это хлопот), тогда присоедините данные к родительскому директивному контроллеру. Служба $scope предоставляет множество полезных вещей, $watch является наиболее очевидным, но если все, что вам нужно, привязать данные к представлению, используя простой контроллер и "контроллер как" в шаблоне, отлично и, возможно, предпочтительнее.

15

В этом курсе (https://www.codeschool.com/courses/shaping-up-with-angular-js) они объясняют, как использовать "this" и многое другое.

Если вы добавляете метод к контроллеру с помощью метода "this", вы должны вызвать его в представлении с именем контроллера "точка" вашего свойства или метода.

Например, используя ваш контроллер в представлении, у вас может быть такой код:

    <div data-ng-controller="YourController as aliasOfYourController">

       Your first pane is {{aliasOfYourController.panes[0]}}

    </div>
  • 6
    После прохождения курса меня сразу смутил код, использующий $scope , поэтому спасибо, что упомянули его.
  • 16
    Этот курс вообще не упоминает $ scope, они просто используют as и this так как это может помочь объяснить разницу?
Показать ещё 4 комментария
14

Я рекомендую вам прочитать следующее сообщение: http://codetunnel.io/angularjs-controller-as-or-scope/

он очень хорошо описывает преимущества использования "Контроллера как" для отображения переменных по "$ scope".

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

Итак, по моему мнению, из-за проблемы с переменными, обсуждаемой в сообщении, лучше просто использовать технику "Контроллер как", а также применить ее к методам.

3

Предыдущие версии Angular (pre 1.0 RC) позволили вам использовать это взаимозаменяемо с методом $scope, но это уже не дело. Внутри методов, определенных в области действия this и $scope, взаимозаменяемый (angular устанавливает это значение в $scope), но не иначе внутри вашего конструктора контроллера.

Чтобы вернуть это поведение (кто-нибудь знает, почему он был изменен?), вы можете добавить:

return angular.extend($scope, this);

в конце вашей функции контроллера (при условии, что $scope был введен в эту функцию контроллера).

Это имеет приятный эффект от доступа к родительской области с помощью объекта контроллера, который вы можете получить с дочерним элементом require: '^myParentDirective'

  • 7
    Эта статья дает хорошее объяснение того, почему это и $ scope отличаются.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню