web-dev-qa-db-fra.com

Confus sur Angularjs transcluded et isoler les liaisons d'étendues

J'ai du mal à comprendre la portée des modèles et leurs liaisons par rapport aux directives qui ont une portée limitée.

Je comprends que restreindre la portée d'une directive signifie que le contrôleur. $ Scope et directive.scope ne sont plus la même chose. Cependant, je suis confus quant à la façon dont le placement des modèles dans le modèle de directive ou dans le HTML affecte la liaison de données. Je sens que je manque quelque chose de très fondamental et pour avancer, je dois comprendre cela.

Prenez le code suivant (violon ici: http://jsfiddle.net/2ams6/ )

Javascript

var app = angular.module('app',[]);
app.controller('Ctrl',function($scope){
});
app.directive('testel', function(){
    return {
        restrict: 'E',
        scope: {
            title: '@'
        },
        transclude: true,
        template:   '<div ng-transclude>'+
                    '<h3>Template title: {{title}}</h3>' +
                    '<h3>Template data.title:{{data.title}}</h3>' +
                    '</div>'
    }    
}); 

HTML

<div ng-app='app'>
    <div ng-controller="Ctrl">
        <input ng-model="data.title">
        <testel title="{{data.title}}">
            <h3>Transclude title:{{title}}</span></h3>
            <h3>Transclude data.title:{{data.title}}</h3>
        </testel>
    </div>
</div>

Le modèle ne met à jour que {{title}} dans le modèle et {{data.title}} dans la transclusion. Pourquoi pas {{title}} dans la transclusion ni {{data.title}} dans le modèle?

Déplacer l'entrée à l'intérieur de la transclusion comme ça (violon ici: http://jsfiddle.net/eV8q8/1/ ):

<div ng-controller="Ctrl">
    <testel title="{{data.title}}">
        <input ng-model="data.title">
         <h3>Transclude title: <span style="color:red">{{title}}</span></h3>

         <h3>Transclude data.title: <span style="color:red">{{data.title}}</span></h3>

    </testel>
</div>

signifie maintenant seulement transclure {{data:title}} est mis à jour. Pourquoi pas l'un des modèles {{title}} ou {{data.title}}, ni transclude {{title}}?

Et enfin, déplacer l'entrée dans le modèle, comme ça (violon ici: http://jsfiddle.net/4ngmf/2/ ):

template: '<div ng-transclude>' +
            '<input ng-model="data.title" />' +
            '<h3>Template title: {{title}}</h3>' +
            '<h3>Template data.title: {{data.title}}</h3>' +
            '</div>'

Désormais, seul le modèle {{data.title}} est mis à jour. Encore une fois, pourquoi pas les 3 autres fixations?

J'espère qu'il y a quelque chose d'évident qui me regarde en face et ça me manque. Si vous me demandez de l'obtenir, je vous achèterai une bière ou vous donnerai des points ou quelque chose de ce genre. Merci beaucoup.

55
dewd

Vos violons créent trois portées:

  1. une portée associée au contrôleur Ctrl, à cause de ng-controller
  2. une directive a exclu la portée, à cause de transclude: true
  3. une directive isoler la portée, à cause de scope: { ... }

Dans fiddle1, avant de taper quoi que ce soit dans la zone de texte, nous avons ce qui suit:

enter image description here

La portée 003 est la portée associée au contrôleur. Comme nous n'avons pas encore tapé dans la zone de texte, il n'y a pas de propriété data. Dans la portée isolée 004, nous voyons qu'une propriété title a été créée, mais elle est vide. Il est vide car la portée parent n'a pas encore de propriété data.title.

Après avoir tapé my title Dans la zone de texte, nous avons maintenant:

enter image description here

La portée du contrôleur 003 a désormais une nouvelle propriété d'objet data (c'est pourquoi il est coloré en jaune), qui a une propriété title désormais définie sur my title. Étant donné que la propriété de portée d'isolat title est un lien de données à sens unique avec la valeur interpolée de data.title, Elle obtient également la valeur my title (La valeur est colorée en jaune car elle a changé).

La portée transclue hérite de manière prototypique de la portée du contrôleur, donc à l'intérieur du HTML transclu, angular peut suivre la chaîne du prototype et trouver $scope.data.title Dans la portée parent (mais $scope.title n'existe pas là-bas).

La portée isolate n'a accès qu'à ses propres propriétés, donc uniquement la propriété title.

Dans fiddle2, avant de taper, nous avons la même image que dans fiddle1.

Après avoir tapé my title:

enter image description here

Remarquez où la nouvelle propriété data.title Est apparue - sur la portée transclue. La portée isolée recherche toujours data.title Sur la portée du contrôleur, mais elle n'est pas là cette fois, donc sa valeur de propriété title reste vide.

Dans fiddle3, avant de taper, nous avons la même image que dans fiddle1.

Après avoir tapé my title:

enter image description here

Remarquez où la nouvelle propriété data.title Est apparue - sur la portée de l'isolat. Aucune des autres étendues n'a accès à la portée isolate, donc la chaîne my title N'apparaîtra nulle part ailleurs.


Mise à jour pour Angular v1.2:

Avec le changement eed299a Angular efface maintenant le point de transclusion avant la transclusion, donc les parties Template title: ... Et Template data.title: ... N'apparaîtront que si vous modifiez le modèle de telle sorte que ng-transclude soit par lui-même, tel que:

'<h3>Template title: <span style="color:red">{{title}}</span></h3>' +
'<h3>Template data.title: <span style="color:red">{{data.title}}</span></h3>' +
'<div ng-transclude></div>'

Dans la mise à jour ci-dessous pour Angular v1.3, cette modification de modèle a été effectuée.


Mise à jour pour Angular v1.3 +:

Depuis Angular v1.3, la portée transclude est maintenant un enfant de la portée isolate de la directive, plutôt qu'un enfant de la portée du contrôleur. Donc dans fiddle1, avant de taper quoi que ce soit:

enter image description here

Les images de cette mise à jour sont dessinées avec l'outil Péri $ scope , donc les images sont un peu différentes. Le @ Indique que nous avons une propriété de portée isolée qui utilise la syntaxe @, Et le fond rose signifie que l'outil n'a pas pu trouver une référence d'ancêtre pour le mappage (ce qui est vrai, puisque nous n'a encore rien saisi dans la zone de texte).

Après avoir tapé my title Dans la zone de texte, nous avons maintenant:

enter image description here

Les propriétés isolées qui utilisent la liaison @ Afficheront toujours le résultat de la chaîne interpolée dans la portée d'isolat après le symbole @. Péri $ scope a également pu trouver cette valeur de chaîne exacte dans une étendue d'ancêtre, il affiche donc également une référence à cette propriété.

Dans fiddle 2, avant de taper, nous avons la même image que dans fiddle1.

Après avoir tapé my title:

enter image description here

Remarquez où la nouvelle propriété data.title Est apparue - sur la portée transclue. La portée isolée recherche toujours data.title Sur la portée du contrôleur, mais elle n'est pas là cette fois, donc sa valeur de propriété title reste vide.

Dans fiddle3, avant de taper, nous avons la même image que dans fiddle1.

Après avoir tapé my title:

enter image description here

Remarquez où la nouvelle propriété data.title Est apparue - sur la portée de l'isolat. Même si la portée transclue a accès à la portée isolée via la relation $parent, Elle n'y cherchera pas title ou data.title - elle ne cherchera que dans le contrôleur portée (c'est-à-dire qu'elle suivra l'héritage prototypique), et la portée du contrôleur n'a pas ces propriétés définies.

113
Mark Rajcok

Après avoir lu toutes les réponses présentées, y compris les schémas fantastiques de Mark, voici ma compréhension de la portée et de l'héritage selon ma question. J'apprécierais des commentaires sur l'endroit où ce diagramme tombe, afin que je puisse mettre à jour de manière appropriée. J'espère que cela fournit simplement une vision différente de ce que Mark a présenté:

Scope inheritance

22
dewd

Bien demandé, btw! J'espère que ma réponse est aussi éloquente ..

La réponse a à voir avec la façon dont les éléments transclus obtiennent leur portée.

Pour résumer, vous avez deux portées:

  1. L'étendue du contrôleur, qui a $scope.data.title. (Implicitement ajouté par votre élément input)
  2. Le champ d'application de la directive, qui a $scope.title.

Lorsque vous modifiez le contrôleur $scope.data.title, directive $scope.title change également.

Vous avez également deux sections de HTML, la transclude et le modèle. Ce qui se passe, c'est que le HTML transclus est dans la portée du contrôleur, et le HTML du modèle est dans la portée de la directive. Ainsi, le HTML transclu ne sait rien de title, et la portée du modèle ne sait rien de data.title

C'est en fait exactement ce à quoi Transclusion était destiné - pour permettre aux éléments enfants d'une directive de conserver leur portée parent, dans ce cas, la portée du contrôleur. De par leur conception, les éléments transclus ne savent pas qu'ils se trouvent dans une directive et n'ont donc pas accès à la portée de la directive.

Les modèles de directives, en revanche, n'auront accès qu'au champ d'application de la directive.

J'ai un peu changé votre code pour rendre les noms un peu plus clairs (même fonctionnalité, cependant)

http://jsfiddle.net/yWWVs/2/

8
Roy Truelove