如何仅使用角度指令将放置元素拖放到日历中

Posted

技术标签:

【中文标题】如何仅使用角度指令将放置元素拖放到日历中【英文标题】:How to drag&drop elements onto a calendar with angular directives only 【发布时间】:2014-04-17 14:19:35 【问题描述】:

我正在尝试使用角度指令实现拖放日历。日历使用 ui-calendar (https://github.com/angular-ui/ui-calendar),它是 Arshaw FullCalendar 的完整 AngularJS 指令。

将元素拖放到日历上由 angular-dragdrop (https://github.com/codef0rmer/angular-dragdrop) 提供支持。

这里是文件夹 demo/ui-calendar/demo 中的my try,但是当我将元素放到日历上时,不会触发任何事件...

html如下:

    <html lang="en" ng-app="calendarDemoApp" id="top" class="ng-scope">
<head>
    <style type="text/css">@charset "UTF-8";[ng\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hidedisplay:none !important;ng\:formdisplay:block;.ng-animate-block-transitionstransition:0s all!important;-webkit-transition:0s all!important;</style>
    <link rel="stylesheet" href="bootstrap.css">
    <link rel="stylesheet" href="fullcalendar.css">
    <link rel="stylesheet" href="calendarDemo.css">

    <script src="jquery.js"></script>
    <script src="jquery-ui.js"></script>
    <script src="angular.js"></script>
    <script src="https://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.9.0.js"></script>
    <script src="fullcalendar.js"></script>
    <script src="calendar.js"></script>

    <script>
      $.fn.ngattr = function(name, value) 
        var element = angular.element(this).get(0);
        return element.getAttribute(name) || element.getAttribute('data-' + name);
      ;
    </script>

</head>
    <body>
        <div ng-controller="dragdropController">

            <ul>
                <li class="btn btn-primary" 
                    ng-repeat='item in list1' 
                    ng-show="item.title" 
                    data-drag="true" 
                    data-jqyoui-options="revert: 'invalid', helper: 'clone'" 
                    ng-model="list1" 
                    jqyoui-draggable="index: $index, animate: true, placeholder: 'keep'">item.title</li>
            </ul>

            <div class="alert-success calAlert" ng-show="alertMessage != undefined && alertMessage != ''">
            <h4>alertMessage</h4>
            </div>

            <div ng-controller="CalendarCtrl" select="renderCalender(myCalendar1);">
                <div class="btn-toolbar">
                    <div class="btn-group">
                        <button type="button" class="btn btn-primary" ng-click="addEvent()">Add Event</button>
                    </div>
                </div>
                <div data-drop="true"
                     jqyoui-droppable="multiple:true" 
                     id="calendar" 
                     class="calendar" 
                     ng-model="eventSources" 
                     calendar="myCalendar1" 
                     config="uiConfig.calendar"
                     ui-calendar="uiConfig.calendar">
                </div>
            </div>

        </div>
    </body>
</html>

为了简单起见,我将所有指令、控制器和服务合并到了下面的 js 文件中:

function CalendarCtrl($scope) 
    /* event source that contains custom events on the scope */
    $scope.events = [
      title: 'All Day Event',start: new Date()
    ];

    /* add custom event*/
    $scope.addEvent = function() 
      $scope.events.push(
        title: 'Open Sesame',
        start: new Date(),
        className: ['openSesame']
      );
    ;

   $scope.drop = function(date, allDay)
        $scope.alertMessage = ('Event Droped on ' + date);
    ;

    /* config object */
    $scope.uiConfig = 
      calendar:
        height: 450,
        editable: true,
        header:
          left: 'title',
          center: '',
          right: 'today prev,next'
        ,
        drop: $scope.drop
      
    ;
    /* event sources array*/
    $scope.eventSources = [$scope.events];



angular.module('calendarDemoApp', [])

.constant('uiCalendarConfig', )

.controller('dragdropController', ['$scope','$timeout', function($scope, $timeout) 
    var addEvent = function (title, length) 
        length = length.length == 0 ? "0" : length;
        title = title.length == 0 ? "Untitled Event (" + length + " min)" : title + " (" + length + " min)";
        $scope.list1.push('title': title, 'length': length);

    

    $('#event_add').unbind('click').click(function () 
        var title = $('#event_title').val();
        var length = $('#event_length').val();
        addEvent(title, length);
    );

    $scope.list1 = [
        title: 'Full check up', length: '25',
        title: 'Whitening', length: '90',
        title: 'Filling', length: '30'];
])

.controller('uiCalendarCtrl', ['$scope', '$timeout', function($scope, $timeout)

      var sourceSerialId = 1,
          eventSerialId = 1,
          sources = $scope.eventSources,
          extraEventSignature = $scope.calendarWatchEvent ? $scope.calendarWatchEvent : angular.noop,

          wrapFunctionWithScopeApply = function(functionToWrap)
              var wrapper;

              if (functionToWrap)
                  wrapper = function()
                      // This happens outside of angular context so we need to wrap it in a timeout which has an implied apply.
                      // In this way the function will be safely executed on the next digest.

                      var args = arguments;
                      $timeout(function()
                          functionToWrap.apply(this, args);
                      );
                  ;
              

              return wrapper;
          ;

      this.eventsFingerprint = function(e) 
        if (!e.__uiCalId) 
          e.__uiCalId = eventSerialId++;
        
        // This extracts all the information we need from the event. http://jsperf.com/angular-calendar-events-fingerprint/3
        return "" + e.__uiCalId + (e.id || '') + (e.title || '') + (e.url || '') + (+e.start || '') + (+e.end || '') +
          (e.allDay || '') + (e.className || '') + extraEventSignature(e) || '';
      ;

      this.sourcesFingerprint = function(source) 
          return source.__id || (source.__id = sourceSerialId++);
      ;

      this.allEvents = function() 
        // return sources.flatten(); but we don't have flatten
        var arraySources = [];
        for (var i = 0, srcLen = sources.length; i < srcLen; i++) 
          var source = sources[i];
          if (angular.isArray(source)) 
            // event source as array
            arraySources.push(source);
           else if(angular.isObject(source) && angular.isArray(source.events))
            // event source as object, ie extended form
            var extEvent = ;
            for(var key in source)
              if(key !== '_uiCalId' && key !== 'events')
                 extEvent[key] = source[key];
              
            
            for(var eI = 0;eI < source.events.length;eI++)
              angular.extend(source.events[eI],extEvent);
            
            arraySources.push(source.events);
          
        

        return Array.prototype.concat.apply([], arraySources);
      ;

      // Track changes in array by assigning id tokens to each element and watching the scope for changes in those tokens
      // arguments:
      //  arraySource array of function that returns array of objects to watch
      //  tokenFn function(object) that returns the token for a given object
      this.changeWatcher = function(arraySource, tokenFn) 
        var self;
        var getTokens = function() 
          var array = angular.isFunction(arraySource) ? arraySource() : arraySource;
          var result = [], token, el;
          for (var i = 0, n = array.length; i < n; i++) 
            el = array[i];
            token = tokenFn(el);
            map[token] = el;
            result.push(token);
          
          return result;
        ;
        // returns elements in that are in a but not in b
        // subtractAsSets([4, 5, 6], [4, 5, 7]) => [6]
        var subtractAsSets = function(a, b) 
          var result = [], inB = , i, n;
          for (i = 0, n = b.length; i < n; i++) 
            inB[b[i]] = true;
          
          for (i = 0, n = a.length; i < n; i++) 
            if (!inB[a[i]]) 
              result.push(a[i]);
            
          
          return result;
        ;

        // Map objects to tokens and vice-versa
        var map = ;

        var applyChanges = function(newTokens, oldTokens) 
          var i, n, el, token;
          var replacedTokens = ;
          var removedTokens = subtractAsSets(oldTokens, newTokens);
          for (i = 0, n = removedTokens.length; i < n; i++) 
            var removedToken = removedTokens[i];
            el = map[removedToken];
            delete map[removedToken];
            var newToken = tokenFn(el);
            // if the element wasn't removed but simply got a new token, its old token will be different from the current one
            if (newToken === removedToken) 
              self.onRemoved(el);
             else 
              replacedTokens[newToken] = removedToken;
              self.onChanged(el);
            
          

          var addedTokens = subtractAsSets(newTokens, oldTokens);
          for (i = 0, n = addedTokens.length; i < n; i++) 
            token = addedTokens[i];
            el = map[token];
            if (!replacedTokens[token]) 
              self.onAdded(el);
            
          
        ;
        return self = 
          subscribe: function(scope, onChanged) 
            scope.$watch(getTokens, function(newTokens, oldTokens) 
              if (!onChanged || onChanged(newTokens, oldTokens) !== false) 
                applyChanges(newTokens, oldTokens);
              
            , true);
          ,
          onAdded: angular.noop,
          onChanged: angular.noop,
          onRemoved: angular.noop
        ;
      ;

      this.getFullCalendarConfig = function(calendarSettings, uiCalendarConfig)
          var config = ;

          angular.extend(config, uiCalendarConfig);
          angular.extend(config, calendarSettings);

          angular.forEach(config, function(value,key)
            if (typeof value === 'function')
              config[key] = wrapFunctionWithScopeApply(config[key]);
            
          );

          return config;
      ;
  ])

.directive('jqyouiDraggable', ['ngDragDropService', function(ngDragDropService) 
    return 
      require: '?jqyouiDroppable',
      restrict: 'A',
      link: function(scope, element, attrs) 
        var dragSettings, jqyouiOptions, zIndex;
        var updateDraggable = function(newValue, oldValue) 
          if (newValue) 
            dragSettings = scope.$eval(element.attr('jqyoui-draggable') || element.attr('data-jqyoui-draggable')) || ;
            jqyouiOptions = scope.$eval(attrs.jqyouiOptions) || ;
            element
              .draggable(disabled: false)
              .draggable(jqyouiOptions)
              .draggable(
                start: function(event, ui) 
                  zIndex = angular.element(jqyouiOptions.helper ? ui.helper : this).css('z-index');
                  angular.element(jqyouiOptions.helper ? ui.helper : this).css('z-index', 9999);
                  angular.startXY = angular.element(this).offset();
                  ngDragDropService.callEventCallback(scope, dragSettings.onStart, event, ui);
                ,
                stop: function(event, ui) 
                  angular.element(jqyouiOptions.helper ? ui.helper : this).css('z-index', zIndex);
                  ngDragDropService.callEventCallback(scope, dragSettings.onStop, event, ui);
                ,
                drag: function(event, ui) 
                  ngDragDropService.callEventCallback(scope, dragSettings.onDrag, event, ui);
                
              );
           else 
            element.draggable(disabled: true);
          
        ;
        scope.$watch(function()  return scope.$eval(attrs.drag); , updateDraggable);
        updateDraggable();

        element.on('$destroy', function() 
          element.draggable('destroy');
        );
      
    ;
  ])

.directive('jqyouiDroppable', ['ngDragDropService', function(ngDragDropService) 
    return 
      restrict: 'A',
      priority: 1,
      link: function(scope, element, attrs) 
        var dropSettings;
        var updateDroppable = function(newValue, oldValue) 
          if (newValue) 
            dropSettings = scope.$eval(angular.element(element).attr('jqyoui-droppable') || angular.element(element).attr('data-jqyoui-droppable')) || ;
            element
              .droppable(disabled: false)
              .droppable(scope.$eval(attrs.jqyouiOptions) || )
              .droppable(
                over: function(event, ui) 
                  ngDragDropService.callEventCallback(scope, dropSettings.onOver, event, ui);
                ,
                out: function(event, ui) 
                  ngDragDropService.callEventCallback(scope, dropSettings.onOut, event, ui);
                ,
                drop: function(event, ui) 
                  if (angular.element(ui.draggable).ngattr('ng-model') && attrs.ngModel) 
                    ngDragDropService.invokeDrop(scope, angular.element(ui.draggable), angular.element(this), event, ui);
                   else 
                    ngDragDropService.callEventCallback(scope, dropSettings.onDrop, event, ui);
                  
                
              );
           else 
            element.droppable(disabled: true);
          
        ;

        scope.$watch(function()  return scope.$eval(attrs.drop); , updateDroppable);
        updateDroppable();

        element.on('$destroy', function() 
          element.droppable('destroy');
        );
      
    ;
  ])

.directive('uiCalendar', ['uiCalendarConfig', '$locale', function(uiCalendarConfig, $locale) 
    return 
        restrict: 'A',
        scope: eventSources:'=ngModel',calendarWatchEvent: '&',
        controller: 'uiCalendarCtrl',
        link: function(scope, elm, attrs, controller) 

            var sources = scope.eventSources,
                sourcesChanged = false,
                eventSourcesWatcher = controller.changeWatcher(sources, controller.sourcesFingerprint),
                eventsWatcher = controller.changeWatcher(controller.allEvents, controller.eventsFingerprint),
                options = null;

            function getOptions()
                var calendarSettings = attrs.uiCalendar ? scope.$parent.$eval(attrs.uiCalendar) : ,
                fullCalendarConfig;
                fullCalendarConfig = controller.getFullCalendarConfig(calendarSettings, uiCalendarConfig);

                options =  eventSources: sources ;
                angular.extend(options, fullCalendarConfig);

                var options2 = ;
                for(var o in options)
                    if(o !== 'eventSources')
                      options2[o] = options[o];
                    
                
                return JSON.stringify(options2);
            

            scope.destroy = function()
              if(attrs.calendar) 
                scope.calendar = scope.$parent[attrs.calendar] =  elm.html('');
               else 
                scope.calendar = elm.html('');
              
            ;

            scope.init = function()
              scope.calendar.fullCalendar(options);
            ;

            eventSourcesWatcher.onAdded = function(source) 
              scope.calendar.fullCalendar('addEventSource', source);
              sourcesChanged = true;
            ;

            eventSourcesWatcher.onRemoved = function(source) 
              scope.calendar.fullCalendar('removeEventSource', source);
              sourcesChanged = true;
            ;

            eventsWatcher.onAdded = function(event) 
              scope.calendar.fullCalendar('renderEvent', event, true);
            ;

            eventsWatcher.onRemoved = function(event) 
              scope.calendar.fullCalendar('removeEvents', function(e)  return e === event; );
            ;

            eventsWatcher.onChanged = function(event) 
              scope.calendar.fullCalendar('updateEvent', event);
            ;

            eventSourcesWatcher.subscribe(scope);
            eventsWatcher.subscribe(scope, function(newTokens, oldTokens) 
              if (sourcesChanged === true) 
                sourcesChanged = false;
                // prevent incremental updates in this case
                return false;
              
            );

            scope.$watch(getOptions, function(newO,oldO)
                scope.destroy();
                scope.init();
            );
      
    ;
])

.service('ngDragDropService', ['$timeout', '$parse', function($timeout, $parse) 
    this.callEventCallback = function (scope, callbackName, event, ui) 
      if (!callbackName) return;

      var objExtract = extract(callbackName),
          callback = objExtract.callback,
          constructor = objExtract.constructor,
          args = [event, ui].concat(objExtract.args);

      // call either $scoped method i.e. $scope.dropCallback or constructor's method i.e. this.dropCallback
      scope.$apply((scope[callback] || scope[constructor][callback]).apply(scope, args));

      function extract(callbackName) 
        var atStartBracket = callbackName.indexOf('(') !== -1 ? callbackName.indexOf('(') : callbackName.length,
            atEndBracket = callbackName.lastIndexOf(')') !== -1 ? callbackName.lastIndexOf(')') : callbackName.length,
            args = callbackName.substring(atStartBracket + 1, atEndBracket), // matching function arguments inside brackets
            constructor = callbackName.match(/^[^.]+.\s*/)[0].slice(0, -1); // matching a string upto a dot to check ctrl as syntax
            constructor = scope[constructor] && typeof scope[constructor].constructor === 'function' ? constructor : null;

        return 
          callback: callbackName.substring(constructor && constructor.length + 1 || 0, atStartBracket),
          args: (args && args.split(',') || []).map(function(item)  return $parse(item)(scope); ),
          constructor: constructor
        
      
    ;

    this.invokeDrop = function (scope, $draggable, $droppable, event, ui) 
      var dragModel = '',
        dropModel = '',
        dragSettings = ,
        dropSettings = ,
        jqyoui_pos = null,
        dragItem = ,
        dropItem = ,
        dragModelValue,
        dropModelValue,
        $droppableDraggable = null,
        droppableScope = $droppable.scope(),
        draggableScope = $draggable.scope();

      dragModel = $draggable.ngattr('ng-model');
      dropModel = $droppable.ngattr('ng-model');
      dragModelValue = draggableScope.$eval(dragModel);
      dropModelValue = droppableScope.$eval(dropModel);

      $droppableDraggable = $droppable.find('[jqyoui-draggable]:last,[data-jqyoui-draggable]:last');
      dropSettings = droppableScope.$eval($droppable.attr('jqyoui-droppable') || $droppable.attr('data-jqyoui-droppable')) || [];
      dragSettings = draggableScope.$eval($draggable.attr('jqyoui-draggable') || $draggable.attr('data-jqyoui-draggable')) || [];

      // Helps pick up the right item
      dragSettings.index = this.fixIndex(draggableScope, dragSettings, dragModelValue);
      dropSettings.index = this.fixIndex(droppableScope, dropSettings, dropModelValue);

      jqyoui_pos = angular.isArray(dragModelValue) ? dragSettings.index : null;
      dragItem = angular.isArray(dragModelValue) ? dragModelValue[jqyoui_pos] : dragModelValue;


      if (angular.isArray(dropModelValue) && dropSettings && dropSettings.index !== undefined) 
        dropItem = dropModelValue[dropSettings.index];
       else if (!angular.isArray(dropModelValue)) 
        dropItem = dropModelValue;
       else 
        dropItem = ;
      

      if (dragSettings.animate === true) 
        this.move($draggable, $droppableDraggable.length > 0 ? $droppableDraggable : $droppable, null, 'fast', dropSettings, null);
        this.move($droppableDraggable.length > 0 && !dropSettings.multiple ? $droppableDraggable : [], $draggable.parent('[jqyoui-droppable],[data-jqyoui-droppable]'), angular.startXY, 'fast', dropSettings, angular.bind(this, function() 
          $timeout(angular.bind(this, function() 
            // Do not move this into move() to avoid flickering issue
            $draggable.css('position': 'relative', 'left': '', 'top': '');
            // Angular v1.2 uses ng-hide to hide an element not display property
            // so we've to manually remove display:none set in this.move()
            $droppableDraggable.css('position': 'relative', 'left': '', 'top': '', 'display': '');

            this.mutateDraggable(draggableScope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable);
            this.mutateDroppable(droppableScope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos);
            this.callEventCallback(droppableScope, dropSettings.onDrop, event, ui);
          ));
        ));
       else 
        $timeout(angular.bind(this, function() 
          this.mutateDraggable(draggableScope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable);
          this.mutateDroppable(droppableScope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos);
          this.callEventCallback(droppableScope, dropSettings.onDrop, event, ui);
        ));
      
    ;

    this.move = function($fromEl, $toEl, toPos, duration, dropSettings, callback) 
      if ($fromEl.length === 0) 
        if (callback) 
          window.setTimeout(function() 
            callback();
          , 300);
        
        return false;
      

      var zIndex = 9999,
        fromPos = $fromEl.offset(),
        wasVisible = $toEl && $toEl.is(':visible'),
        hadNgHideCls = $toEl.hasClass('ng-hide');

      if (toPos === null && $toEl.length > 0) 
        if (($toEl.attr('jqyoui-draggable') || $toEl.attr('data-jqyoui-draggable')) !== undefined && $toEl.ngattr('ng-model') !== undefined && $toEl.is(':visible') && dropSettings && dropSettings.multiple) 
          toPos = $toEl.offset();
          if (dropSettings.stack === false) 
            toPos.left+= $toEl.outerWidth(true);
           else 
            toPos.top+= $toEl.outerHeight(true);
          
         else 
          // Angular v1.2 uses ng-hide to hide an element 
          // so we've to remove it in order to grab its position
          if (hadNgHideCls) $toEl.removeClass('ng-hide');
          toPos = $toEl.css('visibility': 'hidden', 'display': 'block').offset();
          $toEl.css('visibility': '','display': wasVisible ? 'block' : 'none');
        
      

      $fromEl.css('position': 'absolute', 'z-index': zIndex)
        .css(fromPos)
        .animate(toPos, duration, function() 
          // Angular v1.2 uses ng-hide to hide an element
          // and as we remove it above, we've to put it back to
          // hide the element (while swapping) if it was hidden already
          // because we remove the display:none in this.invokeDrop()
          if (hadNgHideCls) $toEl.addClass('ng-hide');
          if (callback) callback();
        );
    ;

    this.mutateDroppable = function(scope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos) 
      var dropModelValue = scope.$eval(dropModel);

      scope.dndDragItem = dragItem;

      if (angular.isArray(dropModelValue)) 
        if (dropSettings && dropSettings.index >= 0) 
          dropModelValue[dropSettings.index] = dragItem;
         else 
          dropModelValue.push(dragItem);
        
        if (dragSettings && dragSettings.placeholder === true) 
          dropModelValue[dropModelValue.length - 1]['jqyoui_pos'] = jqyoui_pos;
        
       else 
        $parse(dropModel + ' = dndDragItem')(scope);
        if (dragSettings && dragSettings.placeholder === true) 
          dropModelValue['jqyoui_pos'] = jqyoui_pos;
        
      
    ;

    this.mutateDraggable = function(scope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable) 
      var isEmpty = angular.equals(angular.copy(dropItem), ),
        dragModelValue = scope.$eval(dragModel);

      scope.dndDropItem = dropItem;

      if (dragSettings && dragSettings.placeholder) 
        if (dragSettings.placeholder != 'keep')
          if (angular.isArray(dragModelValue) && dragSettings.index !== undefined) 
            dragModelValue[dragSettings.index] = dropItem;
           else 
            $parse(dragModel + ' = dndDropItem')(scope);
          
        
       else 
        if (angular.isArray(dragModelValue)) 
          if (isEmpty) 
            if (dragSettings && ( dragSettings.placeholder !== true && dragSettings.placeholder !== 'keep' )) 
              dragModelValue.splice(dragSettings.index, 1);
            
           else 
            dragModelValue[dragSettings.index] = dropItem;
          
         else 
          // Fix: LIST(object) to LIST(array) - model does not get updated using just scope[dragModel] = ...
          // P.S.: Could not figure out why it happened
          $parse(dragModel + ' = dndDropItem')(scope);
          if (scope.$parent) 
            $parse(dragModel + ' = dndDropItem')(scope.$parent);
          
        
      

      $draggable.css('z-index': '', 'left': '', 'top': '');
    ;

    this.fixIndex = function(scope, settings, modelValue) 
      if (settings.applyFilter && angular.isArray(modelValue) && modelValue.length > 0) 
        var dragModelValueFiltered = scope[settings.applyFilter](),
            lookup = dragModelValueFiltered[settings.index],
            actualIndex = undefined;

        modelValue.forEach(function(item, i) 
           if (angular.equals(item, lookup)) 
             actualIndex = i;
           
        );

        return actualIndex;
      

      return settings.index;
    ;
  ])
;

【问题讨论】:

你有没有让这个工作? 是的,安德斯的回答是正确的 【参考方案1】:

没有使用 angular-dragdrop,但是文档说配置对象应该包含一个 onDrop 属性。尝试将jqyoui-droppable="multiple:true" 替换为jqyoui-droppable="multiple:true, onDrop: 'drop'"。 angular-dragdrop 似乎期望 onDrop 是一个字符串,其中包含作用域上的函数名称。

【讨论】:

感谢安德斯!我正在尝试触发日历的 drop 事件,但它仍然非常安静:( 所以你的意思是你的 $scope.drop() 方法根本没有被调用,即使我提到了改变? 是的 $scope.drop 是由于回调而被调用的,但它应该从 $scope.uiConfig 中的 drop 函数中调用,因此我可以获得元素被放置到的日历日期。 【参考方案2】:

实现此目的的另一种方法是使用可拖动的 JqueryUI。创建指令并通过“elem”传递属性。您也可以将标题作为属性包含在内。

.directive('dragMe', function() 
  return 
    restrict: 'A',
    link: function(scope, elem, attr, ctrl) 
      elem.data('event', 
          title: $.trim($(elem).text()), // use the element's text as the event title
          stick: true // maintain when user navigates (see docs on the renderEvent method)
        );
      elem.draggable(
          zIndex: 999,
          revert: true,      // will cause the event to go back to its
          revertDuration: 0  //  original position after the drag
        );
    
  ;
)

【讨论】:

以上是关于如何仅使用角度指令将放置元素拖放到日历中的主要内容,如果未能解决你的问题,请参考以下文章

JQuery UI - 可拖动元素被拖放到可放置区域的多个可放置区域按比例缩小

将图像拖放到画布可放置区域

Jquery将div中的文本拖放到较小的div中

jQuery ui 可拖动拖放到 iframe

如何使用 HTML5 使某些元素可拖动和其他可放置?

将列表中的元素拖放到单独的块中