将过滤器应用于外部事件时,无法再拖入全日历 [英] unable to drag anymore into fullcalendar when filter is applied on external events

查看:85
本文介绍了将过滤器应用于外部事件时,无法再拖入全日历的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将事件从外部事件框中拖到日历中.

I'm trying to drag events from the external events box to the fullcalendar.

我在以下 CodePen 中重新创建了我遇到的问题:

I recreated the issue that I face in the following CodePen:

第一次将外部事件从列表拖到日历中就可以了. 但是,当我在搜索输入文字过滤器书中的某本书上应用过滤器时,会遇到以下问题.

at the first time, Dragging an external event from the list into the calendar works fine. However, when I apply the filter on a book in the search input text filter books I have the following issue.

以下是复制步骤:

1-在搜索输入中寻找111,该输入将过滤到唯一的111事件手册.

1- look for 111 in the search input that will filter to the only resulting 111 event book.

2-如您所见,将这个结果事件从过滤器拖到 日历可以正常工作:但是在这里,我们主要对该案感兴趣 我们决定进行过滤,但不要将任何内容拖到日历中.

2- As you could observe that dragging this resulting event from the filter into the calendar could work fine : but Here we are mainly interested on the case we decide to filter but to do not drag anything into calendar.

3-因此,现在将111过滤器清除为来自搜索输入的文本, 外部事件框将支持所有默认事件,但是这次尝试 将其中之一拖到日历冻结中.不再工作了.
它会冻结所有 最后发现的外部事件 通过过滤器表示111事件(请查看事件的最后一行) 那种可能被拖拉的东西. 并且不再能够将其他事件拖到日历中.

3- so for now clear the 111 filter as text from the search input, the external events box would back all the defaults events but this time trying to drag one of them into the calendar freezes. It's no more working.
It freezes on all the external events except for the last found by the filter means the 111 event (look at the last row on the events ) that onlty one that could be dragged. and no more able to drag others events into the calendar.

4-甚至如果我将111拖到日历中,然后再尝试将其拖到日历中 另一个会冻结.

4- and even If I dragged 111 into the calendar, and after that if I try to drag anothor one It will freeze.

<!DOCTYPE html>
<html ng-app="app">
  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <script data-require="angular.js@1.0.x" src="https://code.angularjs.org/1.3.15/angular.min.js" data-semver="1.0.7"></script>
    <script type="text/javascript" src="https://github.com/kamilkp/angular-vs-repeat/blob/master/dist/angular-vs-repeat.js"></script>
    <script src="https://code.angularjs.org/1.3.15/angular-animate.min.js"></script>
  </head>
<body ng-controller="MainCtrl">
<div id="first">
             <input type="search" id="myInput" ng-model="searchText" placeholder="filter books..." title="filter books"/>
             <div style="width:200px;display:inline-block;vertical-align:top;color: gray;">
                    <input type='checkbox' id='drop-remove' checked='checked'/>
                    <label for='drop-remove'> Remove after a drag </label>
             </div>
 <div id='external-events'>

             <ul vs-repeat="60" class="repeater-container" title="Books darggable({{books.length}})" data-                        drag="true"  data-jqyoui-options="{revert: 'invalid'}">
                 <li class="animate-repeat fc-event item-element" ng-repeat="book in books | orderBy : sort : false | filter:searchText as results track by book.contents.name"   id="{{book.id}}">

                   <div class="circle">{{book.contents['date']}}</div>
                   <div class="left content" ng-bind-html="trustAsHtml(book.contents['name'])" id="book_{{book.id}}"></div>
                   <div class="left rating">2/10</div>
                   <div class="clear"></div>
                </li>
                <li class="animate-repeat" ng-if="results.length === 0">
                    <strong>No results found...</strong>
                </li>

             </ul>
  </div>
</div>

<div id="second"> 
  <div id='calendar-container'>
    <div id='calendar'></div>
  </div>
</div>
</body>
</html>

js

var app = angular.module("app", ['ngAnimate']);
(function(angular) {
  'use strict';
app.controller("MainCtrl", ['$scope', '$sce', function($scope, $sce){

 $scope.books = [
                {
                    id: 'id1',
                    contents: {
                        name: '<span>1Alain du sceau france</span><br><span> Canada Madagascar philipine</span>',
                        price: 'price1',
                        date: '111'
                    }
                },
                {
                    id: 'id2',
                    contents: {
                        name: '<span>2Name zu Long zu Schreiben Bis Here ist Ein Beispiel</span><br><span>Maneschester Canada Madagascar philipine</span>',
                        price: 'price2',
                        date: '22'
                    }
                },
                {
                    id: 'id3',
                    contents: {
                        name: '<span>3name Aleatoire Schwer und zu Leicht Zu Schreiben</span><br><span>Mexico Canada USA France Uk Deutschland Schweiz Madagascar philipine</span>',
                        price: 'price3',
                        date: '23'
                    }
                },
                {
                    id: '4',
                    contents: {
                        name: '<span>4name Aleatoire Schwer und zu Leicht Zu Schreiben</span><br><span>Mexico Canada USA France Uk Deutschland Schweiz Madagascar philipine</span>',
                        price: 'price4',
                        date: '4'
                    }
            },
            {
                    id: 'id5',
                    contents: {
                        name: '<span>5name Aleatoire Schwer und zu Leicht Zu Schreiben</span><br><span>Mexico Canada USA France Uk Deutschland Schweiz Madagascar philipine</span>',
                        price: 'price5',
                        date: '5'
                    }
            },
            {
                    id: 'id6',
                    contents: {
                        name: '<span>6name Aleatoire Schwer und zu Leicht Zu Schreiben</span><br><span>Mexico Canada USA France Uk Deutschland Schweiz Madagascar philipine</span>',
                        price: 'price6',
                        date: '6'
                    }
            },
            {
                    id: 'id7',
                    contents: {
                        name: '<span>7name Aleatoire Schwer und zu Leicht Zu Schreiben</span><br><span>Mexico Canada USA France Uk Deutschland Schweiz Madagascar philipine</span>',
                        price: 'price7',
                        date: '7'
                    }
            },
            {
                    id: 'id8',
                    contents: {
                        name: '<span>8name Aleatoire Schwer und zu Leicht Zu Schreiben</span><br><span>Mexico Canada USA France Uk Deutschland Schweiz Madagascar philipine</span>',
                        price: 'price8',
                        date: '8'
                    }
            },
            {
                    id: 'id9',
                    contents: {
                        name: '<span>9name Aleatoire Schwer und zu Leicht Zu Schreiben</span><br><span>Mexico Canada USA France Uk Deutschland Schweiz Madagascar philipine</span>',
                        price: 'price9',
                        date: '9'
                    }
            },
               {
                    id: 'id10',
                    contents: {
                        name: '<span>10Alain du sceau france</span><br><span> Canada Madagascar philipine</span>',
                        price: 'price10',
                        date: '10'
                    }
                },
                {
                    id: 'id11',
                    contents: {
                        name: '<span>11Name zu Long zu Schreiben Bis Here ist Ein Beispiel</span><br><span>Maneschester Canada Madagascar philipine</span>',
                        price: 'price11',
                        date: '11'
                    }
                },
                {
                    id: 'id12',
                    contents: {
                        name: '<span>12name Aleatoire Schwer und zu Leicht Zu Schreiben</span><br><span>Mexico Canada USA France Uk Deutschland Schweiz Madagascar philipine</span>',
                        price: 'price12',
                        date: '12'
                    }
                },
                {
                    id: 'id13',
                    contents: {
                        name: '<span>13name Aleatoire Schwer und zu Leicht Zu Schreiben</span><br><span>Mexico Canada USA France Uk Deutschland Schweiz Madagascar philipine</span>',
                        price: 'price13',
                        date: '13'
                    }
            },
            {
                    id: 'id14',
                    contents: {
                        name: '<span>14name Aleatoire Schwer und zu Leicht Zu Schreiben</span><br><span>Mexico Canada USA France Uk Deutschland Schweiz Madagascar philipine</span>',
                        price: 'price14',
                        date: '14'
                    }
            },
            {
                    id: 'id15',
                    contents: {
                        name: '<span>15name Aleatoire Schwer und zu Leicht Zu Schreiben</span><br><span>Mexico Canada USA France Uk Deutschland Schweiz Madagascar philipine</span>',
                        price: 'price15',
                        date: '15'
                    }
            },
            {
                    id: 'id16',
                    contents: {
                        name: '<span>16name Aleatoire Schwer und zu Leicht Zu Schreiben</span><br><span>Mexico Canada USA France Uk Deutschland Schweiz Madagascar philipine</span>',
                        price: 'price16',
                        date: '16'
                    }
            },
            {
                    id: 'id17',
                    contents: {
                        name: '<span>17name Aleatoire Schwer und zu Leicht Zu Schreiben</span><br><span>Mexico Canada USA France Uk Deutschland Schweiz Madagascar philipine</span>',
                        price: 'price17',
                        date: '17'
                    }
            },
            {
                    id: 'id18',
                    contents: {
                        name: '<span>18name Aleatoire Schwer und zu Leicht Zu Schreiben</span><br><span>Mexico Canada USA France Uk Deutschland Schweiz Madagascar philipine</span>',
                        price: 'price18',
                        date: '18'
                    }
            },
            {
                    id: 'id19',
                    contents: {
                        name: '<span>19name Aleatoire Schwer und zu Leicht Zu Schreiben</span><br><span>Mexico Canada USA France Uk Deutschland Schweiz Madagascar philipine</span>',
                        price: 'price19',
                        date: '19'
                    }
            },
            {
                    id: 'id20',
                    contents: {
                        name: '<span>20name Aleatoire Schwer und zu Leicht Zu Schreiben</span><br><span>Mexico Canada USA France Uk Deutschland Schweiz Madagascar philipine</span>',
                        price: 'price20',
                        date: '20'
                    }
            }
            ];





  /*$scope.books.forEach(function(book) {
    book.contents.name =  $sce.trustAsHtml(book.contents.name);
  })*/
  $scope.trustAsHtml = $sce.trustAsHtml;

  $scope.h = function(html) {
    return $sce.trustAsHtml(html);
  };

  $scope.sort = function(num) {
    var newNum = parseInt(num.contents.date);
    console.log("$$newnum",newNum);
    return newNum;
  };


 $(document).ready( function(){     
        //Initialise external events
        initialise_external_event('.fc-event');
        initialise_calendar();

  });





  // initialize the external events
  // -----------------------------------------------------------------
function initialise_external_event(selector){

   /* initialize the external events
        -----------------------------------------------------------------*/

        $('#external-events .fc-event').each(function() {

            // store data so the calendar knows to render an event upon drop
            $(this).data('event', {
                title: $.trim($(this).text()), // use the element's text as the event title
                stick: true // maintain when user navigates (see docs on the renderEvent method)
            });

            // make the event draggable using jQuery UI
            $(this).draggable({
                zIndex: 999,
                revert: true,      // will cause the event to go back to its
                revertDuration: 0  //  original position after the drag
            });

        });



}
  function initialise_calendar(){
     /* initialize the calendar
        -----------------------------------------------------------------*/

        $('#calendar').fullCalendar({
            header: {
                left: 'prev,next today',
                center: 'title',
                right: 'month,agendaWeek,agendaDay'
            },
            editable: true,
            droppable: true, // this allows things to be dropped onto the calendar
            dragRevertDuration: 0,
            drop: function() {
                // is the "remove after drop" checkbox checked?
                if ($('#drop-remove').is(':checked')) {
                    // if so, remove the element from the "Draggable Events" list
                    $(this).remove();
                }
            },
            eventDragStop: function( event, jsEvent, ui, view ) {

                if(isEventOverDiv(jsEvent.clientX, jsEvent.clientY)) {
                    $('#calendar').fullCalendar('removeEvents', event._id);
                    var el = $( "<div class='fc-event'>" ).appendTo( '#external-events-listing' ).text( event.title );
                    el.draggable({
                      zIndex: 999,
                      revert: true, 
                      revertDuration: 0 
                    });
                    el.data('event', { title: event.title, id :event.id, stick: true });
                }
            }
        });


        var isEventOverDiv = function(x, y) {

            var external_events = $( '#external-events' );
            var offset = external_events.offset();
            offset.right = external_events.width() + offset.left;
            offset.bottom = external_events.height() + offset.top;

            // Compare
            if (x >= offset.left
                && y >= offset.top
                && x <= offset.right
                && y <= offset .bottom) { return true; }
            return false;

        }
 }

}]);
})(window.angular);

css

    ul[title]::before {

    content: attr(title);
     /* then add some nice styling as needed, eg: */

     font: italic 11px "Trebuchet MS", Verdana, Arial, Helvetica,    sans-serif;
    color: gray;
}

/*ul {
  list-style-type: none;
}*/

#myInput {
  /*background-image: url('/css/searchicon.png');*/
  background-position: 10px 12px;
  background-repeat: no-repeat;
  width: 77%;
  font-size: 16px;
  padding: 12px 20px 12px 40px;
  border: 1px solid #ddd;
  margin-bottom: 12px;
}

/*ul>li {
  display:block;
    padding-right: 0cm;
    margin-left: 0px;
}*/

#calendar{
 padding: 0 10px;
 width: 650px;
 float: right;
 margin: 0px 0px 10px 55px;
 }

#external-events {
  width: 500px;
  padding: 0 0px;
  border: 0px solid #ccc;/* gray moyen*/
  background: #eee;/* #5D6D7E;(Blue mat) */ /* #eee color gray*/
  text-align: left;
}

#external-events .fc-event {
  cursor: pointer;
  z-index: 9999;
  background: #eee;
  border: solid 1px black;
  border-radius: 2px;
  margin-bottom:5px;
}

.content span
{
  color: gray;
}
.fc-event span:first-child
{
  font-size: 25px;
  font-weight: bold italic;
}

.content
{
  float:left;
  max-width:75%;
}

.clear
{
  clear:both;
}

.circle {
  float:left;
  width: 10%;
  height: 25%;
  padding: 0 10px;
  border-radius: 360px;


  /* Just making it pretty */
  @shadow: rgba(0, 0, 0, .1);
  @shadow-length: 4px;
  -webkit-box-shadow: 0 @shadow-length 0 0 @shadow;
          box-shadow: 0 @shadow-length 0 0 @shadow;
  text-shadow: 0 @shadow-length 0 @shadow;
  background: #FFFFFF;/*color white*/
  color: #f05907;/* color red*/
  font-family: Helvetica, Arial Black, sans;
  font-size: 10;
  text-align: center;
}

.rating
{
  float:right;
  background: #FFFFFF;/*color white*/
  color: #f05907;/* color red*/
  font-family: Helvetica, Arial Black, sans;
  font-size: 10;
  text-align: center;
  border-radius: 360px;
}

.animate-repeat {
  line-height:30px;
  list-style:none;
  box-sizing:border-box;
}

.animate-repeat.ng-move,
.animate-repeat.ng-enter,
.animate-repeat.ng-leave {
  transition:all linear 0.5s;
}

.animate-repeat.ng-leave.ng-leave-active,
.animate-repeat.ng-move,
.animate-repeat.ng-enter {
  opacity:0;
  max-height:0;
}

.animate-repeat.ng-leave,
.animate-repeat.ng-move.ng-move-active,
.animate-repeat.ng-enter.ng-enter-active {
  opacity:1;
  max-height:30px;
}


#first {
    width: 650px;
    float: left
}
#second {
    width: 650px;
    float: left;
}


.repeater-container{
  height: 445px;
  overflow: auto;
  box-shadow: 0 0 10px;
  border-radius: 5px;
  list-style: none;
  margin: 0;
  padding: 0;
  -webkit-overflow-scrolling: touch;
}
.repeater-container .item-element {
    margin: 0 !important;
    width: 100%;
    height: 140px;
    border: 1px solid green;
    box-sizing: border-box;
    -moz-box-sizing: border-box;
}

推荐答案

使用过滤器的ng-repeat重新创建项目DOM.

ng-repeat with the filter recreats items DOM.

不应该做的事情 (正如您从HTML代码中看到的那样,我正在使用ng-repeat和track by book.contents.name,据我所知

Thing that should not be done (As you can see from my code in HTML I'm using ng-repeat with track by book.contents.name and as far as I know in

那种情况下angularjs不会重新创建dom.否则,如果我们使用

that case angularjs would not recreat the dom. Otherwise, in case we use

没有ID跟踪的ng-repeat angularjs将重新创建您可以看到的dom THE_FOLOWING_LINK ).

ng-repeat without track by id angularjs would recreat the dom you could see THE_FOLOWING_LINK ).

要确切地理解dom为什么是

It's quite pretty tough for me to understand exactely why the dom is

在这种情况下,使用ng-repeat和过滤器重新创建!

recreated in that case with ng-repeat with the filter !

下面的文字描述了我已经完成的解决方法:

A workaround that I've done is described in text below:

1-因此,为了使这些项目再次可拖动,我们必须调用 .draggable()进行每个过滤操作.

1- So In order to make the items draggable again, we must call .draggable() on every filter action.

完美的解决方案是在一个角度内进行DOM操作 通过链接功能的指令. 但是这里让事情变得简单很多,我刚刚提到的一种解决方法 detectEmpty是有作用域的函数.

The perfect solution is to do a DOM manipulation within an angular directive via the link function. but Here to make things much simple a workaround That I just included in the detectEmpty which is a scoped function .

2-添加了ng-change ="detectEmpty()"以检测何时清除输入

2- added ng-change="detectEmpty()" to detect when the input is cleared

<input type="search" id="myInput" ng-model="searchText" placeholder="filter books..." title="filter books" ng-change="detectEmpty()"/>

3-和.js

$scope.detectEmpty = function() {
    if ($scope.searchText.trim().length === 0) {
         // it's empty
        $(document).ready( function(){     


           initialise_external_event('.fc-event');

        });

    }

Let's try to understand what's going on here?
=============================================
- All html elements loaded first.
- JS trigger all dragging functionality to loaded DOM.
- ng-repeat with filter function call to fetch new data and replace old 
  DOMs.
- Now drag functionality stops working.
- why? because JS drag function run on old DOM and currently it has been 
  removed. **why it has been moved ? I don't have any clue on that**
 - need to call the drag function again on new loaded DOM.
 - that's why added the call to initialise_external_event('.fc-event');
   when the input search is cleared. 

希望它会对其他人有所帮助;)

Hope it would help someone else ;)

还希望AngularJS专业人士告诉我们为什么要在这种情况下重新创建dom吗?

因为之前提出的解决方案是不好的解决方案.

because proposed solution before is a bad solution .

通过每次应用过滤器时重新初始化外部事件:

by re-initializing external events each time when a filter is applied by doing so :

$scope.detectEmpty = function() {
        if ($scope.searchText.trim().length === 0) {
             // it's empty
            $(document).ready( function(){     


               initialise_external_event('.fc-event');

            });

        }

每次将一个附加的外部事件添加到外部事件框中. 例如,如果我们从一个包含20个条目的数组开始,我们将在下一个过滤器上得到40个条目,并且每次应用该过滤器时,我们将向外部数组条目中添加20个条目. 20,40,60,80等等..

will add each time an additional external event to external event box. If we start for example with an array of 20 entries we will end up with 40 on the next filter apply and we will add a 20 entries to our exteranl array entries each time the filter is applied. 20,40,60,80 and so..on,

问题是在将ng-repeat与filter一起使用时如何保留DOM元素?

The question is How to preserve DOM elements when using ng-repeat with filter?

我有一个带过滤器的ng-repeat.

I have an ng-repeat with a filter.

当某些项目被过滤器过滤掉后, 它们在另一个过滤器更改之后恢复 是为这些项目创建的新DOM元素.

When some items are filtered out by the filter and then they are restored after another filter change there are new DOM elements created for these items.

如果该项目上有任何DOM操作,则它会在丢失后丢失 该项目被隐藏并使用过滤器恢复.

If there was any DOM manipulation on the item it gets lost after item is hidden and restored with filter.

即使通过过滤器删除了项目,是否仍可以保留DOM元素?

Is there a way to keep the DOM elements, even when item is removed by filter?

我尝试使用跟踪 LINK ,但这无济于事.

I tried using track by which following LINK, but it doesn't help.

1-因此,请注意DOM的更改并进行确认.您可以使用以下链接并使用 Basheer AL-MOMANI的服务

1- So to watch the DOM change and to confirm that. You can use the following link and use service from Basheer AL-MOMANI

2-问题的根源是我试图在角度之外进行此操作. 当在angular中进行所有操作时,angular将根据需要为我们更新DOM.

2- The root of the problem is that I'm trying to do this outside of angular. When doing everything within angular, angular will update the DOM for us as needed.

如果我们有复杂的DOM操作代码,听起来我们需要一个指令来处理它. 然后,将指令放在这些元素上,然后DOM将根据需要进行更新. 并获得有关此内容的详细信息,请参见以下链接和此[JSFIDDLE]

If we have complex DOM manipulation code, it sounds like we need a directive to handle this. Then we just stick the directive on these elements, and the DOM will get updated as desired. and to get deep info on this see the following link and this [JSFIDDLE]

4 与答案有关.

3-我们还应该尝试使用angular指令实现拖放日历.为此,我们使用可拖动的JqueryUI. 通过创建指令并通过"elem"传递属性.

3- We should also try to implement a drag&drop calendar with angular directives. To accomplish this, we use JqueryUI draggable. By Creating a directive and passing the attributes via "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
        });
    }
  };
})

然后我们在此处添加拖动对象

and we add drag-me here:

<li class="animate-repeat fc-event  item-element" drag-me    
ng-repeat="book in books | orderBy : sort : false  | filter: searchText as results track by book.id"   id="{{book.id}}"
 >                   

另一方面,我有一个将html绑定到它的元素.

On the other hand I have an element which I bind html to it.

在此链接上查看 vkammerer 答案

此处已修订. 工作CodePen 与指令配合使用,每次处理ng-repeat时都会处理可拖动项添加到我们的项目中过滤器已应用.

Here Revised. Working CodePen that works with directives to handle the add draggable to our items each time ng-repeat with the filter is applied.

希望它会对其他人有所帮助;)

Hope it would help someone else ;)

这篇关于将过滤器应用于外部事件时,无法再拖入全日历的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆