Draggable and droppable items with multiple lists and orders - javascript

I am trying to find the order of the items as they are dragged over to the new column. I am updating the column that the item lives in once its dragged over with AJAX. I am also getting everything in order with $(this).sortable('serialize'). when I put that into an alert. The problem I am having though is when I send the array to PHP, one of the items gets dropped in the list. I am guessing it has something to do with the way I am using serialize but I am not sure. Any help I can get would be greatly appreciated. Co-workers tell me I should just accept the limits of the code and live with it. I disagree and know that the order the item is placed is almost as important as what column the data lives in. I'm thinking I need two different events in the javascript. One for the drag between lists and one in case the user rearranges items that are in the div. Hoping someone can point me in the right direction.
The HTML I have
<div class="col-lg-12 text-center">
<div class="col-md-3">
<h3>Unsorted Items</h3>
<ul id="UNSORTED" class="connectedSortable">
<li id="item-1">Unsorted item 1 from DB</li>
<li id="item-2">Unsorted item 2 from DB</li>
<li id="item-3">Unsorted item 3 from DB</li>
<li id="item-4">Unsorted item 4 from DB</li>
<li id="item-5">Unsorted item 5 from DB</li>
</ul>
</div>
<div class="col-md-3">
<h3>ACCEPTED</h3>
<ul id="ACCEPTED" class="connectedSortable">
<li id="item-6">Unsorted item 6 from DB</li>
<li id="item-7">Unsorted item 7 from DB</li>
<li id="item-8">Unsorted item 8 from DB</li>
</ul>
</div>
<div class="col-md-3">
<h3>REJECTED</h3>
<ul id="REJECTED" class="connectedSortable">
<!-- empty to show drag to -->
</ul>
</div>
</div>
The Javascript
<script>
$(function() {
$( "#UNSORTED, #ACCEPTED, #REJECTED" ).sortable({
connectWith: ".connectedSortable",
receive: function(event, ui) {
// The position where the new item was dropped
var newIndex = ui.item.index();
var sender = ui.sender.context.id;
var receiver = this.id;
var idNum = ui.item.context.id;
var display_order = $(this).sortable('serialize');
//this alerts the correct order
alert(display_order);
//this when uncommented alerts what item tranfered to and from
//alert(idNum + ' Was Transfered from "' + sender + '" to "' + receiver + '".');
//this tell the new order of the items the item was dragged to
//alert(receiver + ' Order is ' + $(this).sortable('serialize'));
var action = 'update_selection';
$.ajax({
url: "index.php?action=" + action + "&item=" + idNum + "&selection=" + receiver + '&item-' + display_order,
success:function (data) {
$("#results").html(data).slideDown('2000');
}
});
},
stop: function (event, ui) {
var sender = this.id;
var data = $(this).sortable('serialize');
//this when uncommented alerts new order of old list
//alert(sender + ' Order is ' + data);
//this was to write new order of old list unless I can figure out how to write it in the 'receive' event
/*$.ajax({
data: oData,
type: 'POST',
url: '/your/url/here'
});*/
}
}).disableSelection();
});
</script>
Shortened version of PHP
$item_id = filter_input(INPUT_GET, 'item');
/*the number after item- is dynamic from the DB and I was unable to get serialize to work without the item- in it so now I am removing item- to get the actual DB id with preg_replace */
$item_id = preg_replace('/^item-/', '', $item_id);
$selection = filter_input(INPUT_GET, 'selection');
//can't use filter_input on an array
$display = $_GET['item'];
/*this dumps the array with an array item missing. Sometimes its the first item in the array and sometimes its not */
var_dump($display);

Okay I figured it out. I needed to pass the AJAX with POST and not GET. BUT, I was still having a problem with the way I was doing it because I cannot do it the way I was doing it. I was intending on having a table that just had the display orders for each column. But that's pretty dumb when in the item table there is already a column name that the item belongs to. Its easier to just add another column with display order that update whenever the item is moved to another column. So here is my full working code which updates with AJAX and remembers where in the new column the item was placed. If anyone ever comes across this post and knows a better way, please do share. I love learning from my mistakes.
The HTML
<div class="col-lg-12 text-center sortable">
<div class="col-md-3">
<h3>Unsorted Items</h3>
<!--I am not including the PHP loop to display the list of items -->
<ul id="UNSORTED" class="sort-list">
<li id="item-1">Unsorted item 1 from DB</li>
<li id="item-2">Unsorted item 2 from DB</li>
<li id="item-3">Unsorted item 3 from DB</li>
<li id="item-4">Unsorted item 4 from DB</li>
<li id="item-5">Unsorted item 5 from DB</li>
</ul>
</div>
<div class="col-md-3">
<h3>ACCEPTED</h3>
<ul id="ACCEPTED" class="sort-list">
<li id="item-6">Unsorted item 6 from DB</li>
<li id="item-7">Unsorted item 7 from DB</li>
<li id="item-8">Unsorted item 8 from DB</li>
</ul>
</div>
<div class="col-md-3">
<h3>REJECTED</h3>
<ul id="REJECTED" class="sort-list">
<!-- empty to show drag to -->
</ul>
</div>
</div>
The Javascript
<script>
$(function(){
/* Sort steps */
$('.container').sortable({
axis: "y",
update: function (event, ui) {
var data = $(this).sortable('toArray');
$("#result").html("JSON:<pre>"+JSON.stringify(data)+"</pre>");
}
});
/* Here we will store all data */
var myArguments = {};
function assembleData(object,arguments)
{
var data = $(object).sortable('toArray'); // Get array data
var step_id = $(object).attr("id"); // Get step_id and we will use it as property name
var arrayLength = data.length; // no need to explain
/* Create step_id property if it does not exist */
if(!arguments.hasOwnProperty(step_id))
{
arguments[step_id] = new Array();
}
/* Loop through all items */
for (var i = 0; i < arrayLength; i++)
{
var image_id = data[i];
/* push all image_id onto property step_id (which is an array) */
arguments[step_id].push(image_id);
}
return arguments;
}
/* Sort images */
$('.sort-list').sortable({
connectWith: '.sort-list',
//leaves out the bootstrap class
items : ':not(.col-md-3)',
/* That's fired first */
start : function( event, ui ) {
myArguments = {}; /* Reset the array*/
},
/* That's fired second */
remove : function( event, ui ) {
/* Get array of items in the list where we removed the item */
myArguments = assembleData(this,myArguments);
},
/* That's fired thrird */
receive : function( event, ui ) {
/* Get array of items where we added a new item */
myArguments = assembleData(this,myArguments);
},
update: function(e,ui) {
if (this === ui.item.parent()[0]) {
/* In case the change occures in the same container */
if (ui.sender == null) {
myArguments = assembleData(this,myArguments);
}
}
},
/* That's fired last */
stop : function( event, ui ) {
/* Send JSON to the server */
var action = 'update_selection';
var orders = JSON.stringify(myArguments);
$.ajax({
url: 'index.php',
type: 'POST',
data: {action: action, code: orders},
//I used success function to var_dump PHP when testing
success:function (data) {
$("#result").html(data).slideDown('2000');
}
});
}
});
});
</script>
and last but not least the PHP file. I use MVC so I am calling the action and sending my script to the right case to process the PHP (Just in case someone reading this is unaware I will include that whole PHP file.)
require("classes/Db.class.php");
$db = new Db();
if (isset($_POST['action'])) {
$action = $_POST['action'];
} else if (isset($_GET['action'])) {
$action = $_GET['action'];
} else {
$action = 'home';
}
switch ($action) {
case 'home':
//gets all items and display order for those items
$unsorted = $db->query("SELECT * FROM sorting WHERE column_name = 'UNSORTED' ORDER BY display_order");
$accepted = $db->query("SELECT * FROM sorting WHERE column_name = 'ACCEPTED' ORDER BY display_order");
$rejected = $db->query("SELECT * FROM sorting WHERE column_name = 'REJECTED' ORDER BY display_order");
$possible = $db->query("SELECT * FROM sorting WHERE column_name = 'POSSIBLE' ORDER BY display_order");
include_once('home.php');
break;
case 'update_selection':
$json = filter_input(INPUT_POST, 'code'); //gets the json stringify
$array = json_decode($json, true); //specify an associative array instead of an object from json_decode
foreach($array as $key => $value){
//gets column item belongs to now
$column_name = $key;
foreach($value as $key => $number){
//this gets the key which we will use for ordering
$order = $key;
//update DB with column name item belongs to and the new order of all items in that column
$db->query("UPDATE sorting SET column_name = :column_name, display_order = :order WHERE gun_id = :number", array("column_name"=>$column_name, "number"=>$number, "order" => $order));
}
}
break;

Related

Getting and setting attributes with Kendo sortable on change event

Would just like to get and set different attributes when a list item is moved to the other list on my mvc page... I can't access anything so far. The problem is in the javascript, it hits the onchange event fine. I didnt post the "available list" in the cshtml for brevity. this is what the console.log as the bottom reads:
SpecialNeedInActiveList change: Meds per tube newIndex: -1 oldIndex: 5 action: remove
SpecialNeedActiveList change: Meds per tube newIndex: 3 oldIndex: -1 action: receive
Any help would be appreciated, this has taken way too long for me.
HTML:
<div class="col-md-6" id="SpecialNeedContainerLL">
<ul id="SpecialNeedActiveList" class="col-md-6">
#if (Model.SelectedSpecialNeeds.Any())
{
foreach (var y in Model.SelectedSpecialNeeds)
{
<li class="list-item" selected-personspecialneed="#y.PersonSpecialNeedId" selected-need-type="#y.SpecialNeedTypeId"> #y.SpecialNeedDescription </li>
}
}
</ul>
</div>
#(Html.Kendo().Sortable()
.For("#SpecialNeedActiveList")
.ConnectWith("#SpecialNeedInActiveList")
.PlaceholderHandler("placeholder")
.Events(events => events.Change("onChange"))
)
Javascript:
function onChange(e) {
var id = e.sender.element.attr("id"),
text = e.item.text(),
newIndex = e.newIndex,
oldIndex = e.oldIndex;
if (id == 'SpecialNeedActiveList' && newIndex > -1) {
//add item to selected list
//remove item from availables list
/*NONE of the following works...*/
//var oldPersonSpecialNeedId = e.sender.element.getAttribute('available-personspecialneed');
//var oldSpecialNeedTypeId = e.sender.element.getAttribute('available-need-type');
//e.sender.element.removeAttribute('available-personspecialneed');
//e.sender.element.removeAttribute('available-need-type');
//e.sender.element.setAttribute('selected-personspecialneed', oldPersonSpecialNeedId);
//e.sender.element.setAttribute('selected-need-type', oldSpecialNeedTypeId);
}
console.log(id + " change: " + text + " newIndex: " + newIndex + " oldIndex: " + oldIndex + " action: " + e.action);
}
When you are in the change event handler(onChange()), e.sender.element is NOT the item that was dragged, it is the list that sent the change event, the <ul> element.
The item being drag/dropped is contained in the e.item field, which you should be able to manipulate as normal, for example using jQuery(but you may use whatever DOM manipulation technique you like):
var $item = $(e.item);
var oldPersonSpecialNeedId = $item.attr('selected-personspecialneed');
var oldSpecialNeedTypeId = $item.attr('selected-need-type');
$item.attr("selected-personspecialneed", "new" + oldPersonSpecialNeedId);
$item.attr("selected-need-type", "new" + oldSpecialNeedTypeId);
Here's a working example showing the attributes being changed:http://dojo.telerik.com/#Stephen/arUpO
It is based on single list from the code in your question rather than 2 lists but it simply demonstrates how to access the dragged element from the sortable change event, which is the core of your problem.
Having said that, I would probably investigate using a DataSource-bound list so that you can manipulate fields of a model instead of attributes of a DOM element. http://demos.telerik.com/aspnet-mvc/sortable/integration-listview is a good place to start.

Prevent animation of list items on div refresh every second

I have a page which lists files from database every second.
DESIRED RESULTS
I want all the li elements to fadeIn one by one on page load.
Then fadeIn only the new li elements when new files are added to the database otherwise no animation to the already listed elements.
To get the fresh list of files I am using setInterval which in turn is causing the all the list items to fadeIn one by one every second. What should I do to achieve the desired results?
TO SUM UP WHAT I AM ASKING
I am asking for a way to show the elements as if they are fading in on by on on page load. Then if new items are added to the database then show them only one by one with the fading effect but at that time the fading effect should not be applied to the older items.
JS
$(document).ready(function() {
var filelist = $('#file_list');
$('#loading').show();
var checkUpdate = function () {
$.ajax({
type: "GET",
url: "generate_list.php",
success: function (result) {
$('#loading').hide();
var arr = JSON.parse(result);
console.log(arr);
if (arr.length > 0) {
$('#empty_storage').hide();
filelist.html(arr);
$('li').each(function (i) {
$(this).delay(400*i).fadeIn(400);
});
}
else {
filelist.html('');
$('#empty_storage').show();
}
},
error: function (response) {
$('#loading').hide();
$.alert({
theme: 'black',
title: false,
content: response
});
}
});
setTimeout(checkUpdate, 1700);
};
setTimeout(checkUpdate, 1700);
});
CSS
#file_list li
{
overflow: hidden;
padding-top: 4px;
padding-bottom: 4px;
margin-bottom: 5px;
display: none;
}
PHP SNIPPET
// Fetching only last 10 records LIFO
$query = $dbh->prepare("SELECT * FROM uploads ORDER BY id DESC LIMIT 10");
$query->execute();
$items = array();
while ($row = $query->fetch(PDO::FETCH_ASSOC)) {
// Generate respective data to display
$file_id = $row['id'];
$ico_path = $base_icon_src.get_logo($row['file_ext']);
$full_file_name = $row['file_name'].'.'.$row['file_ext'];
$file_ext = $row['file_ext'];
$file_code = $row['file_code'];
$download_file_path = './storage/'.$file_code.'.'.$file_ext;
$file_size = $row['file_size'];
$file_upload_time = $row['timestamps'];
if(file_exists($download_file_path)) {
// Generating markup
$items[] = '<li>
<div class="f_icon"><img src="' . $ico_path . '"></div>
<div class="left_wing">
<div class="progressbar"></div>
<a class="download_link" href="#" id="'.$file_id.'"><div class="f_name">' . $full_file_name . '</div></a>
<div class="f_time_size">' . date("M d, Y", $file_upload_time) . ' • ' . human_filesize($file_size) . '</div>
</div>
<div class="right_wing">
<div class="f_delete">
<a class="btn btn-danger" href="#" aria-label="Delete" data-id="'.$file_id.'" data-filename="'.$full_file_name.'">
<i class="fa fa-trash-o fa-lg" aria-hidden="true" title="Delete this?"></i>
</a>
</div>
</div>
</li>';
}
}
//content in $items must be in UTF-8
echo json_encode($items);
In general - this is the workflow you should follow:
On page load (first time) get the list of the files you want to preview (it can be all of them, or only the last X). Make sure you have the id (from the database) of the last item (the largest id, if your items are orders). We will call this MAX_ID.
Once loaded - animate them one by one.
Using setTimeout, get the "next bulk" of files (you need to pass to the php script the ID that you saved from #1, so you can get only the files where id > max_id).
If you got new files - add them (you can animate them one by one).
Update the MAX_ID to be the new id you got from the "new bulk".
Inside the success function you should call again (using the setTimeout function to the code the gets the "next bulk" (#3).
Here is a simple example:
$(function() {
$('li').hide();
$($("li").get().reverse()).each(function(i) {
$(this).delay(400*i).fadeIn(400);
});
});
items = ['a', 'b', 'c', 'd']
$('#a1').click(function() {
$(items).each(function(i, val) {
li = $('<li>').text(val).hide();
$('#u1').prepend(li);
li.delay(400*i).fadeIn(400);
});
});
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id="u1">
<li>5</li>
<li>4</li>
<li>3</li>
<li>2</li>
<li>1</li>
</ul>
<div id="a1">Click to add more items</div>

.show() not working jquery

I have a filter class that filters products residing inside the page.There is a function that works if someone checks the filters.
Filters are check-boxes where every check box contains different value.
What my filter function do is that it checks all the checked checkboxes from the page and then uses the data-* global variable present on the list tages to decide what element to show.
DOM structure of the items will be:
<body>
<ul class="products">
<li class="product" data-company="something" data-flavour="something"></li>
<li class="product" data-company="something" data-flavour="something"></li>
.
.
.
.
<li class="product" data-company="something" data-flavour="something"></li>
</ul>
</body>
Below one shows the the function that does the job.
this.filterGridProducts = function() {
$.ajax({
type: 'POST',
url: 'test12.php',
data: category,
success: function(data) {
$('#limitpage').html(data);
// $('.product').hide();
var filteredProducts =$('.product');
//filter by colour, size, shape etc
var filterAttributes = $('input[type="checkbox"]');
var selectedFiltersValues = [];
// for each attribute check the corresponding attribute filters selected
filterAttributes.each(function() {
if (this.checked) {
var currentFilter = $(this);
selectedFiltersValues.push("[data-" + currentFilter.attr('name') + "='" + currentFilter.val() + "']");
filteredProducts = filteredProducts.filter(selectedFiltersValues.join(','));
}
});
filteredProducts = filteredProducts.filter(function() {
return ($(this).attr('data-price') > first && $(this).attr('data-price') <= second);
});
//console.log($('.product').show());
filteredProducts.each(function(e) {
console.log($(this).html().show()); // not working
$(this).show(); // not working
})
filteredProducts.show(); // this one is not working as well.
}
});
};
filteredProducts does contain the elements that need to be filtered but I can't seem to show it.
Ajax call in the above functions loads all the elements present inside the db, The products that come from it are all hidden.
What could be the possible reason for the .show() to not work?
Update:

sort jquery result alphabetically

I am trying to make a dynamic list of my blog posts. I need the list to be displayed alphabetically. The current code is working okay, but gave me a chronological list. How can I arrange my list alphabetically. Current code is given below. It is for blogger blog and I used kimonolabs to make the API used in this code. The feed is in jason. (In blog page area I first created a blank html list and then used below code to insert data. Html is also given.) What should I do to make the result alphabetical.
jQuery.ajax({
"url":"https://www.kimonolabs.com/api/djwmp1p8?apikey=P1DP0fILX0ou5GnXR6DRbbRmkFuQNC0G",
"crossDomain":true,
"dataType":"jsonp",
//Make a call to the Kimono API following the "url"
'success': function(response){
// If the call request was successful and the data was retrieved, this function will create a list displaying the data
jQuery(".panel-heading").html(response.name);
//Puts the API name into the panel heading
var collection = response.results.collection1;
for (var i = 0; i < collection.length; i++){
// Traverses through every element in the entire collection
jQuery(".list-group").append('<li class="list-group-item">' +'<a href='+collection[i].property1.href +'>'+ collection[i].property1.text + '</a>' +'</li>');
// adds the text and the links from the first property into the list
}
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<div class="container padding">
<div class="panel panel-info">
<div class="panel-heading"></div>
<ol class="list-group">
</ol>
</div>
</div>
As response.results.collection1 is array, and you want it to order in alphabetical order, you need to sort by each item's property1.text:
collection.sort(function(item1, item2) {
return item1.property1.text > item2.property1.text ? 1 : -1;
});
jQuery.ajax({
"url":"https://www.kimonolabs.com/api/djwmp1p8?apikey=P1DP0fILX0ou5GnXR6DRbbRmkFuQNC0G",
"crossDomain":true,
"dataType":"jsonp",
//Make a call to the Kimono API following the "url"
'success': function(response){
// If the call request was successful and the data was retrieved, this function will create a list displaying the data
jQuery(".panel-heading").html(response.name);
//Puts the API name into the panel heading
var collection = response.results.collection1;
// VVVV Sort it by item.property1.text before print out.
collection.sort(function(item1, item2) {
// If item1.property1.text's alphabetical order is larger than item2's return 1, otherwise return 0.
return item1.property1.text > item2.property1.text ? 1 : -1;
//return item1.property1.text.localeCompare(item2.property1.text) > 0 ? 1 : -1;
});
for (var i = 0; i < collection.length; i++){
// Traverses through every element in the entire collection
jQuery(".list-group").append('<li class="list-group-item">' +'<a href='+collection[i].property1.href +'>'+ collection[i].property1.text + '</a>' +'</li>');
// adds the text and the links from the first property into the list
}
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<div class="container padding">
<div class="panel panel-info">
<div class="panel-heading"></div>
<ol class="list-group">
</ol>
</div>
</div>
http://jsfiddle.net/03f1ehsf/
collection.sort(function(a,b){ return b.property1.text>a.property1.text?0:1});

$scope.apply not working in Angular

im trying to learn Angular.
Here is what im trying to do:
I am building an App that shows me citys. When i click on a city i want to see a list of all my favourite citys.
Using an "Show-List" Button with ng-click works but requires the button the be pushed.
Here is my approach for getting it done automatic:
I want a list in my DOM automatically updated on change of the list.
$scope.$watch('updatedList', function() {
// CHECK IF WORKS
console.log($scope.updatedList);
// APPLY TO DOM
$timeout(function(){
$scope.$apply(function () {
$scope.watchList = $scope.updatedList;
});
}, 1000)
});
The Console shows no error and gives out the correc values:
Object {city.3: "Herat", city.7: "Haag", city.10: "Tilburg" ......}
In my div is the following:
<ul>
<li ng-repeat="y in updatedList">{{ y }}</li>
</ul>
<ul>
<li ng-repeat="a in watchList">{{ a }}</li>
</ul>
First for the NG-Click-Version(which works on click) second for the $scope.$watch
Sorry for lots of questions but im really struggling with the Angular-Docs.
EDIT:
Function that Adds Citys to the List:
$scope.addToList = function(name,id) {
var cityToAdd = name;
var cityToAddID = id;
// ADD A CITY TO THE COOKIE -> WORKS
$cookies.put('city.' + cityToAddID, cityToAdd);
$scope.newList = $cookies.getAll();
$scope.addToListMessage = cityToAdd + " wurde hinzugefügt";
// Show short INFONOTICE
window.setTimeout(function() {
$scope.$apply(function() {
$scope.addToListMessage = "";
});
}, 1000);
// Update the List
$scope.updateList();
};
Second Functions -> gets Values from Cookies and puts them to an Array:
$scope.updateList = function() {
var allCitys = $cookies.getAll();
// PUT ALL INTO AN ARRAY -> WORKS
var favouritesFromCookie = [];
$.each(allCitys, function(index, value) {
if (index.indexOf('city.') == 0) { favouritesFromCookie.push(value) }
});
// PUT THE ARRAY OF CITYS INTO A SCOPE_VARIABLE
$scope.updatedList = favouritesFromCookie;
};
Your $scope.updatedList needs to be an array to be used in ng-repeat.
You shouldn't directly write a list in expression. Try this
<ul>
<li ng-repeat="y in watchList">{{ y.city }}</li>
<li ng-repeat="y in watchList">{{ y.yourListItem}}</li>
</ul>

Categories

Resources