我正试图将事件从外部事件框拖动到完整日历中。
我在以下CodePen:中重新创建了我面临的问题
第一次,将外部事件从列表拖动到日历中效果很好。然而,当我在搜索输入文本过滤书籍中对书籍应用过滤器时,我会遇到以下问题。
以下是复制的步骤:
1-在搜索输入中查找111,该输入将过滤到唯一生成的111事件簿。
2-正如您所观察到的,将此结果事件从过滤器拖动到日历可以用得很好:但我们主要对这个案子感兴趣我们决定进行筛选,但不将任何内容拖到日历中。
3-所以现在从搜索输入中清除111过滤器作为文本外部事件框将支持所有默认事件,但这次尝试将其中一个拖动到日历冻结中。它已经不起作用了
它冻结所有外部事件,除了最后找到的过滤器表示111事件(查看事件的最后一行)唯一可以拖的。并且不再能够将其他事件拖到日历中。
4-即使我在日历中拖动了111,之后如果我试图拖动另一个它会结冰。
HTML
<!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使用过滤器重新创建项DOM。
不该做的事(正如你从我的HTML代码中看到的,我使用的是带有trackby的ng repeatbook.contents.name和据我所知在
在这种情况下angularjs不会重新获得dom。否则,如果我们使用
没有id angularjs跟踪的ng重复会导致你看到的dom复发_FOLOWING_LINK)。
我很难准确理解为什么dom是
在这种情况下,用带有过滤器的ng repeat重新创建!
我所做的一个变通方法在下面的文本中描述:
1-因此,为了使项目再次可拖动,我们必须调用对每个筛选器操作执行.draggable()。
完美的解决方案是在角度指令。但为了让事情变得更简单,我刚刚在detectEmpty,它是一个作用域函数。
2-添加ng change="detectEmpty()"以检测何时清除输入
<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.
希望它能帮助其他人;)
希望AngularJS pro也能告诉我们为什么在这种情况下要重新创建dom
更新
因为之前提出的解决方案是一个糟糕的解决方案。
通过每次应用筛选器时重新初始化外部事件:
$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等等,
问题是在使用带filter的ng repeat时,如何保留DOM元素?
我有一个带滤镜的ng重复。
当某些项目被过滤器过滤掉,然后它们是在那里的另一个过滤器更改后恢复的是为这些项创建的新DOM元素。
如果对该项进行了任何DOM操作,则该项将在之后丢失项目被隐藏并使用筛选器还原。
有没有一种方法可以保留DOM元素,即使项目被过滤器删除了?
我试着使用跟踪链接,但它没有帮助。
1-所以要观察DOM的变化并确认这一点。您可以使用以下链接并使用Basheer AL-MOMAANI 的服务
2-问题的根源是我试图在角度之外做这件事。在angular中执行所有操作时,angular将根据需要为我们更新DOM。
如果我们有复杂的DOM操作代码,听起来我们需要一个指令来处理它。然后,我们只需将指令粘贴在这些元素上,DOM就会根据需要进行更新。要获得更多信息,请参阅以下链接和此[JSFIDDLE]
4与答案有关。
3-我们还应该尝试实现一个拖拽&删除带有角度指令的日历。为了实现这一点,我们使用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
});
}
};
})
我们在这里添加drag me:
<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绑定到它
查看此链接上的vkammerer答案
我们添加了compile="book.contents.name">
此处已修订。使用CodePen,它与指令一起工作,以在每次应用带过滤器的ng重复时处理向我们的项目添加可拖动项。
希望它能帮助其他人;)