加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

angularjs – 如何仅使用角度指令将元素拖放到日历上

发布时间:2020-12-17 17:13:13 所属栏目:安全 来源:网络整理
导读:我正在尝试使用angular指令实现拖放日历.日历使用ui-calendar( https://github.com/angular-ui/ui-calendar),Arshaw FullCalendar的完整AngularJS指令. 元件在日历上的拖放由角度拖拽(https://github.com/codef0rmer/angular-dragdrop)提供动力. 这是文件夹d
我正在尝试使用angular指令实现拖放日历.日历使用ui-calendar( https://github.com/angular-ui/ui-calendar),Arshaw FullCalendar的完整AngularJS指令.

元件在日历上的拖放由角度拖拽(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-hide{display:none !important;}ng:form{display:block;}.ng-animate-block-transitions{transition: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',$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,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,drag: function(event,ui) {
                  ngDragDropService.callEventCallback(scope,dragSettings.onDrag,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',function(ngDragDropService) {
    return {
      restrict: 'A',priority: 1,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,dropSettings.onOver,out: function(event,dropSettings.onOut,drop: function(event,ui) {
                  if (angular.element(ui.draggable).ngattr('ng-model') && attrs.ngModel) {
                    ngDragDropService.invokeDrop(scope,angular.element(ui.draggable),angular.element(this),ui);
                  } else {
                    ngDragDropService.callEventCallback(scope,dropSettings.onDrop,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',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',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,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,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,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',null);
        this.move($droppableDraggable.length > 0 && !dropSettings.multiple ? $droppableDraggable : [],$draggable.parent('[jqyoui-droppable],[data-jqyoui-droppable]'),angular.startXY,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','top': '','display': ''});

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

    this.move = function($fromEl,$toEl,toPos,duration,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,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,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,$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': '','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;
    };
  }])
;

解决方法

没有使用angular-dragdrop,但是文档说配置对象应该包含onDrop属性.尝试用jqyoui-droppable =“{multiple:true,onDrop:’drop’}”替换jqyoui-droppable =“{multiple:true}”. angular-dragdrop似乎期望onDrop是一个带有作用域名称的字符串.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读