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?


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.

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


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.


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.


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 :)


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>
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.