Optimal way to save order of jQueryUI sortable list with Meteor - jquery-ui

I need some guidance/suggestions for an optimal way to save the order of a sortable list that takes advantage of Meteor.
The following is a scaled down version of what I'm trying to do. The application is a simple todo list. The end goal for the user is to sort their list where the data is picked up from the database. As the user sorts tasks, I would like to save the order of the tasks.
I've implemented this application without Meteor using php/ajax calls using sortable's update event that would delete the entry in the database and replace it with what was currently in the DOM. I'm curious to know if there are a better ways to do this taking advantage of Meteor's capabilities.
The following sample code is straight off of a live demo.
HTML:
<template name="todo_list">
<div class="todo_list sortable">
{{#each task}}
<div class="task">
<h1>{{title}}</h1>
{{description}}
</div>
{{/each}}
</div>
</template>
JS(Without the Meteor.isServer that simply populates the database.):
if (Meteor.isClient) {
//Populate the template
Template.todo_list.task = function () {
return Tasks.find({});
};
//Add sortable functionality
Template.todo_list.rendered = function () {
$( ".sortable" ).sortable();
$( ".sortable" ).disableSelection();
};
}
Sample data (Output of Tasks.find({})):
[{
title:"CSC209",
description:"Assignment 3"
},
{
title:"Laundry",
description:"Whites"
},
{
title:"Clean",
description:"Bathroom"
}]

You'd probably want to first sort your items by a new field on you collection then, you'll want to hook into the jQuery sortable update event:
if (Meteor.isClient) {
// Populate the template
Template.todo_list.task = function () {
return Tasks.find({}, { sort: ['order'] });
};
// Add sortable functionality
Template.todo_list.rendered = function () {
$('.sortable').sortable({
update: function (event, ui) {
// save your new list order based on the `data-id`.
// when you save the items, make sure it updates their
// order based on their index in the list.
some_magic_ordering_function()
}
});
$( ".sortable" ).disableSelection();
};
}
You template would look a bit like this:
<template name="todo_list">
<div class="todo_list sortable">
{{#each task}}
<div class="task" data-id="{{_id}}">
<h1>{{title}}</h1>
{{description}}
</div>
{{/each}}
</div>
</template>
And when that event is triggered, it would determine the order of the list and save the new order in the documents for the collection.
This isn't really a complete answer, but hopefully it helps a bit.

Related

How to show multiple entries in multiple bootstrap cards?

I want to show my tasks like this in bootstrap cards -
Picture for reference
Code that I'm using right now -
HTML
<div class="card" id="todo"></div>
AJAX
var data = JSON.parse(httpRequest2.response);
console.log(data);
$.each(data, function(index, itemData) {
$("#todo").append('<div class="row"><div class="col-9">'+
itemData.Todo.task+
'</div></div></div>'+
'<div class="col-3"><p>'+
Some Data
</div>'
);
});
It is showing multiple to-do in one card. How can I show one to-do in one card?
Please help!
The problem is that you add all the todo in one card (div#todo). So what you need to do is juste to make a div that will contains the card.
<div id="todos"></div>
const data = JSON.parse(httpRequest2.response);
console.log(data);
$.each(data, function(index, itemData) {
$("#todos").append('<div class="card"><div class="row"><div class="col-9">'+
itemData.Todo.task+
'</div></div></div>'+
'<div class="col-3"><p>'+
Some Data
</div></div>'
);
});
It should work as you want. And you can add css to improve style.

jQuery UI tooltip on pseudo disabled elements

I would like to show a tooltip on a text input that has a ui-state-disabled class.
I took a peek to the tooltip source code and I couldn't find something that checks against that particular class. So I don't know why it won't show.
As far as I can tell, the elements aren't disabled per se, they just have a class applied to them.
So, how can I show a tooltip on elements that have that class? I don't want to use a wrapper or anything like that. Maybe extending through widget factory...
Here's a sample code
HTML
<input name="#1" class="text" data-tooltip="message A">
<input name="#2" class="text" data-tooltip="message B">
<br>
<button id="disable">disable input #2</button>
<button id="enable">enable input #2</button>
JS
$(".text").each(function()
{
$(this).tooltip({
content: $(this).data("tooltip"),
items: ".text"
});
});
$("#disable").click(function()
{
$("input[name='#2']").addClass("ui-state-disabled");
});
$("#enable").click(function()
{
$("input[name='#2']").removeClass("ui-state-disabled");
});
FIDDLE: https://jsfiddle.net/hn1o4qs2/
See the doc (http://api.jqueryui.com/tooltip/):
In general, disabled elements do not trigger any DOM events.
Therefore, it is not possible to properly control tooltips for
disabled elements, since we need to listen to events to determine when
to show and hide the tooltip. As a result, jQuery UI does not
guarantee any level of support for tooltips attached to disabled
elements. Unfortunately, this means that if you require tooltips on
disabled elements, you may end up with a mixture of native tooltips
and jQuery UI tooltips.
Solution with wrapper
Your HTML:
<span class="input-container" data-tooltip="message A">
<input name="#1" class="text">
</span>
<span class="input-container" data-tooltip="message B">
<input name="#2" class="text">
</span>
<br>
<button id="disable">
disable input #2
</button>
<button id="enable">
enable input #2
</button>
Your Javascript
$(".input-container").each(function()
{
$(this).tooltip({
content: $(this).data("tooltip"),
items: ".input-container"
});
});
// ... The rest is the same
Solution with fake disabled-property
Here you can use a readonly attribute and a custom class for disabled input.
Playground: https://jsfiddle.net/5gkx8qec/
As I've stated in my question, I needed to get this working without adding a container or anything like that. And I was willing to extend the widget somehow...
So I read the source code more carefully and searched throught the whole repository for ui-state-disabled, and found that in widget.js there is an _on() method that at some point performs a check against that class and a flag called suppressDisabledCheck
A comment in code says
// Allow widgets to customize the disabled handling
// - disabled as an array instead of boolean
// - disabled class as method for disabling individual parts
This was very important, it gave me the clue that this check could be overriden. So a quick search in google and the widget factory had the answer:
Automatically handles disabled widgets: If the widget is disabled or
the event occurs on an element with the ui-state-disabled class, the
event handler is not invoked. Can be overridden with the
suppressDisabledCheck parameter.
So basically I did this:
$.widget("ui.tooltip", $.ui.tooltip,
{
options: {
allowOnDisabled: false
},
_on: function()
{
var instance = this;
this._super(instance.options.allowOnDisabled, {
mouseover: "open",
focusin: "open",
mouseleave: "close",
focusout: "close"
});
}
});
And then used it like this:
$(".text").each(function()
{
$(this).tooltip({
allowOnDisabled: true,
content: $(this).data("tooltip"),
items: ".text"
});
});
EDIT 2022-09-15
I was having some trouble with this implementation, so I've changed it a little bit
$.widget("ui.tooltip", $.ui.tooltip,
{
options: {
allowOnDisabled: false
},
_create: function()
{
this._super();
var instance = this;
this._on(instance.options.allowOnDisabled, {
mouseover: "open",
focusin: "open",
mouseleave: "close",
focusout: "close"
});
}
});

Knockout Binding Not Working with jQueryUI Dialogue

My viewModel has an array called 'Items'. I want to display the contents of 'Items' using a foreach binding. Everything works fine when I use regular HTML. But does not work with a dialogue box which I created using jQueryUI.
HTML:
<div id="skus0">
<div id="skus1">
<ul data-bind="foreach: Items">
<li data-bind="text:Name"></li>
</ul>
</div>
<input type="button" id="openQryItems" class="btn btn-info" value="Open" data-bind="click:openQueryItems" />
</div>
JavaScript:
// my view model
var viewModel = {
Items: [{Name:'Soap'},{Name:'Toothpaste'}]
};
// JS to configure dialogue
$("#skus1").dialog({
autoOpen: false,
width: 500,
modal: true,
buttons: {
"OK": function () {
$(this).dialog("close");
},
"Cancel": function () {
$(this).dialog("close");
}
}
});
// for mapping my model using ko.mapping plugin
var zub = zub || {};
zub.initModel = function (model) {
zub.cycleCountModel = ko.mapping.fromJS(model);
zub.cycleCountModel.openQueryItems = function () {
$("#skus1").dialog("open");
}
ko.applyBindings(zub.cycleCountModel, $("#skus0")[0]);
}
zub.initModel(viewModel);
I have created a fiddle here my fiddle
$.fn.dialog removes the element from its place in the DOM and places it in a new container; this is how it can create a floating window. The problem with this happening is that it breaks data binding, since the dialog DOM is no-longer nested within the top-level data-bound DOM.
Moving the dialog initialization to after ko.applyBindings will enable dialog to yank stuff out of the DOM after the list is populated. Of course, this means that after that point, future changes will still not be reflected, which may be important if you're wanting the opened dialog to change automatically.
If you are wanting the dialog contents to be fully dynamic, you could create a binding handler; we did this in our project. Here's a rough outline of how we did this:
ko.bindingHandlers.dialog = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingCtx) {
var bindingValues = valueAccessor();
var hasAppliedBindings = false;
var elem = $(element);
var options = {
id: ko.utils.unwrapObservable(bindingValues.id),
title: ko.utils.unwrapObservable(bindingValues.title),
// etc...
onOpen: function () {
if (!hasAppliedBindings) {
hasAppliedBindings = true;
var childCtx = bindingCtx.createChildContext(viewModel);
ko.applyBindingsToDescendants(childCtx, element);
}
}
};
elem.dialog(options);
}
return { controlsDescendantBindings: true };
}
...which we used like this:
<div data-bind="dialog: { title: 'some title', id: 'foo', ... }">
<!-- dialog contents -->
</div>
What return { controlsDescendantBindings: true } does is makes sure that outer bindings do not affect anything using the dialog binding handler. Then we create our own Knockout binding "island" after it is pulled out of the DOM, based on the original view model.
Although in our project we also used hybrid jQuery+Knockout, I would highly recommend you avoid this whenever possible. There were so many hacks we had to employ to sustain this type of application. The very best thing you should do is prefer Knockout binding handlers (and I think it has a "component" concept now which I haven't played with) over DOM manipulations to avoid buggy UI management.

Having trouble with jQueryUI accordion and Knockoutjs

I've been able to replicate the problem here: http://jsfiddle.net/NE6dm/
I have the following HTML which I'm using in an app:
<div data-bind="foreach: items, jqAccordion: { active: false, collapsible: true }">
<h3>
</h3>
<div>
hello
</div>
</div>
<button title="Click to return to the complaints list." data-bind="click: addItem">Add Item</button>
The idea is to display an accordion for a bunch of items that will dynamically be added/removed via a Knockout observable array.
Here's some JavaScript code which I use:
// Tab.
var tab = function (questionSet) {
this.id = questionSet.code;
this.title = questionSet.description;
this.questionSet = questionSet;
};
Custom Knockout binding handler:
ko.bindingHandlers.jqAccordion = {
init: function (element, valueAccessor) {
var options = valueAccessor();
$(element).accordion(options);
$(element).bind("valueChanged", function () {
ko.bindingHandlers.jqAccordion.update(element, valueAccessor);
});
},
update: function (element, valueAccessor) {
var options = valueAccessor();
$(element).accordion('destroy').accordion(options);
}
};
var NonSequentialViewModel = function () {
var items = ko.observableArray();
items.push(new tab({ id: 23, description : 'Added Inline' }));
var addItem = function() {
items.push(new tab({ id: 5, description: 'Added by a click' }));
};
return {
addItem: addItem,
items: items
}
}
var nonsequentialViewModel = new NonSequentialViewModel();
ko.applyBindings(nonsequentialViewModel);
Now the problem is this - when I view the HTML page, the item 'Added Inline' appears fine, in that I can collapse and expand it. However, when I click the button 'Add Item', a new item is added to he accordion, but it has not styling at all. For example:
In the above image, the first item is styled correctly, however the remaining items have none of the jQuery UI styling applied. Basically, any item which is added dynamically does not have any accordion styling applied.
I have seen this question
knockout.js and jQueryUI to create an accordion menu
and I've tried using the jsFiddle included in the question, but I cannot see why my code doesn't have the same result.
I'm hoping someone else has experienced this before and can help.
EDIT:
I've looked into this further and see that the problem is this - when I add a new item to the oservable array, the custom handler's update method is not executed. Thus the redrawing of the accordion never happens.
I can't see why the update should not be called. This is realyl doing my head in! :)
EDIT:
I've been able to replicate the problem here: http://jsfiddle.net/NE6dm/
Your NonSequentialViewModel constructor doesn't return items array. Update return statement to this:
return {
items: items,
addItem: addItem
}
Here is working fiddle: http://jsfiddle.net/vyshniakov/MfegM/323/
Old question, but I believe I was having the same problem.
I may need to submit a bug to knockout.js. I just spend several hours trying to figure out similar problems.
In short... if I load your jsfiddle and change the version of knockout to 2.1.0, it appears to work fine.
this:
<script type="text/javascript" src="http://cloud.github.com/downloads/SteveSanderson/knockout/knockout-2.2.0.debug.js"></script>
to this:
<script type="text/javascript" src="http://cloud.github.com/downloads/SteveSanderson/knockout/knockout-2.1.0.debug.js"></script>
(The only difference being the version 2.2.0 --> 2.1.0)
Further... I ended up settling on several versions:
jquery: 1.9.1
jquery-ui (combined): 1.9.2
knockoutjs: 2.1.0

jQuery - Append multiple elements one after the other

I'm looking to create a nice effect when appending multiple paragraphs to a div. When I append I want to fadeIn the paragraphs one at a time so one shows after the other. At the moment they all fade in at the same time.
example html;
<div class="wrapper">
<p class="para">paragraph 1</p>
<p class="para">paragraph 2</p>
<p class="para">paragraph 3</p>
</div>
Here is the code used to append to the div
$(results).prependTo(".wrapper").hide().fadeIn('slow');
(Results) is simply the multiple paragraphs.
Thanks in advance for your help and advice.
You could try something like this:
$(results).bind('appear', function(){ // bind a custom event
$(this).fadeIn('slow', function(){
$(this).next('p.para').trigger('appear'); // recurse
});
})
.prependTo('wrapper')
.end() // back to "results"
.hide() // not necessary if already hidden by style rule
.first().trigger('appear'); // start the cascade
Here's an example: http://jsfiddle.net/redler/CDbEn/
You could use queue() for this:
$('p').prependTo("#wrapper").hide().each(function() {
var element = $(this);
$('#wrapper').queue(function() {
setTimeout(function() {
element.fadeIn('slow');
$('#wrapper').dequeue();
}, 700);
});
});
Example.
//assuming resuls is an array of strings containing your p's
var appear = function(index) {
if (results[index])
$(results[index]).prepend('.wrapeer').hide().fadeIn('slow', function(){ appear(index+1); });
}
What about
$('.para').each(function(index){
$(this).hide().prependTo('.wrapper').delay(500 * index).fadeIn('slow');
});

Resources