No "Criar Componentes" seção da página inicial da AngularJS's, há este exemplo:
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);
}
}
Observe como o método select
é adicionado ao $scope
, mas o método addPane
é adicionado ao this
. Se eu mudar para $scope.addPane
, o código quebra.
A documentação diz que de facto há uma diferença, mas não'não menciona qual é a diferença:
As versões anteriores do Angular (pré 1.0 RC) permitiam a utilização do método "este" intercambialmente com o método "$$scope", mas este já não é o caso. Dentro dos métodos definidos no escopo
this
e$scope
são intercambiáveis (conjuntos angularesthis
a$scope
), mas não dentro do seu construtor de controladores.
Como funcionam "isto" e "escopo" nos controladores da AngularJS?
"Como funcionam "isto" e "escopo" nos controladores AngularJS?"
**Resposta curta***:
*this''.
$scope
é chamada, isto
é o " scope em efeito quando a função foi chamada". Este pode (ou não!) ser o $scope
em que a função está definida. Então, dentro da função, isto
e $scope
podem não ser o mesmo.$escopo
$scope
associado.$scope
associado.$scope
(e objetos de escopo pai, se herança prototípica estiver em jogo) são acessíveis a partir do HTML/view. Por exemplo, a partir do ng-click
, filtros, etc.**Resposta longa***:
A função do controlador é uma função construtora de JavaScript. Quando a função de construtor executa (por exemplo, quando uma vista carrega), isto
(isto é, o "function context") é definido para o objeto controlador. Então no "tabs" função construtora do controlador, quando a função addPane é criada
this.addPane = function(pane) { ... }
ele é criado no objeto controlador, não no escopo. As visualizações não podem ver a função addPane -- elas só têm acesso às funções definidas no $scope. Em outras palavras, no HTML, isso ganhou't funciona:
<a ng-click="addPane(newPane)">won't work</a>
Depois do "tabs" a função construtor do controlador executa, temos o seguinte:
A linha preta tracejada indica herança de protótipos -- um protótipo de escopo isolado herda de Scope. (Ele não herda prototipicamente do escopo em vigor onde a diretiva foi encontrada no HTML).
Agora, a função de link da diretiva pane's quer se comunicar com a diretiva tabs (o que realmente significa que ela precisa afetar os tabs isolar $scope de alguma forma). Eventos poderiam ser utilizados, mas outro mecanismo é ter a diretiva de painel require
o controlador de tabs. (Parece não haver um mecanismo para a diretiva pane para require
o escopo $stabs).
Então, isto levanta a questão: se só temos acesso ao controlador de abas, como é que temos acesso às abas isolar $scope (que é o que realmente queremos)?
Bem, a linha pontilhada vermelha é a resposta. A função addPane() function's "scope" (I'm referindo-se ao JavaScript's function scope/closures aqui) dá à função acesso aos tabs isolate $scope. Isto é, addPane() tem acesso ao "tabs IsolateScope" no diagrama acima por causa de um fechamento que foi criado quando addPane() foi definido. (Se em vez disso definíssemos addPane() no objeto tabs $scope, a diretiva pane não teria acesso a esta função e, portanto, não teria como se comunicar com o tabs $scope).
Para responder a outra parte da sua pergunta: como funciona o $scope nos controladores?
:
Dentro das funções definidas em $scope, isso
está definido para " o $scope em efeito onde/quando a função foi chamada". Suponha que temos o seguinte 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>
E o ParentCtrl
(Solely) tem
$scope.logThisAndScope = function() {
console.log(this, $scope)
}
Clicando no primeiro link, será mostrado que "este" e "escopo" são os mesmos, pois "o escopo em vigor quando a função foi chamada" é o escopo associado com o "ParentCtrl".
Clicando no segundo link, será revelado que isto
e $scope
são **não*** o mesmo, pois "o scope em efeito quando a função foi chamada" é o scope associado com o ChildCtrl
. Então aqui, isso
está definido como ChildCtrl
's $scope
. Dentro do método, $scope
ainda é o ParentCtrl
's $scope'.
[violino][2]
Eu tento não utilizar "isto" dentro de uma função definida em $scope, pois fica confuso qual $scope está sendo afetado, especialmente considerando que ng-repeat, ng-include, ng-switch, e diretrizes podem todos criar seus próprios escopos filhos.
O motivo 'addPane' é atribuído a isto por causa da diretiva <pane>
.
A diretiva pane
requer: '^tabs'`, que coloca o objeto controlador de abas de uma diretiva pai, na função de link.
O addPane
é atribuído ao isto
para que a função de ligação pane
possa vê-lo. Então na função de link pane
, addPane
é apenas uma propriedade do controlador tabs
, e it's apenas tabsControllerObject.addPane. Então a função de ligação da diretiva pane's pode acessar o objeto controller tabs e portanto acessar o método addPane.
Espero que a minha explicação seja suficientemente clara... é... é um pouco difícil de explicar.
As versões anteriores do Angular (pré 1.0 RC) permitiam-lhe utilizar este intercambiavelmente com o método $scope, mas este já não é o caso. Dentro dos métodos definidos no escopo este e $scope são intercambiável (angular define isto para $scope), mas não de outra forma dentro do seu construtor de controladores.
Para trazer este comportamento de volta (alguém sabe porque foi mudado?) você pode acrescentar:
return angular.extend($scope, this);
no final da sua função de controlador (desde que tenha sido injectado $scope a esta função de controlador).
Isto tem um efeito agradável de ter acesso ao escopo dos pais via objeto controlador que você pode obter na criança com require: '^myParentDirective'