Transclusão e escopos em angular:Por que isto não está funcionando?
-
21-12-2019 - |
Pergunta
Eu tenho uma configuração muito simples:
<pane title="mytitle">Title in parent (transcluded): {{title}}</pane>
e
angular.module('transclude', [])
.directive('pane', function(){
return {
restrict: 'E',
transclude: true,
scope: { title:'@' },
template: '<div>' +
'<div>Title in isolated scope: {{title}}</div>' +
'<div ng-transclude></div>' +
'</div>'
};
});
O plunker está aqui: http://plnkr.co/edit/yRHcNGjQAq1NHDTuwXku
A transclusão em si está funcionando, mas o {{title}}
só é substituído no modelo da diretiva.
O {{title}}
dentro do elemento transcluído, entretanto, permanece vazio, mesmo que a diretiva tenha uma variável title
em seu escopo.Por que é que?
Solução
O escopo do elemento transcluído não é um escopo filho da diretiva, mas um escopo irmão.Isto é o que a documentação diz:
Em uma configuração típica, o widget cria um escopo isolado, mas a transclusão não é filha, mas sim irmã do escopo isolado.
A solução mais simples neste caso, como você pode acessar o escopo transcutido, é assim:
.directive('pane', function () {
return {
restrict: 'E',
transclude: true,
scope: {
title: '@'
},
template:
'<div>' +
'<div>Title in isolated scope: {{title}}</div>' +
'<div ng-transclude></div>' +
'</div>',
link: function (scope, element, attrs) {
scope.$$nextSibling.title = attrs.title;
}
};
});
Demonstração: http://plnkr.co/edit/ouq9B4F2qFPh557708Q1?p=preview
Outras dicas
@dfsq está correto sobre:
O escopo do elemento transludido não é um escopo infantil da diretiva, mas um irmão
Gostaria de adicionar mais comentários sobre por que esse é o comportamento esperado dos angularJs.Como diz a documentação:
Em uma configuração típica, o widget cria um escopo de isolado, mas a transclusão não é criança, mas um irmão do escopo de isolado.Isso possibilita que o widget tenha estado privado e a transclusão esteja ligada ao escopo dos pais (pré-isolado).
O conteúdo transcluído dentro da diretiva é arbitrário, não deveria temos conhecimento do âmbito de aplicação isolado da directiva, caso contrário, criaríamos uma código de acoplamento apertado.Porque para que o conteúdo transcluído funcione, o conteúdo deve conhecer os detalhes de implementação da sua diretiva (o que está disponível para uso).
Se você decidir que o conteúdo pertence à directiva.Você tem 2 opções:
1) Torne o conteúdo parte do modelo
angular.module('transclude', [])
.directive('pane', function(){
return {
restrict: 'E',
transclude: true,
scope: { title:'@' },
template: '<div>' +
'<div>Title in isolated scope: {{title}}</div>' +
'<div>' +
' Title in parent (transcluded): {{title}} ' +
' </div>' +
'</div>'
};
});
2) Use a trancclusão personalizada para vincular o escopo você mesmo:
angular.module('transclude', [])
.directive('pane', function(){
return {
restrict: 'E',
transclude: true,
scope: { title:'@' },
template: '<div>' +
'<div>Title in isolated scope: {{title}}</div>' +
'<div class="transclude"></div>' +
'</div>',
link: function (scope, element, attr,controller, linker) {
linker(scope, function(clone){
element.find(".transclude").append(clone); // add to DOM
});
}
};
});