Linked jQuery sortable lists and Backbone collections - jquery-ui

I'm still finding my way with Backbone and I've always use Prototype instead of jQuery in the past so please forgive me if I'm doing something stupid.
I'm trying to develop a UI containing several connected unordered lists where each sortable list is represented by a separate Backbone collection. I'm using ICanHaz and Mustache templates but that's not of importance for my question.
When dragging items between the lists, how would I best achieve the automatic updating of the collections (remove a model from one and insert it into another)? I'm currently trying to use the receive and remove methods in the jQueryUI Sortable interaction — am I at least on the right lines?
var WS = {};
(function(ns) {
ns.Item = Backbone.Model.extend();
ns.Content = Backbone.Collection.extend({
model: ns.Item,
url: location.href,
initialize: function(el) {
this.el = $(el);
this.deferred = this.fetch();
},
recalculate: function() {
var count = this.length;
this.el.next(".subtotal").html(count);
},
setOrder: function() {
$.ajax({
url: this.url + "/reorder",
type: "POST",
data: "tasks=" + $(this.el).attr("id") + "&" + this.el.sortable("serialize")
});
}
});
ns.ContentRow = Backbone.View.extend({
tagName: "li",
className: "item",
events: {
"click .delete": "destroy"
},
initialize: function(options) {
_.bindAll(this, "render", "destroy");
this.model.bind("change", this.render);
this.model.view = this;
},
render: function() {
var row = ich.item(this.model.toJSON());
$(this.el).html(row);
return this;
},
destroy: function() {
if (confirm("Really delete?")) {
this.model.destroy({
success: function(model, response) {
$(model.view.el).remove();
},
error: function(model, response) {
console.log(response);
}
});
}
}
});
ns.ListView = Backbone.View.extend({
initialize: function(collection) {
this.el = collection.el;
this.collection = collection;
this.collection.bind("add", this.addOne, this);
_.bindAll(this, "addOne");
this.el.sortable({
axis: "y",
connectWith: ".tasks",
receive: _.bind(function(event, ui) {
// do something here?
}, this),
remove: _.bind(function(event, ui) {
// do something here?
}, this),
update: _.bind(function(event, ui) {
var list = ui.item.context.parentNode;
this.collection.setOrder();
}, this)
});
},
insert: function(item) {
var prefix = this.el.parentsUntil('ul').parent().attr("id"),
view = new ns.ContentRow({
model: item,
id: prefix + "_" + item.id
});
this.el.append(view.render().el);
},
addOne: function(item) {
if (item.isNew()) {
item.save({}, {
success: _.bind(function(model, response) {
// I should set id from JSON response when live
model.set({ id: this.collection.length });
this.insert(model);
}, this)
});
} else {
this.insert(item);
}
},
addAll: function() {
this.collection.each(this.addOne);
},
render: function() {
this.collection.deferred.done(_.bind(function() {
this.addAll();
}, this));
}
});
ns.AppView = Backbone.View.extend({
lists: [],
initialize: function(holder) {
holder.find("ul").each(_.bind(function(index, list) {
var Items = new WS.Content(list),
App = new WS.ListView(Items);
App.render();
this.lists.push(Items);
}, this));
}
});
})(WS);
$(document).ready(function() {
var App = new WS.AppView($("#tasks"));
});

You are on the right track. You will probably want to add the id of each sortable element into the template somewhere. Then when you receive the event, you know which model to add or remove from the collection. For example add...
<div data-id={{id}}> ... my thing ... </div>
And in the sortable call get the target's id attribute and call Collection.add() or remove()

Just use Backbone.CollectionView.. it has this functionality built in out of the box.
var listView = new Backbone.CollectionView( {
el : $( "#list1" ),
sortable : true,
sortableOptions : {
connectWith : "#list2"
},
collection : new Backbone.Collection
} );
var listView = new Backbone.CollectionView( {
el: $( "#list2" ),
sortable : true,
sortableOptions : {
connectWith : "#list1"
},
collection : new Backbone.Collection
} );

Related

why the function in side of function wont get called?

i have the following reactjs code to generate two dropdown list where the ddlproducts gets loaded by ddlCategories selection. but when i called the function getDataById() and tried to print the ajax populated array data2 to alert(), there was no alert() there were two alerts none of the alerts were prompted. it shown this error message on the IE console,
execution did not reached the function getDataById() 'cus the alert() in that function even didn't execute
SCRIPT438: Object doesn't support property or method 'getDataById'
correction: once the calling of this.props.getDataById() was changed to this.getDataById() it worked
but how do populate the ddlProducts dropdown. how do i access tag of the ddlProducts and then add the options to it?
here is the code:
var gdata=[];
var trStyle = {
'color': 'black',
'border-style' :'solid',
'margin-left':'20%'
};
var HCOMP = React.createClass({
getInitialState:function(){
return{data1:[], data2:[], isMounted:false, selectedValue:0}
},
componentDidMount:function(){
this.getData();
this.setState({isMounted:true})
},
ddlProdCatsChanegeEvent: function(e) {
if (this.state.isMounted)
{
var newV = ReactDOM.findDOMNode(this.refs.refProdCats).value;
var seleValue = newV;
this.setState({selectedValue:newV}, function(){
this.getDataById(this.state.selectedValue);
alert(this.state.data2);
});
}
},
render: function() {
var prodCats = this.state.data1.map(function(ele, index){// <PRODCATSOPTION optValue={ele.ProductCategoryID} optText={ele.Name} />
return <option value={ele.ProductCategoryID} data-key={index}>{ele.Name}</option>
});
prodCats.unshift(<option value={''}>{'---- select category ------'}</option>)
return (<div>Prodcut Category:<br /><select id="ddlCategories" ref="refProdCats" onChange={this.ddlProdCatsChanegeEvent}>{prodCats}</select><br />
Products:<br /><select id="ddlPorducts" ref="refProds"></select><br /></div>
)
},
getDataById:function(catId){
var x = catId;
alert('rec id:'+x);
$.ajax({
url:'http://localhost:53721//Home/GetProductCats?id='+ x,
method:'GET',
success:function(d1){
this.setState({data2:d1});
}.bind(this),
error:function(){
alert('ERROR');
}.bind(this)
})
},
getData:function(){
//ajax here
$.ajax({
url:'http://localhost:53721//Home/GetProductCats',
method:'GET',
success:function(d1){
this.setState({data1:d1});
}.bind(this),
error:function(){
alert('ERROR');
}.bind(this)
})
}
});
var PRODOPTIONS = React.createClass({
render:function(){
return(<option value={this.props.optValue}>{this.props.optText}</option> )
}
});
var PRODCATSOPTION = React.createClass({
render:function(){
return(<option value={this.props.optValue}>{this.props.optText}</option> )
}
});
ReactDOM.render( <HCOMP/>, document.getElementById('d1') );
Try updating ddlProdCatsChanegeEvent to ddlPropCatsChangeEvent.

get instance of map in jquery ui map

i want to make the markers clustered with markerClusterer but i cannot get the map instance with jquery ui map . js
tried:
var map = $('#map_canvas').gmap('getMap');
or
var map = $('map_canvas').gmap('get', 'map');
and after:
var markerCluster = new MarkerClusterer(map, allMarkers);
but with errors
Thank you
Tried this . No Errors but no clusters...
$('#map_canvas').gmap({ 'callback': function () {
var self = this;
$.getJSON('Data/markers.json', function (data) {
$.each(data.markers, function (i, marker) {
self.addMarker({ 'position': new google.maps.LatLng(marker.latitude,marker.longitude)}).click(function () {
$.ajax({
type: "GET",
url: "/LocoMap/LocoMap/InfoMobilePartialView/",
data: { latitude: marker.latitude, longitude: marker.longitude},
success: function (data) {
$("#marker-info").remove();
$(document.body).append("<div id='marker-info' data-role ='page'> </div>");
var $contentDiv = $("#marker-info");
$contentDiv.html(data).trigger('create');
$.mobile.changePage("#marker-info", { changeHash: false, type: "get", transition: 'pop',rel:"external" });
},
error: function (errorData) { onError(errorData); }
});
});
});
});
self.set('MarkerClusterer', new MarkerClusterer(this.get('map'), this.get('markers')));
}});
$('#map_canvas').gmap({'zoom': 2, 'disableDefaultUI':true}).bind('init', function(evt, map) {
$.getJSON( 'Data/markers.json', function(data) {
$.each( data.markers, function(i, m)
$('#map_canvas').gmap('addMarker', { 'position': new google.maps.LatLng(m.latitude, m.longitude), 'bounds':true } );
});
});
$('#map_canvas').gmap('set', 'MarkerClusterer', new MarkerClusterer(map,$(this).gmap('get', 'markers')));
});
with no errors and no clusters
it seems **$(this).gmap('get', 'markers')));** returns Array[0]

Backbone route not invoking an action with URL

This is the code to a simple ToDo app using ASP.NET MVC:
var Appointment = Backbone.Model.extend({
defaults: function () {
return {
'date': new Date(),
'cancelled': false,
'title': 'Checkup'
}
}
});
var AppointmentList = Backbone.Collection.extend({
model: Appointment,
url:"/Home/Appointments"
});
var AppointmentView = Backbone.View.extend({
template: _.template('<span class="<% if(cancelled) print("cancelled") %>"><%= title %></span>x'),
initialize: function () {
this.model.on('change', this.render, this);
},
render: function () {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
var AppointmentListView = Backbone.View.extend({
initialize: function () {
this.collection.on('add', this.addOne, this);
this.collection.on('reset', this.render, this);
},
render: function () {
this.collection.forEach(this.addOne, this);
},
addOne: function (model) {
var appointmentView = new AppointmentView({ model: model });
appointmentView.render();
this.$el.append(appointmentView.el);
}
});
var AppRouter = new (Backbone.Router.extend({
routes: { "Home/Appointment/:id": "show", "": "index" },
initialize: function (options) {
this.appointmentList = new AppointmentList();
},
start: function () {
Backbone.history.start({ pushState: true });
},
index: function () {
var appointmentsView = new AppointmentListView({ collection: this.appointmentList });
appointmentsView.render();
$('body').html(appointmentsView.el);
this.appointmentList.fetch();
},
show: function (id) {
debugger;
var appointment = new Appointment({ id: id });
var appointmentView = new AppointmentView({ model: appointment });
appointmentView.render();
$('body').html(appointmentView.el);
appointment.fetch();
}
}));
$(function () { AppRouter.start() });
Please do not be overwhelmed by the code. If you look closely it is very simple as I am a noob.
My problem is that show action is not being hit. But index can works and can populate the page with view. I tried with all kinds of url to hit the show function but unsuccessful. Can someone please tell me what I need to do to make this right?

events not firing when creating single instance of a view, but works when mutliple instance are created

I'm a complete newbie to Backbone and am trying to get my head round few things. Im trying to build something using jQuery mobile and Backbone. Please find my code below
var WelcomePage = Backbone.View.extend({
initialize:function () {
this.template = _.template($("#welcome_template").html());
},
render:function (eventName) {
$(this.el).html(this.template());
return this;
},
events:{
"click .btn_continue" : function(){
appRouter.navigate('login',{trigger: true});
}
}
});
var Login = Backbone.View.extend({
initialize:function () {
this.template = _.template($("#login_template").html());
},
render:function (eventName) {
$(this.el).html(this.template());
return this;
},
events:{
"click .btn_login" : function(){
appRouter.navigate('dashboard',{trigger: true});
}
}
});
var Dashboard = Backbone.View.extend({
initialize:function () {
this.template = _.template($("#dashboard_template").html());
},
render:function (eventName) {
$(this.el).html(this.template());
return this;
},
events:{
"click .btn_loadImages" : function(){
console.log('load Images');
}
}
});
var Router = Backbone.Router.extend({
routes:{
"":"welcome",
"login":"login",
"dashboard":"dashboard",
},
initialize:function () {
},
welcome:function () {
this.changePage(new WelcomePage());
},
login:function () {
this.changePage(new Login());
},
dashboard:function(){
this.changePage(new Dashboard());
},
changePage:function (page) {
$(page.el).attr('data-role', 'page');
page.render();
$('body').append($(page.el));
$.mobile.changePage($(page.el), {changeHash:false, transition: 'slide'});
}
});
var appRouter = new Router();
Backbone.history.start();
Now while I go back and forth through the screens using the BACK key the events fire OK using the code above. Then I tried replacing the code for the Router with the code below
var Router = Backbone.Router.extend({
routes:{
"":"welcome",
"login":"login",
"dashboard":"dashboard",
},
initialize:function () {
},
welcome:function () {
this.changePage(v_WelcomePage);
},
login:function () {
this.changePage(v_Login);
},
dashboard:function(){
this.changePage(v_Dashboard);
},
changePage:function (page) {
$(page.el).attr('data-role', 'page');
page.render();
$('body').append($(page.el));
$.mobile.changePage($(page.el), {changeHash:false, transition: 'slide'});
}
});
var v_WelcomePage = new WelcomePage();
var v_Login = new Login();
var v_Dashboard = new Dashboard();
var appRouter = new Router();
Backbone.history.start();
I noticed when I go back to the previous screens the events stop firing. Instead of creating the instance of the view in the action of the router I have created it outside and call it each time.I hope im making some sense.
Any advice much appreciated.
Events are hooked up using jQuery when the view is instantiated, not rendered (in the Backbone View constructor function). jQuery disconnects those events when the html is removed from the page (probably in $.mobile.changePage).
So, the second time you render the page, the events will not be hooked back up. You could try calling page.delegateEvents() to manually hook up the events again, or you could re-create the view each time.

jquery ui autocomplete inside jQuery Ui Dialog

Hi I have a JQuery Ui (jquery-ui-1.8.13.custom.min.js) inside a Dialog. When I start typing on the box I get the dropdown of items but it hides right away? Does anyone know why? Here is my code:
$(".openDialog").live("click", function (e) {
e.preventDefault();
var itemId = $(this).attr("data-item-id");
var ajaxurl = $(this).attr('data-ajax-refresh-url');
var dialogId = $(this).attr("data-dialog-id");
$('<div><img src="Content/images/spinner.gif" /> Loading...</div>')
.addClass("dialog")
.attr("id", $(this).attr("data-dialog-id"))
.appendTo("body")
.dialog({
width: 'auto',
title: $(this).attr("data-dialog-title"),
buttons: {
"Save": function () {
$(this).find('form').submit();
},
close: function () {
if (typeof itemId != "undefined") {
$.get(ajaxurl, { id: itemId },
function (data) {
// The data returned is a table <tr>
$("#Row" + itemId).replaceWith(data);
});
bindConfirm();
}
$(this).remove();
}
},
modal: true
}).load(this.href, function () {
$(this).find("input[data-autocomplete]").autocomplete({ source: $(this).find("input[data-autocomplete]").attr("data-autocomplete") });
});
});
They also had problems in early 1.8 releases. I remember applying a custom CSS selector to increase zIndex manually.
See also: http://forum.jquery.com/topic/autocomplete-inside-a-dialog-1-8rc2

Resources