web-dev-qa-db-fra.com

Directives multiples [myPopup, myDraggable] demandant une étendue nouvelle / isolée

J'ai écrit une directive pour les dialogues (myPopup) et une autre pour faire glisser ce dialogue (myDraggable), mais j'obtiens toujours l'erreur:

Directives multiples [myPopup, myDraggable] demandant une étendue nouvelle/isolée

Voici un Plunker: http://plnkr.co/edit/kMQ0hK5RnVw5xOBdDq5P?p=preview

Que puis-je faire?

Code JS:

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

function myController($scope) {
    $scope.isDraggable = true;
}


app.directive('myPopup', [
    function () {
        "use strict";

        return {
            restrict: 'E',
            replace: true,
            transclude: true,
            template: '<div my-draggable="draggable"class="dialog"><div class="title">{{title}}</div><div class="content" ng-transclude></div></div>',
            scope: {
                title: '@?dialogTitle',
                draggable: '@?isDraggable',
                width: '@?width',
                height: '@?height',
            },
            controller: function ($scope) {
                // Some code
            },
            link: function (scope, element, attr) {
                if (scope.width) {
                    element.css('width', scope.width);
                }

                if (scope.height) {
                    element.css('height', scope.height);
                }                    
            }
        };
    }
]);

app.directive('myDraggable', ['$document',
    function ($document) {
    return {
        restrict: 'A',
        replace: false,
        scope: { enabled: '=myDraggable' },

        link: function (scope, Elm, attrs) {
            var startX, startY, initialMouseX, initialMouseY;

            if (scope.enabled === true) {
                Elm.bind('mousedown', function ($event) {
                    startX = Elm.prop('offsetLeft');
                    startY = Elm.prop('offsetTop');
                    initialMouseX = $event.clientX;
                    initialMouseY = $event.clientY;
                    $document.bind('mousemove', mousemove);
                    $document.bind('mouseup', mouseup);
                    $event.preventDefault();
                });
            }

            function getMaxPos() {
                var computetStyle = getComputedStyle(Elm[0], null);
                var tx, ty;
                var transformOrigin =
                    computetStyle.transformOrigin ||
                    computetStyle.webkitTransformOrigin ||
                    computetStyle.MozTransformOrigin ||
                    computetStyle.msTransformOrigin ||
                    computetStyle.OTransformOrigin;
                tx = Math.ceil(parseFloat(transformOrigin));
                ty = Math.ceil(parseFloat(transformOrigin.split(" ")[1]));
                return {
                    max: {
                        x: tx + window.innerWidth - Elm.prop('offsetWidth'),
                        y: ty + window.innerHeight - Elm.prop('offsetHeight')
                    },
                    min: {
                        x: tx,
                        y: ty
                    }
                };
            }

            function mousemove($event) {
                var x = startX + $event.clientX - initialMouseX;
                var y = startY + $event.clientY - initialMouseY;
                var limit = getMaxPos();
                x = (x < limit.max.x) ? ((x > limit.min.x) ? x : limit.min.x) : limit.max.x;
                y = (y < limit.max.y) ? ((y > limit.min.y) ? y : limit.min.y) : limit.max.y;
                Elm.css({
                    top: y + 'px',
                    left: x + 'px'
                });
                $event.preventDefault();
            }

            function mouseup() {
                $document.unbind('mousemove', mousemove);
                $document.unbind('mouseup', mouseup);
            }
        }
    };
}]);
51
Martin

De docs :

Exemples de scénarios de plusieurs directives incompatibles appliquées au même élément:

Plusieurs directives demandant une étendue isolée .

Plusieurs directives publiant un contrôleur sous le même nom.

Plusieurs directives déclarées avec l'option de transclusion.

Plusieurs directives tentant de définir un modèle ou un modèleURL.

Essayez de supprimer isolate scope sur la directive myDraggable:

app.directive('myDraggable', ['$document',
    function ($document) {
    return {
        restrict: 'A',
        replace: false,
        scope: { enabled: '=myDraggable' }, //remove this line

Remplacer scope.enabled avec attrs.enabled:

if (attrs.enabled == "true") {

Et modifiez votre modèle pour lier l'attribut enable:

<div my-draggable="draggable" enabled="{{draggable}}"

DÉMO

62
Khanh TO

Un élément DOM crée une collision avec vos étendues d'isolation essayées. Par conséquent, vous devriez toujours vous demander si une portée isolée est nécessaire.

Pensez à supprimer la portée d'isolement sur myDraggable, à interpoler la valeur myDraggable (comme vous l'avez fait avec isDraggable) et à accéder à l'attribut dans la fonction link.

<div class="draggable" my-draggable="{{isDraggable}}">I am draggable {{isDraggable}}</div>
...

replace: false,

link: function (scope, Elm, attrs) {
  var startX, startY, initialMouseX, initialMouseY,
      enabled = attrs.myDraggable === 'true';
  if (enabled === true) {

...

Voir votre Plunker mis à jour ici et notez le changement dans le modèle myPopup.

Si vous voulez voir les changements d'attributs myDraggable, implémentez quelque chose comme:

attrs.$observe('myDraggable', function(iVal) {
  enabled = iVal === 'true';
  // AND/OR
  if (iVal === 'true') doSomething();
});

Voir Documents d'attributs angulaires $ observer la fonction

22
Stephen J Barker

mon erreur était similaire:

Erreur: [$ compile: multidir] Directives multiples [groupes, groupes] demandant pour une nouvelle portée/une portée isolée sur:

Dans mon cas, j'avais une double déclaration de

 .component('groups', new GroupsComponent()); 

dans le fichier app.js/app.ts

et en même temps sur le composant lui-même

const groups = angular.module('groups', ['toaster'])
.component('groups', new GroupsComponent());

Le supprimer de app.js/app.ts a résolu le problème.

7
d689p

J'ai couru dans une situation similaire. Si cela ne gâche pas votre mise en page et que vous devez absolument avoir une portée isolée sur les deux directives, ma suggestion serait de supprimer la propriété replace: true de la définition de la directive myPopup.

4
Mihai Popescu

Il existe un moyen de contourner le problème.

Vous n'isolerez pas la portée de la directive, nous créerons une nouvelle portée isolée à la place de $ new method . Cette méthode crée une nouvelle portée enfant. Si vous utilisez true au premier paramètre, elle créera une portée isolée:

Si la valeur est true, la portée n'hérite pas de manière prototypique de la portée parent. La portée est isolée, car elle ne peut pas voir les propriétés parent> scope. Lors de la création de widgets, il est utile que le widget ne lise pas accidentellement l'état parent.

Mais ce n’est pas un problème car nous avons accès à une portée privée par la fonction de liaison de directive; il est donc possible de travailler en parallèle avec une portée "parent" et isolée dans un comportement très proche d’une directive avec une portée isolée.

Voir l'exemple ci-dessous:

app.directive('myDraggable', ['$document',
    function ($document) {
    return {
        restrict: 'A',
        replace: false,
        scope: false,
        //scope: { enabled: '=myDraggable', oneWayAttr: "@" }, //Just for reference I introduced a new 
        link: function(parentScope, elem, attr) {
        var scope = parentScope.$new(true); //Simulated isolation.
            scope.oneWayAttr = attr.oneWayAttr; //one-way binding @
            scope.myDraggable = parentScope.$parent.$eval(attr.myDraggable);

            scope.watchmyDraggable = function () {
                    return scope.myDraggable = parentScope.$parent.$eval(attr.myDraggable); //parent -> isolatedscope
            };          
            scope.$watch(scope.watchmyDraggable, function(newValue, oldValue) {
             //(...)
            });

            parentScope.innerScope = scope; //If you need view access, you must create a kind of symbolic link to it.

        //(...)
        }

J'ai développé ce travail autour d'une directive de validation, cela fonctionne très bien.

4
LeonanCarvalho

J'ai inclus mon fichier de directive js à deux reprises lorsque j'ai compressé mon application. Cela a causé l'erreur.

1
Gerfried

Laissez la portée: {enabled: '= myDraggable'} de votre directive "myDraggable", vous n'en avez pas besoin. Alors:

  return {
    restrict: 'A',
    replace: false,
    link: function (scope, Elm, attrs) {
0
mhtsbt