I am writing an attendance web app using dev-Extrem dx-list (In UI weight). In this attendance we will display all users names in dxlist. and for attendance we just have to select the user. that will enter his name in db. My requirement is if we refresh the page, present should be selected.
My code example is here! ( I am using dev-extreme. )
var listWidget = "";
function getUsers(){
var date = $('#date').val();
var dt = date.split("/");
var dat = dt[2]+"-"+dt[0]+"-"+dt[1];
//alert(dat);
var data = { 'action': 'getUsers', 'date': dat};
$.post("php/users.php", data).success(function (resp) {
//alert(resp);
var users = $.parseJSON(resp);
var selectedItems = [];
var dataSource = new DevExpress.data.DataSource({
store: users,
searchOperation: "contains",
searchExpr: "name",
});
listWidget = $("#simpleList").dxList({
dataSource: dataSource,
editEnabled: true,
height: 400,
allowItemDeleting: false,
itemDeleteMode: "toggle",
showSelectionControls: true,
selectionMode: "multiple",
itemTemplate: function(data, index) {
var result = $("<div>").addClass("product");
$("<img>").attr("src", "images/"+data.id+".jpg").appendTo(result);
var content = $("<div class='content'>").appendTo(result);
$("<div class='findAtt' style='display:none;'>").text(data.attendaceId).appendTo(result);
$("<div class='findId'>").text(data.id).appendTo(content);
$("<div class='findName'>").text(data.name).appendTo(content);
$("<div class='findDep'>").text(data.department).appendTo(content);
$("<div class='findCode'>").text(data.userCode).appendTo(content);
/*var edit = $("<div class='findEdit'>").appendTo(result);
$("<a class='edit' onclick='edit()'>").text("Edit").appendTo(edit);*/
var salary = $("<div class='salary'>").appendTo(result);
$("<div class='findSalary'>").text("Rs."+data.userSalary).appendTo(salary);
return result;
},
onItemSwipe: function(data){
var userId = data.itemData.id;
var userSalary = data.itemData.userSalary;
$('#simpleList').dxList('instance').repaint();
edit(userId, userSalary);
}
}).dxList("instance");
$("#textbox").dxTextBox({
valueChangeEvent: "keyup",
placeholder: "Search",
onValueChanged: function(args) {
dataSource.searchValue(args.value);
dataSource.load();
},
mode: "search"
});
$('.dx-item').click(function(){
var att = $(this).find('.findId').text();
var dep = $(this).find('.findDep').text();
//console.log(this);
attendance(this, att, dep);
$(this).toggleClass('background');
//$(this).find('.dx-checkbox-checked').toggleClass('dx-checkbox-checked');
});
});
}
getUsers();
function attendance(bg, att, dep){
if($(bg).hasClass('background')){
var usersId = att;
var data = { 'action': 'absent',
'userId': usersId
};
$.post("php/users.php", data).success(function (resp) {
});
}else{
var userId = att;
var data = { 'action': 'attendance',
'userId': userId,
'dep': dep
};
$.post("php/users.php", data).success(function (resp) {
//alert(resp);
});
}
}
function edit(userId, userSalary){
$('#eSalary').val(userSalary);
$('#eId').val(userId);
$('#editModal').modal();
}
$('#saveSalary').click(function(){
var userId = $('#eId').val();
var salary = $('#eSalary').val();
var data = { 'action': 'changeSalary', 'userId': userId, 'salary': salary};
$.post("php/users.php", data).success(function (resp) {
$('#editModal').modal('hide');
alert(resp);
//location.reload();
$('#simpleList').dxList('instance').reload();
});
});
$('#depList').change(function(){
var depId = $('#depList option:selected').val();
var data = { 'action': 'getUserDep', 'depId': depId};
$.post("php/users.php", data).success(function (resp) {
//alert(resp);
var users = $.parseJSON(resp);
var selectedItems = [];
var dataSource = new DevExpress.data.DataSource({
store: users,
searchOperation: "contains",
searchExpr: "name"
});
var listWidget = $("#simpleList").dxList({
dataSource: dataSource,
editEnabled: true,
height: 400,
allowItemDeleting: false,
itemDeleteMode: "toggle",
showSelectionControls: true,
selectionMode: "multiple",
itemTemplate: function(data, index) {
var result = $("<div>").addClass("product");
$("<img>").attr("src", "images/"+data.id+".jpg").appendTo(result);
var content = $("<div class='content'>").appendTo(result);
$("<div class='findId'>").text(data.id).appendTo(content);
$("<div class='findName'>").text(data.name).appendTo(content);
$("<div class='findDep'>").text(data.department).appendTo(content);
$("<div class='findCode'>").text(data.userCode).appendTo(content);
var salary = $("<div class='salary'>").appendTo(result);
$("<div class='findSalary'>").text("Rs."+data.userSalary).appendTo(salary);
return result;
}
}).dxList("instance");
$("#textbox").dxTextBox({
valueChangeEvent: "keyup",
placeholder: "Search",
onValueChanged: function(args) {
dataSource.searchValue(args.value);
dataSource.load();
},
mode: "search"
});
$('.dx-item').click(function(){
var att = $(this).find('.findId').text();
var dep = $(this).find('.findDep').text();
//alert(att);
attendance(this, att, dep);
$(this).toggleClass('background');
});
});
});
As far as I understand, you want to save a selection of dxList items on page reload.
If so, I suggest you use the next approach:
Add the selected field for an each item(I mean DB schema). It should be like:
{ id: 1, text: "Bob", selected: true }
Get selected items before list rendering:
var selectedItems = load_from_your_server();
Init a list with the selectedItems option:
$("#list").dxList({
//...
selectedItems: selectedItems
});
Next, add the onSelectionChanged event handler:
$("#list").dxList({
//...
selectedItems: selectedItems,
onSelectionChanged: function(e) {
var listInst = e.component;
e.addedItems.forEach(function(item) {
// update item that was selected
update_your_item_in_db(item.id, {selected: true});
});
e.removedItems.forEach(function(item) {
// update item that was unselected
update_your_item_in_db(item.id, {selected: false});
});
}
});
I've create the small sample http://jsfiddle.net/vvh70no2/5/ that illustrates my idea. I've used an Array, not a real db.
Related
All I am trying to do is that I want to refresh my DataTable when the page is relaoded/refreshed. Right now on refreshing the page it retains its data. My application is using ASP.Net MVC tech.
Here is my Data Table and relevant functions:
$(document).ready(function () {
//debugger;
var table = $('#user_session_center_grid').DataTable({
"searching": false,
"colReorder": true,
"lengthChange": false,
"processing": true,
"serverSide": true,
"autoWidth": false,
"ajax": {
cache: false,
url: "#Url.Action("GetUserSessionHistory", "LoggedIn")",
type: 'POST',
data: function (data) {
data.SortBy = 'Month';
data.FromDate = "";
data.ToDate = "";
data.userSearch = $('#userSearch').val();
if (event) {
var objDtFilter = event.target;
if ($(objDtFilter).hasClass('dtFilter')) {
data.FromDate = event.target.getAttribute('fromdt');
data.ToDate = event.target.getAttribute('todt');
}
if ($(objDtFilter).hasClass('dtSort'))
data.SortBy = event.target.getAttribute('sort');
if ($(objDtFilter).hasClass('UserTypeFilter'))
data.value1 = event.target.getAttribute('value');
console.log(data.value1);
}
},
},
"language": {
oPaginate: {
sNext: '<i class="fa fa-chevron-right"></i>',
sPrevious: '<i class="fa fa-chevron-left"></i>',
sFirst: '<i class="fa fa-chevron-right"></i>',
sLast: '<i class="fa fa fa-chevron-left"></i>'
}
},
"columns": [
{
data: null,
class: "userName",
render: function (data, type, row) {
return "<div>" + data.FirstName + " " + data.LastName + "</div></td>";
}
},
{
data: null,
class: "startDate",
render: function (data, type, row) {
var parsedDate = JSON.parse(JSON.stringify(data.StartTime), ToJavaScriptDate);
return "<div>" + parsedDate + "</div></td>";
}
},
//{ 'data': 'User_ID' },
//{ 'data': 'FirstName' },
//{ 'data': 'LastName' },
//{ 'data': 'StartTime' },
],
});
table.on('draw', function () {
var widthofPagination = $('.dataTables_paginate.paging_simple_numbers').width() + 25;
$('#user_session_center_grid_info').css({ width: 'calc(100% - ' + widthofPagination + 'px)' });
});
$("#date_filter_ul.dropdown-menu li a").click(function () {
//debugger;
$('#date_filter_ul').removeClass('show');
$(this).closest('#CategoryFilter').find('.csp-select > span').text("Duration:" + $(this).text());
$('#user_session_center_grid').DataTable().ajax.reload();
});
$("#user_session_center_status_ul.dropdown-menu li a").click(function () {
$('#user_session_center_status_ul').removeClass('open');
$(this).closest('#StatusFilter').find('.csp-select > span').text($(this).text());
$('#user_session_center_grid').DataTable().ajax.reload();
});
});
Here are my controller functions:
public ActionResult UserSessionCenter()
{
if (Session["selectedCustomer"] == null)
{
Session["selectedCustomer"] = 9;
}
int customerId = (int)Session["selectedCustomer"];
var model = new UserSessionHistory();
var daccess = new ApplicationCommon.Ado.SqlDataAccess();
var customerNamesDt = daccess.GetUserNames(customerId);
var customerList = new List<UserSessionData>();
for (var i = 0; i < customerNamesDt.Rows.Count; i++)
{
var userinfo = new UserSessionData();
userinfo.CustomerId = customerNamesDt?.Rows[i]["Customer_Id"].ToString() ?? "";
userinfo.CustomerName = customerNamesDt?.Rows[i]["Customer_Name"].ToString() ?? "";
userinfo.UserId = customerNamesDt?.Rows[i]["User_ID"].ToString() ?? "";
userinfo.UserName = customerNamesDt?.Rows[i]["UserName"].ToString() ?? "";
customerList.Add(userinfo);
}
model.UserInfoList = customerList;
return View(model);
}
[HttpPost]
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public JsonResult GetUserSessionHistory()
{
var draw = Request.Form.GetValues("draw").FirstOrDefault();
var start = Request.Form.GetValues("start").FirstOrDefault();
var length = Request.Form.GetValues("length").FirstOrDefault();
if (Request["value1"] != null)
Session["userId"] = Request["value1"];
int pageSize = length != null ? Convert.ToInt32(length) : 0;
try
{
var UserID = Convert.ToInt32(Session["userId"]);
var filterModel = new FilterSessionHistoryModel();
filterModel.UserID = UserID;
filterModel.Skip = int.Parse(start);
filterModel.Take = int.Parse(length);
var fromDate = Request["FromDate"];
var toDate = Request["ToDate"];
filterModel.userSearch = Request["userSearch"];
if (!string.IsNullOrEmpty(fromDate) && !string.IsNullOrEmpty(toDate))
{
filterModel.DateFrom = fromDate;
filterModel.DateTo = toDate;
}
UserService userService = new UserService();
List<ADM_User_Session_History_Result> SessionList = userService.ADM_User_Session_History(filterModel);
int? numberOfRecords = 0;
if(SessionList.Count>0) {
numberOfRecords=SessionList[0].NumberOfRecords;
}
return Json(new { draw = draw, recordsFiltered = (int)numberOfRecords, recordsTotal = (int)numberOfRecords, data = SessionList }, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
CustomLogging.LogMessage(ex.Message + "/r/n" + ex.Source + "/r/n" + ex.StackTrace, LogType.Error);
return this.GetJSONObject(false, ex.Message.ToString());
}
}
The hierarchy is like this.
1. The Admin user can use any customer
2. One customer can have many users
3. The table shows the time of log in of the user selected.
The problem is that the list through which the Admin can change the Customer is in the top nav present in _Layout.cshtml where as this data table is in another view. The data changes fine while cahanging the user but I need it to reload or get empty when the customer is changed.
old browsers like 'explorer' keep caching the info from a first ajax call,
you can try to set the cash flag to false and the table will be updated each time
function ReLoadTable() {
$.ajax({
cache: false,
url: 'Your URL',
type: 'POST',
in controller put this attribute
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public class HomeController : Controller
{
finally in your Global.asax file add this function
protected void Application_PreSendRequestHeaders(Object sender, EventArgs e)
{
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetAllowResponseInBrowserHistory(false);
}
We use jQuery DataTables plug in across our app and it works pretty well. However we're encountering an error when we try load up the plug in with mock data (services aren't ready yet) in a modal on the page. We get the following errors ( the 2nd one happens after you click the link again to open the modal)
TypeError: d[i] is undefined
TypeError: b.nTableWrapper is null
The code looks correct, so I can't figure it out. Anyone with experience on this error ? We built the table map to correspond with the sample data nodes - and we've also tried maps with current working ones to no avail. (They are separated by purpose).
I've added the HTML in the fiddle. But basically it's just a simple table with the ID - the other datatables across the site work just fine with the same structure. Just stumped.
var sampleData = [{
"approvable": true,
"id": 4938,
"order_number": "3948948392893",
"order_template": "AJHSFJKHAS-SDJAS",
"size": "45mb",
"size_sort": 4500,
"ad_type": "testadtype",
"production_status": "yep!"
}
];
var makeCampaignApproveModal = function () {
var $approveParams;
var $approveComments = $('#approveComments');
var $approveCampaignForm = $('#approveCampaignForm');
var $campaignApprove = $('#campaignApprove');
var $approveCampaignHeader = $campaignApprove.children('h3');
var $ratingStars = $('#ratingStars');
var $ratingStar = $('.ratingStar');
var $ratingApproveCampaign = $('#ratingApproveCampaign');
var $ratingCancelCampaign = $('#ratingCancelCampaign');
var init = function () {
makeCampaignBulkOrders(sampleData);
//bindings
$ratingStar.mouseover(function (e) {
var $this = $(this);
$this.prevAll().addClass('active');
$this.nextAll().addClass('inactive');
});
$ratingStar.mouseout(function (e) {
var $this = $(this);
$this.prevAll().removeClass('active');
$this.nextAll().removeClass('inactive');
});
$ratingStar.on("click", function (e) {
e.preventDefault();
var $this = $(this);
//style prevs
$this.addClass('selected');
$this.prevAll().addClass('selected');
$this.nextAll().removeClass('selected');
$ratingStars.data('rating', $this.attr('rel'));
});
$ratingApproveCampaign.on("click", function (e) {
e.preventDefault();
var approveHandler = Portal.Details.synchronousRatingHandler;
utils.makeProxyCall((actionQueryPath + '/proof/approve'), approveHandler, Portal.Details.proofReturnE);
callInProgress();
return false;
});
$ratingCancelCampaign.on("click", function (e) {
e.preventDefault();
$campaignApprove.modality('close');
});
$campaignApprove.addClass('initted');
}; //init
if (!$campaignApprove.hasClass('initted')) {
init();
}
aep.utils.setupForm($approveCampaignForm, true, false);
$ratingStars.data();
$ratingApproveCampaign.text($l(2019));
$approveCampaignHeader.html($l(2018) + '<span class="actionModalObjectNumber"></span>');
updateModalHeader($campaignApprove);
var curRating = actionDetails['rating'];
$ratingStar.each(function () {
var $this = $(this);
if ($this.attr('rel') <= curRating) {
$this.addClass('selected');
} else {
$this.removeClass('selected');
}
});
$ratingStars.data('rating', curRating);
$campaignApprove.modality(modalConfig);
};
var makeCampaignBulkOrders = function (coData) {
var $campaignBulkOrders = $('#campaignBulkOrders');
var $campaignBulkOrdersTable = $('#bulk-ords-table');
var DTConfig = {
'bDestroy': true,
'bAutoWidth': true,
'iDisplayLength': -1,
'bPaginate': false,
'sScrollY': '200px',
'bScrollCollapse': true,
'oLanguage': {
'sSearch': $l(1024)
}
};
var coData = sampleData || [];
var coMap = getTableMapBulkOrders();
var cDTConfig = DTConfig;
cDTConfig['aaData'] = coData;
cDTConfig['aaColumns'] = coMap;
cDTConfig['aaSorting'] = [[1, 'desc']];
cDTConfig['sScrollY'] = '';
cDTConfig['bScrollCollapse'] = false;
$campaignBulkOrders.find('.tableLoading').hide();
$campaignBulkOrdersTable.dataTable(cDTConfig).fnDraw();
cDTConfig['aaSorting'] = [];
};
var getTableMapBulkOrders = function () {
return [{
oid: 'approvable',
'sTitle': '',
'mDataProp': function (source, type, val) {
if (type == "display") {
return '<input type="checkbox" class="campaign-order-approval" rel="' + source.id + '" />';
}
return source.id;
},
'sClass': 'checkbox'
},
{
oid: 'order_number',
'sClass': 'order_number',
'mDataProp': function (source, type, val) {
var ordNumber = '<a class="detailsLink" href="/portal/details/#orders/' + source.id + '">' + source.order_number + '</a>';
if (type == 'display') {
return ordNumber;
}
return source['order_number'];
},
'sTitle': $l(2089),
'sClass': 'orderNumber'
},
{
oid: 'order_template',
'mDataProp': 'order_template',
'sTitle': $l(2048),
'sClass': 'order_template'
}, {
oid: 'fileSize',
'mDataProp': function (source, type, val) {
if (type == "sort") {
return source['size_sort'];
}
return source['size'];
},
'sTitle': $l(2099),
"sType": "numeric",
'sClass': 'file_size'
},
{
oid: 'ad_type',
'mDataProp': 'ad_type',
'sTitle': $l(2045),
'sClass': 'ad_type'
}, {
oid: 'production_status',
'mDataProp': 'production_status',
'sTitle': $l(2097),
'sClass': 'production_status'
}
];
};
jsfiddle is over here
I have an editable table. Below is a function that renders the table, given the Element ID to which the table is to be rendered, an array of columns, and a controller name, which is used in the construction of the URLs. The URLs are of the following format, where exampleController is an example controller:
To Update: php/gateway.php?sender=exampleController&action=update
To List: php/gateway.php?sender=exampleController&action=list
I want the POST request to contain the edited info, along with the respective row.
I've tried to do (something) in the code below. It makes the request but the POST is not included.
var initiateGrid = function(element_id, columns, controllerName) {
var gridModel = Backbone.Model.extend({
initialize: function() {
Backbone.Model.prototype.initialize.apply(this, arguments);
this.on("change", function(model, options) {
if (options && options.save === false) {
return;
}
this.sync();
});
},
sync: function(method, model, options) {
var methodUpdateMethodUrl = 'php/gateway.php?sender=' + controllerName + '&action=update';
options = options || {};
options.url = methodUpdateMethodUrl;
Backbone.sync(method, model, options);
}
});
var gridCollectionModel = Backbone.Collection.extend({
model: gridModel,
url: 'php/gateway.php?sender=' + controllerName + '&action=list',
});
var gridCollection = new gridCollectionModel();
var grid = new Backgrid.Grid({
columns: columns,
collection: gridCollection,
emptyText: "No Data"
});
var gridContainer = $("#" + element_id);
gridContainer.append(grid.render().el);
gridCollection.fetch({
reset: true
});
};
And, of course, there is this error:
Uncaught TypeError: Cannot read property 'trigger' of undefined
Try to change url
url:function() {
return 'php/gateway.php?sender=' + controllerName + '&action=list',
}
The following code worked for me:
var initiateGrid = function(element_id, columns, controllerName) {
var gridModel = Backbone.Model.extend({
initialize: function() {
Backbone.Model.prototype.initialize.apply(this, arguments);
this.on("change", function(model, options) {
this.sync("update", model, options);
});
},
sync: function(method, model, options) {
var methodUrl = {
'update': 'php/gateway.php?sender=' + controllerName + '&action=update'
};
console.log(method, model, options);
if (methodUrl && methodUrl[method.toLowerCase()]) {
options = options || {};
options.url = methodUrl[method.toLowerCase()];
}
Backbone.sync(method, model, options);
}
});
var gridCollectionModel = Backbone.Collection.extend({
model: gridModel,
url: 'php/gateway.php?sender=' + controllerName + '&action=list',
});
var gridCollection = new gridCollectionModel();
var grid = new Backgrid.Grid({
columns: columns,
collection: gridCollection,
emptyText: "No Data"
});
var gridContainer = $("#" + element_id);
gridContainer.append(grid.render().el);
gridCollection.fetch({
reset: true
});
};
And the PHP to get an array:
function get_form_data() {
$dump = json_decode(file_get_contents('php://input'));
$transfer_info = array();
foreach ($dump as $key => $value) {
$transfer_info[$key] = $value;
}
return $transfer_info;
}
I'm starting to learn and azure phonejs.
Todo list get through a standard example:
$(function() {
var client = new WindowsAzure.MobileServiceClient('https://zaburrito.azure-mobile.net/', 'key');
var todoItemTable = client.getTable('todoitem');
// Read current data and rebuild UI.
// If you plan to generate complex UIs like this, consider using a JavaScript templating library.
function refreshTodoItems() {
var query = todoItemTable.where({ complete: false });
query.read().then(function(todoItems) {
var listItems = $.map(todoItems, function(item) {
return $('<li>')
.attr('data-todoitem-id', item.id)
.append($('<button class="item-delete">Delete</button>'))
.append($('<input type="checkbox" class="item-complete">').prop('checked', item.complete))
.append($('<div>').append($('<input class="item-text">').val(item.text)));
});
$('#todo-items').empty().append(listItems).toggle(listItems.length > 0);
$('#summary').html('<strong>' + todoItems.length + '</strong> item(s)');
}, handleError);
}
function handleError(error) {
var text = error + (error.request ? ' - ' + error.request.status : '');
$('#errorlog').append($('<li>').text(text));
}
function getTodoItemId(formElement) {
return $(formElement).closest('li').attr('data-todoitem-id');
}
// Handle insert
$('#add-item').submit(function(evt) {
var textbox = $('#new-item-text'),
itemText = textbox.val();
if (itemText !== '') {
todoItemTable.insert({ text: itemText, complete: false }).then(refreshTodoItems, handleError);
}
textbox.val('').focus();
evt.preventDefault();
});
// Handle update
$(document.body).on('change', '.item-text', function() {
var newText = $(this).val();
todoItemTable.update({ id: getTodoItemId(this), text: newText }).then(null, handleError);
});
$(document.body).on('change', '.item-complete', function() {
var isComplete = $(this).prop('checked');
todoItemTable.update({ id: getTodoItemId(this), complete: isComplete }).then(refreshTodoItems, handleError);
});
// Handle delete
$(document.body).on('click', '.item-delete', function () {
todoItemTable.del({ id: getTodoItemId(this) }).then(refreshTodoItems, handleError);
});
// On initial load, start by fetching the current data
refreshTodoItems();
});
and it works!
Changed for the use of phonejs and the program stops working, even mistakes does not issue!
This my View:
<div data-options="dxView : { name: 'home', title: 'Home' } " >
<div class="home-view" data-options="dxContent : { targetPlaceholder: 'content' } " >
<button data-bind="click: incrementClickCounter">Click me</button>
<span data-bind="text: listData"></span>
<div data-bind="dxList:{
dataSource: listData,
itemTemplate:'toDoItemTemplate'}">
<div data-options="dxTemplate:{ name:'toDoItemTemplate' }">
<div style="float:left; width:100%;">
<h1 data-bind="text: name"></h1>
</div>
</div>
</div>
</div>
This my ViewModel:
Application1.home = function (params) {
var client = new WindowsAzure.MobileServiceClient('https://zaburrito.azure-mobile.net/', 'key');
var todoItemTable = client.getTable('todoitem');
var toDoArray = ko.observableArray([
{ name: "111", type: "111" },
{ name: "222", type: "222" }]);
var query = todoItemTable.where({ complete: false });
query.read().then(function (todoItems) {
for (var i = 0; i < todoItems.length; i++) {
toDoArray.push({ name: todoItems[i].text, type: "NEW!" });
}
});
var viewModel = {
listData: toDoArray,
incrementClickCounter: function () {
todoItemTable = client.getTable('todoitem');
toDoArray.push({ name: "Zippy", type: "Unknown" });
}
};
return viewModel;
};
I can easily add items to the list of programs, but from the server list does not come:-(
I am driven to exhaustion and can not solve the problem for 3 days, which is critical for me!
Specify where my mistake! Thank U!
I suggest you use a DevExpress.data.DataSource and a DevExpress.data.CustomStore instead of ko.observableArray.
Application1.home = function (params) {
var client = new WindowsAzure.MobileServiceClient('https://zaburrito.azure-mobile.net/', 'key');
var todoItemTable = client.getTable('todoitem');
var toDoArray = [];
var store = new DevExpress.data.CustomStore({
load: function(loadOptions) {
var d = $.Deferred();
if(toDoArray.length) {
d.resolve(toDoArray);
} else {
todoItemTable
.where({ complete: false })
.read()
.then(function(todoItems) {
for (var i = 0; i < todoItems.length; i++) {
toDoArray.push({ name: todoItems[i].text, type: "NEW!" });
}
d.resolve(toDoArray);
});
}
return d.promise();
},
insert: function(values) {
return toDoArray.push(values) - 1;
},
remove: function(key) {
if (!(key in toDoArray))
throw Error("Unknown key");
toDoArray.splice(key, 1);
},
update: function(key, values) {
if (!(key in toDoArray))
throw Error("Unknown key");
toDoArray[key] = $.extend(true, toDoArray[key], values);
}
});
var source = new DevExpress.data.DataSource(store);
// older version
store.modified.add(function() { source.load(); });
// starting from 14.2:
// store.on("modified", function() { source.load(); });
var viewModel = {
listData: source,
incrementClickCounter: function () {
store.insert({ name: "Zippy", type: "Unknown" });
}
};
return viewModel;
}
You can read more about it here and here.
I've just started using Backbone.js and my test cases are churning up something pretty weird.
In short, what I am experiencing is -- after I call a Backbone Model's constructor, some of the fields in my object seem to come from a previously item. For instance, if I call:
var playlist = new Playlist({
title: playlistTitle,
position: playlists.length,
userId: user.id
});
playlist.get('items').length; //1
however if I do:
var playlist = new Playlist({
title: playlistTitle,
position: playlists.length,
userId: user.id,
items: []
});
playlist.get('items').length; //0
Here's the code:
define(['ytHelper', 'songManager', 'playlistItem'], function (ytHelper, songManager, PlaylistItem) {
'use strict';
var Playlist = Backbone.Model.extend({
defaults: {
id: null,
userId: null,
title: 'New Playlist',
selected: false,
position: 0,
shuffledItems: [],
history: [],
items: []
},
initialize: function () {
//Our playlistItem data was fetched from the server with the playlist. Need to convert the collection to Backbone Model entities.
if (this.get('items').length > 0) {
console.log("Initializing a Playlist object with an item count of:", this.get('items').length);
console.log("items[0]", this.get('items')[0]);
this.set('items', _.map(this.get('items'), function (playlistItemData) {
var returnValue;
//This is a bit more robust. If any items in our playlist weren't Backbone.Models (could be loaded from server data), auto-convert during init.
if (playlistItemData instanceof Backbone.Model) {
returnValue = playlistItemData;
} else {
returnValue = new PlaylistItem(playlistItemData);
}
return returnValue;
}));
//Playlists will remember their length via localStorage w/ their ID.
var savedItemPosition = JSON.parse(localStorage.getItem(this.get('id') + '_selectedItemPosition'));
this.selectItemByPosition(savedItemPosition != null ? parseInt(savedItemPosition) : 0);
var songIds = _.map(this.get('items'), function(item) {
return item.get('songId');
});
songManager.loadSongs(songIds);
this.set('shuffledItems', _.shuffle(this.get('items')));
}
},
//TODO: Reimplemnt using Backbone.sync w/ CRUD operations on backend.
save: function(callback) {
if (this.get('items').length > 0) {
var selectedItem = this.getSelectedItem();
localStorage.setItem(this.get('id') + '_selectedItemPosition', selectedItem.get('position'));
}
var self = this;
console.log("Calling save with:", self);
console.log("my position is:", self.get('position'));
$.ajax({
url: 'http://localhost:61975/Playlist/SavePlaylist',
type: 'POST',
dataType: 'json',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(self),
success: function (data) {
console.log('Saving playlist was successful.', data);
self.set('id', data.id);
if (callback) {
callback();
}
},
error: function (error) {
console.error("Saving playlist was unsuccessful", error);
}
});
},
selectItemByPosition: function(position) {
//Deselect the currently selected item, then select the new item to have selected.
var currentlySelected = this.getSelectedItem();
//currentlySelected is not defined for a brand new playlist since we have no items yet selected.
if (currentlySelected != null && currentlySelected.position != position) {
currentlySelected.set('selected', false);
}
var item = this.getItemByPosition(position);
if (item != null && item.position != position) {
item.set('selected', true);
localStorage.setItem(this.get('id') + '_selectedItemPosition', item.get('position'));
}
return item;
},
getItemByPosition: function (position) {
return _.find(this.get('items'), function(item) {
return item.get('position') == position;
});
},
addItem: function (song, selected) {
console.log("this:", this.get('title'));
var playlistId = this.get('id');
var itemCount = this.get('items').length;
var playlistItem = new PlaylistItem({
playlistId: playlistId,
position: itemCount,
videoId: song.videoId,
title: song.title,
relatedVideos: [],
selected: selected || false
});
this.get('items').push(playlistItem);
this.get('shuffledItems').push(playlistItem);
this.set('shuffledItems', _.shuffle(this.get('shuffledItems')));
console.log("this has finished calling");
//Call save to give it an ID from the server before adding to playlist.
songManager.saveSong(song, function (savedSong) {
song.id = savedSong.id;
playlistItem.set('songId', song.id);
console.log("calling save item");
$.ajax({
type: 'POST',
url: 'http://localhost:61975/Playlist/SaveItem',
dataType: 'json',
data: {
id: playlistItem.get('id'),
playlistId: playlistItem.get('playlistId'),
position: playlistItem.get('position'),
songId: playlistItem.get('songId'),
title: playlistItem.get('title'),
videoId: playlistItem.get('videoId')
},
success: function (data) {
playlistItem.set('id', data.id);
},
error: function (error) {
console.error(error);
}
});
});
return playlistItem;
},
addItemByVideoId: function (videoId, callback) {
var self = this;
ytHelper.getVideoInformation(videoId, function (videoInformation) {
var song = songManager.createSong(videoInformation, self.get('id'));
var addedItem = self.addItem(song);
if (callback) {
callback(addedItem);
}
});
},
//Returns the currently selected playlistItem or null if no item was found.
getSelectedItem: function() {
var selectedItem = _.find(this.get('items'), function (item) {
return item.get('selected');
});
return selectedItem;
}
});
return function (config) {
var playlist = new Playlist(config);
playlist.on('change:title', function () {
this.save();
});
return playlist;
};
});
basically I am seeing the property 'items' is populated inside of initialize when I've passed in a config object that does not specify items at all. If I specify a blank items array in my config object, then there are no items in initialize, but this seems counter-intuitive. Am I doing something wrong?
The problem is with using reference types (arrays) in the defaults object. When a new Playlist model is created without specifying an items value, the default is applied. In case of arrays and objects this is problematic, because essentially what happens is:
newModel.items = defaults.items
And so all models initialized this way refer to the same array. To verify this, you can test:
var a = new Playlist();
var b = new Playlist();
var c = new Playlist({items:[]});
//add an item to a
a.get('items').push('over the rainbow');
console.log(b.get('items')); // -> ['over the rainbow'];
console.log(c.get('items')); // -> []
To get around this problem, Backbone supports defining Model.defaults as a function:
var Playlist = Backbone.Model.extend({
defaults: function() {
return {
id: null,
userId: null,
title: 'New Playlist',
selected: false,
position: 0,
shuffledItems: [],
history: [],
items: []
};
}
});