Angular 以 angular 方式将可拖动行为绑定到指令中的元素

Angular bind draggable behaviour to element in directive the angular way

我正在使用 Angular 和 JSPlumb,我想将 jsplumb 的可拖动行为绑定到指令中的元素(在 link 函数中使用 element)。

目前我做这样的事情 (http://jsfiddle.net/r8epahbt/):

// In the controller I define a method to get the elements
// via jquery and then make them draggable
function MyCtrl($scope, $http, $log, $timeout) {
    $scope.makeWidgetsDraggable = function() {
        // ISSUE: I have to use jQuery here, how can I do it the Angular way?
        // I only want to make the "current" element draggable (it's wasteful to do ALL .widgets)
        // How can I make only THIS element (could be passed from directive below) draggable
        jsPlumb.draggable($('#canvas .widget'), { // Do this $('#canvas .widget') - the Angular Way
            containment: "parent"
        });
    };
}

// When the value of $scope.items changes, we call scope.makeWidgetDraggable
// which will get ALL widgets and make them draggable.
// I only want to make the newly created widget draggable
myApp.directive("widgetTemplate", function($parse, $timeout) {
    //...
    link: function (scope, element, attrs) {
        // Watch the `items` for change, if so (item added)
        // make the new element(s) draggable
        scope.$watch('items', function() {
            $timeout(function() {
                // [ISSUE] - This method uses jQuery to get `this` element (and all other elements)
                // How can I do this the `angular way` - I want to make `this` element draggable 
                // (the one that is being rendered by this directive)
                scope.makeWidgetsDraggable();

                // I want to do something like this:
                // But it Gives error: TypeError: Cannot read property 'offsetLeft' of undefined
                /*jsPlumb.draggable(element, {
                    containment: "parent"
                });*/
            }); // $timeout
        }); // $watch
    },// link
    //...
}

我认为这样的事情应该可行(在指令的 link 函数中):

// Gives me error (through JSPlumb Library):
// TypeError: Cannot read property 'offsetLeft' of undefined
jsPlumb.draggable(element, {
    containment: "parent"
});

我制作了一个可用的 JSFiddle,如果有人能看一下,我将不胜感激。

http://jsfiddle.net/r8epahbt/


基本上,我想找到一种更好的方法来完成第 11 行(在 fiddle 中)

// I want to remove this jquery selector
jsPlumb.draggable($('#canvas .widget'),...)

// and do it the `Angular Way`
jsPlumb.draggable(element, ...)
// Doesn't work, gives me error:
// TypeError: Cannot read property 'offsetLeft' of undefined

您可以将 $scope.makeWidgetsDraggable 函数移动到指令的 link 的一部分,并使控制器不再耦合到指令,因为您已经通过指令的范围传递了项目=:

指令

myApp.directive("widgetTemplate", function ($parse, $timeout) {
    return {
        restrict: "E",
        templateUrl: "widgetTemplate",
        replace: true,
        scope: {
            items: "="
        },
        link: function (scope, element, attrs) {

            scope.makeWidgetsDraggable = function () {
                // ISSUE: I have to use jQuery here, how can I do it the Angular way?
                // I only want to make the "current" element draggable (it's wasteful to do ALL .widgets)
                // How can I make only THIS element (could be passed from directive line 46) draggable
                jsPlumb.draggable(element, { // Do this $('#canvas .widget') - the Angular Way
                    containment: "parent"
                });
            };


            // Watch the `items` for change, if so (item added), make the new element(s) draggable
            scope.$watch('items', function () {
                $timeout(function () {
                    // [ISSUE] - This method uses jQuery to get `this` element
                    // How can I do this the `angular way` - I want to make `this` element draggable 
                    // (the one that is being rendered by this directive)
                    scope.makeWidgetsDraggable();

                    // I want to do something like this:
                    // But it Gives error: TypeError: Cannot read property 'offsetLeft' of undefined
                    /*jsPlumb.draggable(element, {
                        containment: "parent"
                    });*/
                }); // $timeout
            }); // $watch
        }, // link
    }
});

这是你的 fiddle 变化:http://jsfiddle.net/r8epahbt/1/

编辑

好像我之前发布了一点,但解决方案并不是 100% 有效,但我留下了这个答案,以防它可以帮助处于类似情况的其他人。

在看到 element 如何指向 DOM comment 之后,我的第二个建议是将单个项目传递给指令,并且每个项目有 1 个指令实例。这样您就不必查看项目是否有任何更改,也不需要任何超时:

视图改变:

<div id="canvas" class="canvas">
    <div class="absolute widget" ng-repeat="item in items" id="widget{{$index}}" data-wId="{{$index}}">
        <widget-template item="item"></widget-template>
    </div>
</div>       

请注意,我删除了 <span ng-init="fakeAjaxCall"></span> 并将其移至控制器。

指令

myApp.directive("widgetTemplate", function ($parse, $timeout) {
    return {
        restrict: "E",
        templateUrl: "widgetTemplate",
        replace: true,
        scope: {
            item: "="
        },
        link: function (scope, element, attrs) {
            jsPlumb.draggable(element.parent(), { 
                containment: "parent"
            }); 

            //no need to watch for items added since an instance of the directive is used for each element.

        }, // link
    }
});

终于更新了 jsfiddle,这次我再次确保它可以正常工作。 http://jsfiddle.net/r8epahbt/10/