web-dev-qa-db-fra.com

Accéder à la portée parent dans la directive transclue

Je voudrais accéder à la portée d'une directive parent, mais je n'arrive pas à obtenir la bonne combinaison de paramètres. Est-ce possible et est-ce la bonne approche?

Je veux vraiment éviter de mettre quelque chose comme SOME_CONST (qui m'aiderait à faire des mises à jour DOM via le flux de contrôle) dans MyCtrl

<div ng-controller="MyCtrl">
    <parent>
        <child></child>
    </parent>
</div>

var myApp = angular.module('myApp',[]);

function MyCtrl($scope) {
    $scope.obj = {prop:'foo'};
}

myApp.directive('parent', function() {
    return {
        scope: true,
        transclude: true,
        restrict: 'EA',
        template: '<div ng-transclude><h1>I\'m parent {{obj.prop}}<h1></div>',
        link: function(scope, elem, attrs) {
            scope.SOME_CONST = 'someConst';
        }
    }
});

myApp.directive('child', function() {
    return {
        restrict: 'EA',
        template: '<h1>I\'m child.... I want to access my parent\'s stuff, but I can\'t.  I can access MyCtrlScope though, see <b>{{obj.prop}}</b></h1> how can I access the <b>SOME_CONST</b> value in my parent\'s link function?  is this even a good idea? {{SOME_CONST}}.  I really don\'t want to put everything inside the MyCtrl',
    }
});

Veuillez voir ceci ( violon

Merci

29
binarygiant

Avec transclude: true et scope: true, la directive parent crée deux nouvelles étendues: enter image description here

La portée 004 est le résultat de scope: true, et la portée 005 est le résultat de transclude: true. Étant donné que la directive child ne crée pas de nouvelle étendue, elle utilise l'étendue transclue 005. Comme vous pouvez le voir sur le diagramme, il n'y a pas de chemin de l'étendue 005 à l'étendue 004 (sauf via la propriété privée $$ prevSibling, qui va dans la direction opposée de $$ nextSibling - mais ne les utilisez pas.)

La solution de @ joakimbl est probablement la meilleure ici, bien que je pense qu'il est plus courant de définir une API sur le contrôleur de la directive parent, plutôt que de définir des propriétés sur this:

controller: function($scope) {
    $scope.SOME_CONST = 'someConst';
    this.getConst = function() {
       return $scope.SOME_CONST;
    }
}

Puis dans la directive child:

link:function(scope,element,attrs,parentCtrl){
    scope.SOME_CONST = parentCtrl.getConst();
},

C'est ainsi que les directives tabs et pane fonctionnent sur la page d'accueil d'Angular (exemple "Créer des composants").

42
Mark Rajcok

Normalement, la façon dont vous accédez à une variable de portée parent dans une directive se fait via une liaison bidirectionnelle (scope:{model:'=model'} - voir le angular sur les directives ) dans la configuration des directives), mais puisque vous utilisez la transclusion, ce n'est pas si simple. Si la directive enfant sera toujours un enfant de la directive parent, vous pouvez toutefois la configurer pour exiger le parent, puis accéder au contrôleur parent dans la fonction de liaison enfant:

myApp.directive('parent', function() {
  return {
    scope: true,
    transclude: true,
    restrict: 'EA',
    template: '<div ng-transclude><h1>I\'m parent {{obj.prop}}<h1></div>',
    controller: function($scope) {
        $scope.SOME_CONST = 'someConst';
        this.SOME_CONST = $scope.SOME_CONST;
    }
  }
});

myApp.directive('child', function() {
  return {
    restrict: 'EA',
    require:'^parent',
    scope:true,
    link:function(scope,element,attrs,parentCtrl){
        scope.SOME_CONST = parentCtrl.SOME_CONST;
    },
    template: '<h1>I\'m child.... I want to access my parent\'s stuff, but I can\'t.  I can access MyCtrlScope though, see <b>{{obj.prop}}</b></h1> how can I access the <b>SOME_CONST</b> value in my parent\'s link function?  is this even a good idea? {{SOME_CONST}}.  I really don\'t want to put everything inside the MyCtrl',
  }
});

Voir cette mise à jour: http://jsfiddle.net/uN2uv/

9
joakimbl

J'ai juste eu le même problème et je l'ai finalement résolu avec le angular;)

En bref: vous devez utiliser une directive contrôleur dans votre parent et exiger ce contrôleur dans votre enfant directive. De cette façon, vous pouvez obtenir vos propriétés parentales.

Voir https://docs.angularjs.org/guide/directive Chapitre: Création de directives qui communiquent

J'ai changé votre violon pour utiliser un contrôleur, maintenant vous pouvez accéder à votre constante: https://jsfiddle.net/bbrqdmt3/1/

var myApp = angular.module('myApp',[]);

function MyCtrl($scope) {
    $scope.obj = {prop:'foo'};
}

myApp.directive('parent', function() {
    return {
        scope: true,
        transclude: true,
        restrict: 'EA',
        template: '<div ng-transclude><h1>I\'m parent {{obj.prop}}<h1></div>',
        controller: function($scope) {
            this.getConst= function() {
                return 'someConst';
            }                        
        },
    }
});

myApp.directive('child', function() {
    return {
        restrict: 'EA',
        require : '^parent',
        link: function(scope, element, attrs, ctrl) {
            scope.value= ctrl.getConst();
        },
        template: '<h1>I\'m child.... I want to access my parent\'s stuff, but I can\'t.  I can access MyCtrlScope though, see <b>{{obj.prop}}</b></h1> how can I access the <b>SOME_CONST</b> value in my parent\'s link function?  is this even a good idea? {{value}}.  I really don\'t want to put everything inside the MyCtrl',
    }
});
6
firebean

Il y a une transclude fn dans les arguments du lien fn après le contrôleur.

myApp.directive('parent', function() {
  return {
    scope: true,
    transclude: true,
    restrict: 'EA',
    template: '<div><h1>I'm a parent header.</h1></div>',
    link: function (scope, el, attrs, ctrl, transclude) {

        transclude(scope, function (clone, scope) {
            element.append(clone); // <-- will transclude it's own scope
        });

    },
    controller: function($scope) {
        $scope.parent = {
            binding: 'I\'m a parent binding'
        };
    }
  }
});

myApp.directive('child', function() {
  return {
    restrict: 'EA',
    require:'^parent',
    scope:true,
    link:function(scope,element,attrs,parentCtrl){

    },
    template: '<div>{{parent.binding}}</div>' // <-- has access to parent's scope
  }
});
0
cameronroe