Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple directives [myPopup, myDraggable] asking for new/isolated scope

I wrote a directive for dialogs (myPopup) and another one for dragging this dialog (myDraggable), but I allways get the error:

Multiple directives [myPopup, myDraggable] asking for new/isolated scope

Here is a Plunker: http://plnkr.co/edit/kMQ0hK5RnVw5xOBdDq5P?p=preview

What can I do?

JS code:

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);             }         }     }; }]); 
like image 938
Martin Avatar asked Mar 10 '14 14:03

Martin


People also ask

Can we use multiple directives in AngularJS?

... is quite illustrative as AngularJS doesn't allow multiple directives (on the same DOM level) to create their own isolate scopes. According to the documentation, this restriction is imposed in order to prevent collision or unsupported configuration of the $scope objects.

What is isolate scope?

Isolated scope directive is a scope that does not inherit from the parent and exist on its own. Scenario: Lets create a very simple directive which will show the object from the parent controller.

How I use scope in a directive AngularJS?

The directive scope uses prefixes to achieve that. Using prefixes helps establish a two-way or one-way binding between parent and directive scopes, and also make calls to parent scope methods. To access any data in the parent scope requires passing the data at two places – the directive scope and the directive tag.

What is the default scope in an angular directive?

Default Scope To put it another way, the scope is passed as “reference” to the directive. All the scope data will be immediately available within directive to consume. Likewise, any change made to the scope within directive will be reflected in the invoking view immediately.


2 Answers

From docs:

Example scenarios of multiple incompatible directives applied to the same element include:

Multiple directives requesting isolated scope.

Multiple directives publishing a controller under the same name.

Multiple directives declared with the transclusion option.

Multiple directives attempting to define a template or templateURL.

Try removing isolate scope on myDraggable's directive:

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

Replace scope.enabled with attrs.enabled:

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

And modify your template to bind the enable attribute:

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

DEMO

like image 115
Khanh TO Avatar answered Sep 25 '22 17:09

Khanh TO


A DOM element is creating a collision with your attempted isolate scopes. Therefore, you should always ask yourself if an isolate scope is needed.

Consider removing the isolate scope on myDraggable, interpolating the myDraggable value (like you did with isDraggable), and accessing the attribute in the link function.

<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) {  ... 

See your updated Plunker here and notice the change in the myPopup template.

If you want to see the myDraggable attribute changes then implement something like:

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

See Angular Attribute Docs $observe function

like image 32
Stephen J Barker Avatar answered Sep 23 '22 17:09

Stephen J Barker