'this' vs $scope в контроллерах AngularJS
В разделе "Создание компонентов" на домашней странице 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 (до 1.0 RC) позволяли использовать
this
взаимозаменяемо с$scope
метод, но это больше не так. Внутри методов, определенных в области видимостиthis
а также$scope
взаимозаменяемы (угловые комплектыthis
в$scope
), но не иначе внутри вашего конструктора контроллера.
Как this
а также $scope
работать в контроллерах AngularJS?
7 ответов
"Как
this
а также$scope
работать в контроллерах AngularJS?
Краткий ответ:
this
- Когда вызывается функция конструктора контроллера,
this
это контроллер. - Когда функция определена на
$scope
объект называется,this
это "область действия, когда функция была вызвана". Это может (или не может!) Быть$scope
что функция определена на Итак, внутри функции,this
а также$scope
не может быть одинаковым
- Когда вызывается функция конструктора контроллера,
$scope
- Каждый контроллер имеет связанный
$scope
объект. - Функция контроллера (конструктора) отвечает за установку свойств модели и функций / поведения на ее
$scope
, - Только методы, определенные на этом
$scope
объект (и родительские объекты области видимости, если прототипное наследование находится в игре) доступны из HTML/ представления. Например, изng-click
, фильтры и пр.
- Каждый контроллер имеет связанный
Длинный ответ:
Функция контроллера - это функция конструктора JavaScript. Когда выполняется функция конструктора (например, когда загружается представление), this
(т. е. "контекст функции") устанавливается для объекта контроллера. Так в функции конструктора контроллера "вкладки", когда создается функция addPane
this.addPane = function(pane) { ... }
он создается на объекте контроллера, а не на $scope. Представления не могут видеть функцию addPane - они имеют доступ только к функциям, определенным в $scope. Другими словами, в HTML это не будет работать:
<a ng-click="addPane(newPane)">won't work</a>
После выполнения функции конструктора контроллера "tabs" имеем следующее:
Пунктирная черная линия указывает на наследование прототипа - область изолята, прототипно наследуемая от Scope. (Он не наследуется по прототипу от области действия, в которой директива встречалась в HTML.)
Теперь функция link директивы pane хочет связаться с директивой tabs (что на самом деле означает, что она должна каким-то образом влиять на вкладки, изолируя $ scope). Можно использовать события, но другой механизм должен иметь директиву панели require
Контроллер вкладок. (Похоже, нет механизма для директивы панели require
вкладки $scope.)
Итак, напрашивается вопрос: если у нас есть доступ только к контроллеру вкладок, как нам получить доступ к вкладкам, изолирующим $scope (что мы действительно хотим)?
Ну, красная пунктирная линия - это ответ. "Область" функции addPane() (я имею в виду область / замыкания функции JavaScript здесь) дает функции доступ к вкладкам, изолирующим область $. То есть addPane() имеет доступ к "вкладкам IsolateScope" на диаграмме выше из-за замыкания, которое было создано при определении addPane(). (Если бы мы вместо этого определили addPane() на объекте $ scope вкладок, директива pane не будет иметь доступа к этой функции, и, следовательно, не будет иметь возможности связаться с вкладками $scope.)
Чтобы ответить на другую часть вашего вопроса: how does $scope work in controllers?
:
Внутри функций, определенных в $scope, this
устанавливается в "область действия $, где / когда была вызвана функция". Предположим, у нас есть следующий 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
(Исключительно) имеет
$scope.logThisAndScope = function() {
console.log(this, $scope)
}
Нажатие на первую ссылку покажет, что this
а также $scope
одинаковы, так как "область действия, когда функция была вызвана" - это область, связанная с ParentCtrl
,
Нажатие на вторую ссылку покажет this
а также $scope
не совпадают, так как "область действия, когда функция была вызвана" - это область, связанная с ChildCtrl
, Так вот, this
установлен в ChildCtrl
"s $scope
, Внутри метода, $scope
все еще ParentCtrl
Сфера действия
Я стараюсь не использовать this
внутри функции, определенной в $scope, становится непонятно, на какую область $ затрагивается, особенно учитывая, что директивы ng-repeat, ng-include, ng-switch и все могут создавать свои собственные дочерние области.
Причина, по которой этому назначается addPane, заключается в том, что <pane>
директивы.
pane
директива делает require: '^tabs'
, который помещает объект контроллера tabs из родительской директивы в функцию link.
addPane
назначен на this
таким образом pane
Функция ссылки может видеть это. Тогда в pane
функция связи, addPane
это просто свойство tabs
контроллер, и это просто tabsControllerObject.addPane. Таким образом, функция связывания директивы панели может обращаться к объекту контроллера вкладок и, следовательно, к методу addPane.
Надеюсь, мои объяснения достаточно ясны... это сложно объяснить.
Я только что прочитал довольно интересное объяснение различий между ними и растущего предпочтения присоединять модели к контроллеру и псевдоним контроллера для привязки моделей к представлению. http://toddmotto.com/digging-into-angulars-controller-as-syntax/ это статья. Он не упоминает об этом, но при определении директив, если вам нужно разделить что-то между несколькими директивами и не хотите, чтобы сервис (есть законные случаи, когда сервисы доставляют неудобства), тогда присоедините данные к контроллеру родительской директивы. Сервис $ scope предоставляет множество полезных вещей, наиболее очевидным является $ watch, но если все, что вам нужно для привязки данных к представлению, использование простого контроллера и "контроллер как" в шаблоне - это хорошо, и, возможно, предпочтительнее.
Я рекомендую вам прочитать следующий пост: AngularJS: "Controller as" или "$scope"?
Он очень хорошо описывает преимущества использования "Controller as" для выставления переменных над "$scope".
Я знаю, что вы спрашивали конкретно о методах, а не о переменных, но я думаю, что лучше придерживаться одного метода и придерживаться его.
Поэтому, на мой взгляд, из-за проблемы с переменными, обсуждавшейся в посте, лучше просто использовать технику "Контроллер как", а также применить ее к методам.
В этом курсе ( https://www.codeschool.com/courses/shaping-up-with-angular-js) они объясняют, как использовать "это" и многие другие вещи.
Если вы добавляете метод к контроллеру через метод "this", вы должны вызывать его в представлении с именем контроллера "dot" вашего свойства или метода.
Например, используя ваш контроллер в представлении, вы можете иметь такой код:
<div data-ng-controller="YourController as aliasOfYourController">
Your first pane is {{aliasOfYourController.panes[0]}}
</div>
Предыдущие версии Angular (до 1.0 RC) позволяли вам использовать это взаимозаменяемо с методом $scope, но это уже не так. Внутри методов, определенных в области видимости, this и $scope являются взаимозаменяемыми (angular устанавливает это в $scope), но не иначе внутри конструктора контроллера.
Чтобы вернуть это поведение (кто-нибудь знает, почему оно было изменено?), Вы можете добавить:
return angular.extend($scope, this);
в конце вашей функции контроллера (при условии, что $scope был введен в эту функцию контроллера).
Это имеет хороший эффект наличия доступа к родительской области через объект контроллера, который вы можете получить в child с require: '^myParentDirective'
$scope имеет другое "this", чем контроллер "this". Таким образом, если вы поместите console.log(this) внутри контроллера, он даст вам объект (controller), а this.addPane() добавляет метод addPane к объекту контроллера. Но $scope имеет различную область видимости, и $scope.methodName() должен принимать все методы в своей области видимости.this.methodName()
внутри контроллера означает добавить методы внутри объекта контроллера.$scope.functionName()
в HTML и внутри
$scope.functionName(){
this.name="Name";
//or
$scope.myname="myname"//are same}
Вставьте этот код в ваш редактор и откройте консоль, чтобы увидеть...
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>this $sope vs controller</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.7/angular.min.js"></script>
<script>
var app=angular.module("myApp",[]);
app.controller("ctrlExample",function($scope){
console.log("ctrl 'this'",this);
//this(object) of controller different then $scope
$scope.firstName="Andy";
$scope.lastName="Bot";
this.nickName="ABot";
this.controllerMethod=function(){
console.log("controllerMethod ",this);
}
$scope.show=function(){
console.log("$scope 'this",this);
//this of $scope
$scope.message="Welcome User";
}
});
</script>
</head>
<body ng-app="myApp" >
<div ng-controller="ctrlExample">
Comming From $SCOPE :{{firstName}}
<br><br>
Comming from $SCOPE:{{lastName}}
<br><br>
Should Come From Controller:{{nickName}}
<p>
Blank nickName is because nickName is attached to
'this' of controller.
</p>
<br><br>
<button ng-click="controllerMethod()">Controller Method</button>
<br><br>
<button ng-click="show()">Show</button>
<p>{{message}}</p>
</div>
</body>
</html>