AngularJS - Acesso ao escopo filho

110

Se eu tiver os seguintes controladores:

function parent($scope, service) {
    $scope.a = 'foo';

    $scope.save = function() {
        service.save({
            a:  $scope.a,
            b:  $scope.b
        });
    }
}

function child($scope) {
    $scope.b = 'bar';
}

O que é a maneira correta para deixar parentler bfora child? Se for necessário definir bem parent, isso não tornaria semanticamente incorreto assumir que bé uma propriedade que descreve algo relacionado childe não parent?

Update: Pensando mais sobre isso, se mais de uma criança teve bque iria criar um conflito para parentem que bpara recuperar. Meus questão permanece, o que é a maneira correta de acesso ba partir de parent?

Aziz Alfoudari
fonte
1
Além disso, certifique-se de ler isto: stackoverflow.com/questions/14049480/… Uma visão geral muito boa e detalhada dos escopos e herança no angularJS.
Martijn

Respostas:

162

Os escopos em AngularJS usam herança prototípica. Ao pesquisar uma propriedade em um escopo filho, o interpretador pesquisará a cadeia de protótipos começando do filho e continuará até os pais até encontrar a propriedade, e não o contrário.

Verifique os comentários da Vojta sobre o problema https://groups.google.com/d/msg/angular/LDNz_TQQiNE/ygYrSvdI0A0J

Resumindo: você não pode acessar escopos filho de um escopo pai.

Suas soluções:

  1. Defina propriedades nos pais e acesse-as a partir dos filhos (leia o link acima)
  2. Use um serviço para compartilhar o estado
  3. Passe dados por meio de eventos. $emitenvia eventos para cima para os pais até o escopo raiz e $broadcastdespacha eventos para baixo. Isso pode ajudá-lo a manter as coisas semanticamente corretas.
jaime
fonte
8
Além disso, a diferença entre $ emit e $ broadcast é onde $ emit pode ser cancelado e $ broadcast não.
Azri Jamil
Um lugar em que você provavelmente deseja obter o escopo filho é durante o teste de unidade de diretivas. Se você tiver uma diretiva transcluída, o escopo é filho do escopo usado para compilar o elemento. Obter acesso ao escopo da diretiva compilada para teste é um desafio.
pmc
Obrigado. Outra opção pode ser localStoragecom uma dependência como ngStorage.
Akash de
81

Embora a resposta de jm-seja a melhor maneira de lidar com esse caso, para referência futura, é possível acessar escopos filho usando os membros $$ childHead, $$ childTail, $$ nextSibling e $$ prevSibling de um escopo. Eles não estão documentados, portanto, podem ser alterados sem aviso prévio, mas estarão lá se você realmente precisar percorrer os escopos.

// get $$childHead first and then iterate that scope's $$nextSiblings
for(var cs = scope.$$childHead; cs; cs = cs.$$nextSibling) {
    // cs is child scope
}

Violino

Jussi Kosunen
fonte
49

Você pode tentar isto:

$scope.child = {} //declare it in parent controller (scope)

em seguida, no controlador filho (escopo), adicione:

var parentScope = $scope.$parent;
parentScope.child = $scope;

Agora o pai tem acesso ao escopo do filho.

Farzin Mo
fonte
1
Muito limpo e direto. Para adicionar a isso, apenas perceba que não há vinculação, então quando você declara como acima, o parentScope.child está segurando o escopo do filho naquele momento. Para resolver isso, você pode adicionar $ scope. $ Watch (function () {parentScope.child = $ scope}); para que, após cada resumo, ele empurre o novo escopo para o pai. Na extremidade pai, se você estiver usando qualquer um dos escopos filhos para visuais no html, você precisará adicionar um relógio em sua variável filha e dizer a ele para atualizar o escopo da seguinte forma: $ scope. $ Watch ('child', function ( ) {$ scope. $ evalAsync ();}); . Espero que isso economize algum tempo!
IfTrue
2
@IfTrue Eu acredito que $ watch () não é necessário. São objetos e passam por referência.
Swanidhi
2
@Swanidhi = Eu sei o que você quer dizer e também pensei assim, mas não funcionou quando eu estava experimentando no momento em que escrevi o comentário. Pelo que vale a pena, mudei de fazer isso para emitir / transmitir.
IfTrue
1
Como fazer isso no Type Script?
ATHER
Melhor resposta para mim !! Melhor exemplo de objetos tratados como por referência em javascript
Vikas Gautam
4

Uma possível solução alternativa é injetar o controlador filho no controlador pai usando uma função init.

Implementação possível:

<div ng-controller="ParentController as parentCtrl">
   ...

    <div ng-controller="ChildController as childCtrl" 
         ng-init="ChildCtrl.init()">
       ...
    </div>
</div>

Onde ChildControllervocê tem:

app.controller('ChildController',
    ['$scope', '$rootScope', function ($scope, $rootScope) {
    this.init = function() {
         $scope.parentCtrl.childCtrl = $scope.childCtrl;
         $scope.childCtrl.test = 'aaaa';
    };

}])

Agora no ParentControllervocê pode usar:

app.controller('ParentController',
    ['$scope', '$rootScope', 'service', function ($scope, $rootScope, service) {

    this.save = function() {
        service.save({
            a:  $scope.parentCtrl.ChildCtrl.test
        });
     };

}])

Importante:
Para funcionar corretamente você tem que usar a diretiva ng-controllere renomear cada controlador usando ascomo eu fiz no html, por exemplo.

Dicas:
Use o inspetor-ng do plugin do Chrome durante o processo. Isso vai te ajudar a entender a árvore.

borracciaBlu
fonte
3

Usando $ emit e $ broadcast (conforme mencionado por walv nos comentários acima)

Para disparar um evento para cima (de filho para pai)

$scope.$emit('myTestEvent', 'Data to send');

Para disparar um evento para baixo (de pai para filho)

$scope.$broadcast('myTestEvent', {
  someProp: 'Sending you some data'
});

e finalmente ouvir

$scope.$on('myTestEvent', function (event, data) {
  console.log(data);
});

Para mais detalhes: - https://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/

Aproveitar :)

Anjana Silva
fonte
1

Sim, podemos atribuir variáveis ​​do controlador filho às variáveis ​​do controlador pai. Esta é uma maneira possível:

Visão geral: O objetivo principal do código, abaixo, é atribuir $ scope.variable do controlador filho a $ scope.assign do controlador pai

Explicação: Existem dois controladores. No html, observe que o controlador pai inclui o controlador filho. Isso significa que o controlador pai será executado antes do controlador filho. Portanto, primeiro setValue () será definido e, em seguida, o controle irá para o controlador filho. $ scope.variable será atribuído como "filho". Então, esse escopo filho será passado como um argumento para a função do controlador pai, onde $ scope.assign obterá o valor como "filho"

<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script type="text/javascript">
    var app = angular.module('myApp',[]);

    app.controller('child',function($scope){
        $scope.variable = "child";
        $scope.$parent.setValue($scope);
    });

    app.controller('parent',function($scope){
        $scope.setValue = function(childscope) {
            $scope.assign = childscope.variable;
        }
    });

</script>
<body ng-app="myApp">
 <div ng-controller="parent">
    <p>this is parent: {{assign}}</p>
    <div ng-controller="child">
        <p>this is {{variable}}</p>
    </div>
 </div>
</body>
</html>
rupali317
fonte