Am still getting the hang of promises..
Here are the models of the db collections involved:
var itemSchema = new Schema({
label : String,
tag : { "type": Schema.ObjectId, "ref": "tag" }
});
var tagSchema = new Schema({
label : String,
});
And here's the series of Promises (map, map, map, join):
Right now, the Promise.join saves&completes before the 'items' map finishes running, and so the 'senders' map doesn't include the 'itemsArray' javascriptObject on save .. how can this be resolved?
var reqItems = req.body.items;
var itemsArray = [];
var items = Promise.map(reqItems,function(element){
var existingItem = Models.Item.findOneAsync({ "label": element });
existingItem.then(function (value) {
if ( (existingItem.fulfillmentValue != null) ) { // if this item exists
var itemObject = [
{ "item" : existingItem.fulfillmentValue._id },
{ "label" : existingItem.fulfillmentValue.label }
{ "tag" : existingItem.fulfillmentValue.tag }
];
itemsArray.push(itemObject);
} else { // first instance of this item .. create newItem
var existingTag = Models.Tag.findOneAsync({ "label": element });
existingTag.then(function (value) {
if ( (existingTag.fulfillmentValue != null) ) { // if this tag exists
var newItem = new Models.Item(
{
label : element,
tag : existingTag.fulfillmentValue._id,
}
);
newItem.save(function (err) { // save the newItem with existing tag
console.log(err);
var newSavedItem = Models.Item.findOneAsync({ "label": element });
newSavedItem.then(function (value) { // ensure existence of newItem
var itemObject = [
{ "item" : newSavedItem.fulfillmentValue._id },
{ "label" : newSavedItem.fulfillmentValue.label },
{ "tag" : newSavedItem.fulfillmentValue.tag }
];
itemsArray.push(itemObject); // push item to array
});
});
} else { // else this tag does not exist
var newTag = new Models.Tag(
{
label : element
}
);
newTag.save(function (err) {
console.log(err);
var newSavedTag = Models.Tag.findOneAsync({ "label": element });
newSavedTag.then(function (value) { // ensure existence of newTag
if ( (newSavedTag.fulfillmentValue != null) ) {
var newItem = new Models.Item(
{
label : element,
tag : newSavedTag.fulfillmentValue._id,
}
);
newItem.save(function (err) {
console.log(err);
var newSavedItem = Models.Item.findOneAsync({ "label": element });
newSavedItem.then(function (value) { // ensure existence of newItem
var itemObject = [
{ "item" : newSavedItem.fulfillmentValue._id },
{ "label" : newSavedItem.fulfillmentValue.label },
{ "tag" : newSavedItem.fulfillmentValue.tag }
];
itemsArray.push(itemObject); // push item to array
}); // newSavedItem.then
}); // newItem.save
} // if newSavedTag.isFulfilled
}); // newSavedTag.then
}); // newTag.save
} // else tag does not exist
}); // existingTag.then
} // first instance of this item .. create newItem
}); // existingItem.then
itemObject = null; // reset for map loop
}); // Promise.map itemsArray
var receivers = Promise.map(receiverArray,function(element){
return Promise.props({
username : element
});
});
var senders = Promise.map(senderArray,function(element){
return Promise.props({
username : element,
items : itemsArray
});
});
Promise.join(receivers, senders, function(receivers, senders){
store.receivers = receivers;
store.senders = senders;
var saveFunc = Promise.promisify(store.save, store);
return saveFunc();
}).then(function(saved) {
console.log(saved);
res.json(saved);
})...error handling...});
Yes that can be massively simplified, although your problem was probably just forgetting to return anything to the first mapper (and the massive wasteland of useless fulfillmentValue code).
var reqItems = req.body.items;
var items = Promise.map(reqItems, function(element) {
return Models.Item.findOneAsync({ "label": element }).then(function(item) {
if (item != null) {
return item;
} else {
return Models.Tag.findOneAsync({ "label": element }).then(function(tag) {
if (tag == null) {
var newTag = new Models.Tag({label: element});
return newTag.saveAsync().then(function() {
return Models.Tag.findOneAsync({ "label": element });
})
}
return tag;
}).then(function(tag) {
var newItem = new Models.Item({
label: element,
tag: tag._id
});
return newItem.saveAsync().then(function() {
return Models.Item.findOneAsync({ "label": element });
});
})
}
});
});
var receivers = Promise.map(receiverArray, function(element){
return Promise.props({
username : element
});
});
var senders = Promise.map(senderArray, function(element){
return Promise.props({
username : element,
items : itemsArray
});
});
Promise.join(receivers, senders, items, function(receivers, senders, items) {
store.receivers = receivers;
store.senders = senders;
store.items = items;
return store.saveAsync().return(store);
}).then(function(store) {
console.log("saved store", store);
res.json(store);
}).catch(Promise.OperationalError, function(e) {
console.log("error", e);
res.send(500, "error");
});
Related
I'm creating a map based on this example:
https://labs.mapbox.com/education/impact-tools/finder-with-filters/
In their example, they have two dropdown filters and one checkbox filter. I would like to have three checkbox filters. I created three checkbox filters, and on their own, they seem to work well. The issue is that the filters seem to override each other in the order clicked. In their example, the filters seem to be working together, so I can't figure out why it's not working anymore when I changed the filter type.
Here's the code for my project:
https://codepen.io/flyinginsect2/pen/eYdyqxZ
Here are snippets of the code relevant to filtering:
const config = {
style: "mapbox://styles/mapbox/light-v10",
accessToken: "pk.eyJ1IjoibGF1cmFqZWFudGhvcm5lIiwiYSI6ImNraXl5M29oMDEyMjgzM3BhNTh1MGc1NjkifQ.g4IAFIrXPpl3ricw3f_Onw",
CSV: "https://docs.google.com/spreadsheets/d/106xm254us29hAUEtR7mTo0hwbDJv8dhyQs9rxY601Oc/gviz/tq?tqx=out:csv&sheet=Attributes",
center: [-104.339, 46.869],
zoom: 2,
title: "ENVIROlocity Mapper",
description: "Environmental Networking, Volunteering, Internship, and R.... Opportunities",
sideBarInfo: ["Org_name", "CityState"],
popupInfo: ["Org_name"],
filters: [
{
type: "checkbox",
title: "Sector: ",
columnHeader: "Sector",
listItems: ["Local Government", "Nonprofit"]
},
{
type: "checkbox",
title: "Industry: ",
columnHeader: "Industry_type",
listItems: ["Conservation", "Policy"]
},
{
type: "checkbox",
title: "Internships: ",
columnHeader: "internships_YN",
listItems: ["Yes"]
}
]
};
const selectFilters = [];
const checkboxFilters = [];
function createFilterObject(filterSettings) {
filterSettings.forEach(function (filter) {
if (filter.type === 'checkbox') {
columnHeader = filter.columnHeader;
listItems = filter.listItems;
const keyValues = {};
Object.assign(keyValues, { header: columnHeader, value: listItems });
checkboxFilters.push(keyValues);
}
if (filter.type === 'dropdown') {
columnHeader = filter.columnHeader;
listItems = filter.listItems;
const keyValues = {};
Object.assign(keyValues, { header: columnHeader, value: listItems });
selectFilters.push(keyValues);
}
});
}
function applyFilters() {
const filterForm = document.getElementById('filters');
filterForm.addEventListener('change', function () {
const filterOptionHTML = this.getElementsByClassName('filter-option');
const filterOption = [].slice.call(filterOptionHTML);
const geojSelectFilters = [];
const geojCheckboxFilters = [];
filteredFeatures = [];
filteredGeojson.features = [];
filterOption.forEach(function (filter) {
if (filter.type === 'checkbox' && filter.checked) {
checkboxFilters.forEach(function (objs) {
Object.entries(objs).forEach(function ([key, value]) {
if (value.includes(filter.value)) {
const geojFilter = [objs.header, filter.value];
geojCheckboxFilters.push(geojFilter);
}
});
});
}
if (filter.type === 'select-one' && filter.value) {
selectFilters.forEach(function (objs) {
Object.entries(objs).forEach(function ([key, value]) {
if (value.includes(filter.value)) {
const geojFilter = [objs.header, filter.value];
geojSelectFilters.push(geojFilter);
}
});
});
}
});
if (geojCheckboxFilters.length === 0 && geojSelectFilters.length === 0) {
geojsonData.features.forEach(function (feature) {
filteredGeojson.features.push(feature);
});
} else if (geojCheckboxFilters.length > 0) {
geojCheckboxFilters.forEach(function (filter) {
geojsonData.features.forEach(function (feature) {
if (feature.properties[filter[0]].includes(filter[1])) {
if (
filteredGeojson.features.filter(
(f) => f.properties.id === feature.properties.id
).length === 0
) {
filteredGeojson.features.push(feature);
}
}
});
});
if (geojSelectFilters.length > 0) {
const removeIds = [];
filteredGeojson.features.forEach(function (feature) {
let selected = true;
geojSelectFilters.forEach(function (filter) {
if (
feature.properties[filter[0]].indexOf(filter[1]) < 0 &&
selected === true
) {
selected = false;
removeIds.push(feature.properties.id);
} else if (selected === false) {
removeIds.push(feature.properties.id);
}
});
});
removeIds.forEach(function (id) {
const idx = filteredGeojson.features.findIndex(
(f) => f.properties.id === id
);
filteredGeojson.features.splice(idx, 1);
});
}
} else {
geojsonData.features.forEach(function (feature) {
let selected = true;
geojSelectFilters.forEach(function (filter) {
if (
!feature.properties[filter[0]].includes(filter[1]) &&
selected === true
) {
selected = false;
}
});
if (
selected === true &&
filteredGeojson.features.filter(
(f) => f.properties.id === feature.properties.id
).length === 0
) {
filteredGeojson.features.push(feature);
}
});
}
map.getSource('locationData').setData(filteredGeojson);
buildLocationList(filteredGeojson);
});
}
function filters(filterSettings) {
filterSettings.forEach(function (filter) {
if (filter.type === 'checkbox') {
buildCheckbox(filter.title, filter.listItems);
} else if (filter.type === 'dropdown') {
buildDropDownList(filter.title, filter.listItems);
}
});
}
function removeFilters() {
let input = document.getElementsByTagName('input');
let select = document.getElementsByTagName('select');
let selectOption = [].slice.call(select);
let checkboxOption = [].slice.call(input);
filteredGeojson.features = [];
checkboxOption.forEach(function (checkbox) {
if (checkbox.type == 'checkbox' && checkbox.checked == true) {
checkbox.checked = false;
}
});
selectOption.forEach(function (option) {
option.selectedIndex = 0;
});
map.getSource('locationData').setData(geojsonData);
buildLocationList(geojsonData);
}
function removeFiltersButton() {
const removeFilter = document.getElementById('removeFilters');
removeFilter.addEventListener('click', function () {
removeFilters();
});
}
createFilterObject(config.filters);
applyFilters();
filters(config.filters);
removeFiltersButton();
I read this Mapbox documentation on combining filters, but I can't figure out how to work it in.
https://docs.mapbox.com/mapbox-gl-js/style-spec/other/#other-filter
I know there are many other Stack Exchange posts out there that address filtering on multiple criteria, but I can't find one that seems to address this specific issue.
The issue is in the space in value for "Local Government"
If you look at the generated HTML you will see a space in the id, which is not valid HTML
<input class="px12 filter-option" type="checkbox" id="Local Government" value="Local Government">
Just remove the whitespaces when building the HTML id attribute
input.setAttribute('id', listItems[i].replace(/\s/g,''));
In jsPlumb, I am trying to get the full path between 2 nodes as an array with a single query even if there is multiple nodes in between source and destination. I am currently doing it with a BFS algorithm because I couldn't find anything like that in the documentation the code is
getPath: function (sourceId, targetId) {
var me = this;
var addNode = function addNode(graph, node) {
graph.set(node, {
in: new Set(),
out: new Set()
});
};
var connectNodes = function connectNodes(graph, sourceId, targetId) {
graph.get(sourceId).out.add(targetId);
graph.get(targetId).in.add(sourceId);
};
var buildGraphFromEdges = function buildGraphFromEdges(edges) {
return edges.reduce(function (graph, {
sourceId,
targetId
}) {
if (!graph.has(sourceId)) {
addNode(graph, sourceId);
}
if (!graph.has(targetId)) {
addNode(graph, targetId);
}
connectNodes(graph, sourceId, targetId);
return graph;
}, new Map());
};
var buildPath = function buildPath(targetId, path) {
var result = [];
while (path.has(targetId)) {
var sourceId = path.get(targetId);
result.push({
sourceId,
targetId
});
targetId = sourceId;
}
return result.reverse();
};
var findPath = function findPath(sourceId, targetId, graph) {
if (!graph.has(sourceId)) {
throw new Error('Unknown sourceId.');
}
if (!graph.has(targetId)) {
throw new Error('Unknown targetId.');
}
var queue = [sourceId];
var visited = new Set();
var path = new Map();
while (queue.length > 0) {
var start = queue.shift();
if (start === targetId) {
return buildPath(start, path);
}
for (var next of graph.get(start).out) {
if (visited.has(next)) {
continue;
}
if (!queue.includes(next)) {
path.set(next, start);
queue.push(next);
}
}
visited.add(start);
}
return null;
};
var graph = buildGraphFromEdges(me.jsPlumbContainer.getAllConnections());
var resultPath = findPath(sourceId, targetId, graph);
return resultPath;}
Is there a much better way to implement this ?
example array for my configuration is:
var connections =[
{
"sourceId": "l1",
"targetId": "l2"
},
{
"sourceId": "l2",
"targetId": "l4"
},
{
"sourceId": "l2",
"targetId": "l3"
},
{
"sourceId": "l4",
"targetId": "l5"
}, ]
var responseArr = new Array();
async.each(response, function (value, k) {
if(isDateFlag)
{
var defaultValue = value.auction_id;
grpArray.push(value.title);
var postData = {
parent_id : parent_id,
defaultValue : defaultValue,
isDateFlag : isDateFlag,
search_text : search_text
}
getChildNotificationList(postData, function (childArrayData) {
//Creating the response array
responseArr.push({
'notification_id' : childArrayData['notification_id'],
'notification_text' : childArrayData['notification_text']
});
});
}
});
return responseArr;//Blank Array
I want to return the final responseArr after manipulating it from child data query. It return blank array because it does not wait for the query response.
So how it can be async. Thanks
I referred http://justinklemm.com/node-js-async-tutorial/ and https://github.com/caolan/async.
This happens because the control goes on executing the code since javascript is synchronous. For getting the expected result modify the code as below:
var responseArr = new Array();
async.each(response, function (value, k) {
if(isDateFlag){
var defaultValue = value.auction_id;
grpArray.push(value.title);
var postData = {
parent_id : parent_id,
defaultValue : defaultValue,
isDateFlag : isDateFlag,
search_text : search_text
}
getChildNotificationList(postData, function (childArrayData) {
//Creating the response array
responseArr.push({
'notification_id' : childArrayData['notification_id'],
'notification_text' : childArrayData['notification_text']
});
k();
});
} else {
k();
}
}, function (err) {
if (err) {
console.log(err);
} else {
return responseArr;
}
});
The above code is inside a function block. You could get the result by calling the function.
Including the answer using async.map:
async.map(response, function (value, k) {
if(isDateFlag){
var defaultValue = value.auction_id;
grpArray.push(value.title);
var postData = {
parent_id : parent_id,
defaultValue : defaultValue,
isDateFlag : isDateFlag,
search_text : search_text
}
getChildNotificationList(postData, function (childArrayData) {
k(null, {
'notification_id' : childArrayData['notification_id'],
'notification_text' : childArrayData['notification_text']
});
});
} else {
k(null, {
'notification_id' : '',
'notification_text' : ''
});
}
}, function(err, results){
// results is now an array
return results;
});
In SAPUI5 I have a Model ("sModel") filled with metadata.
In this model I have a property "/aSelectedNumbers".
I also have a panel, of which I want to change the visibility depending on the content of the "/aSelectedNumbers" property.
update
first controller:
var oModelMeta = cv.model.recycleModel("oModelZAPRegistratieMeta", that);
//the cv.model.recycleModel function sets the model to the component
//if that hasn't been done so already, and returns that model.
//All of my views are added to a sap.m.App, which is returned in the
//first view of this component.
var aSelectedRegistratieType = [];
var aSelectedDagdelen = ["O", "M"];
oModelMeta.setProperty("/aSelectedRegistratieType", aSelectedRegistratieType);
oModelMeta.setProperty("/aSelectedDagdelen", aSelectedDagdelen);
First Panel (Which has checkboxes controlling the array in question):
sap.ui.jsfragment("fragments.data.ZAPRegistratie.Filters.RegistratieTypeFilter", {
createContent: function(oInitData) {
var oController = oInitData.oController;
var fnCallback = oInitData.fnCallback;
var oModel = cv.model.recycleModel("oModelZAPRegistratieMeta", oController);
var oPanel = new sap.m.Panel( {
content: new sap.m.Label( {
text: "Registratietype",
width: "120px"
})
});
function addCheckBox(sName, sId) {
var oCheckBox = new sap.m.CheckBox( {
text: sName,
selected: {
path: "oModelZAPRegistratieMeta>/aSelectedRegistratieType",
formatter: function(oFC) {
if (!oFC) { return false; }
console.log(oFC);
return oFC.indexOf(sId) !== -1;
}
},
select: function(oEvent) {
var aSelectedRegistratieType = oModel.getProperty("/aSelectedRegistratieType");
var iIndex = aSelectedRegistratieType.indexOf(sId);
if (oEvent.getParameters().selected) {
if (iIndex === -1) {
aSelectedRegistratieType.push(sId);
oModel.setProperty("/aSelectedRegistratieType", aSelectedRegistratieType);
}
} else {
if (iIndex !== -1) {
aSelectedRegistratieType.splice(iIndex, 1);
oModel.setProperty("/aSelectedRegistratieType", aSelectedRegistratieType);
}
}
// arrays update niet live aan properties
oModel.updateBindings(true); //******** <<===== SEE HERE
if (fnCallback) {
fnCallback(oController);
}
},
width: "120px",
enabled: {
path: "oModelZAPRegistratieMeta>/bChanged",
formatter: function(oFC) {
return oFC !== true;
}
}
});
oPanel.addContent(oCheckBox);
}
addCheckBox("Presentielijst (dag)", "1");
addCheckBox("Presentielijst (dagdelen)", "2");
addCheckBox("Uren (dagdelen)", "3");
addCheckBox("Tijd (dagdelen)", "4");
return oPanel;
}
});
Here is the panel of which the visibility is referred to in the question. Note that it DOES work after oModel.updateBindings(true) (see comment in code above), but otherwise it does not update accordingly.
sap.ui.jsfragment("fragments.data.ZAPRegistratie.Filters.DagdeelFilter", {
createContent: function(oInitData) {
var oController = oInitData.oController;
var fnCallback = oInitData.fnCallback;
var oModel = cv.model.recycleModel("oModelZAPRegistratieMeta", oController);
var oPanel = new sap.m.Panel( {
content: new sap.m.Label( {
text: "Dagdeel",
width: "120px"
}),
visible: {
path: "oModelZAPRegistratieMeta>/aSelectedRegistratieType",
formatter: function(oFC) {
console.log("visibility");
console.log(oFC);
if (!oFC) { return true; }
if (oFC.length === 0) { return true; }
return oFC.indexOf("2") !== -1;
}
}
});
console.log(oPanel);
function addCheckBox(sName, sId) {
var oCheckBox = new sap.m.CheckBox( {
text: sName,
selected: {
path: "oModelZAPRegistratieMeta>/aSelectedDagdelen",
formatter: function(oFC) {
if (!oFC) { return false; }
console.log(oFC);
return oFC.indexOf(sId) !== -1;
}
},
select: function(oEvent) {
var aSelectedDagdelen = oModel.getProperty("/aSelectedDagdelen");
var iIndex = aSelectedDagdelen.indexOf(sId);
if (oEvent.getParameters().selected) {
if (iIndex === -1) {
aSelectedDagdelen.push(sId);
oModel.setProperty("/aSelectedDagdelen", aSelectedDagdelen);
}
} else {
if (iIndex !== -1) {
aSelectedDagdelen.splice(iIndex, 1);
oModel.setProperty("/aSelectedDagdelen", aSelectedDagdelen);
}
}
if (fnCallback) {
fnCallback(oController);
}
},
enabled: {
path: "oModelZAPRegistratieMeta>/bChanged",
formatter: function(oFC) {
return oFC !== true;
}
},
width: "120px"
});
oPanel.addContent(oCheckBox);
}
addCheckBox("Ochtend", "O", true);
addCheckBox("Middag", "M", true);
addCheckBox("Avond", "A");
addCheckBox("Nacht", "N");
return oPanel;
}
});
The reason that the model doesn´t trigger a change event is that the reference to the Array does not change.
A possible way to change the value is to create a new Array everytime you read it from the model:
var newArray = oModel.getProperty("/aSelectedNumbers").slice();
// do your changes to the array
// ...
oModel.setProperty("/aSelectedNumbers", newArray);
This JSBin illustrates the issue.
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.