Testing Custom Adapters for Ember-Data beta - javascript

I wish to write some unit tests for the custom adapter I have written for Ember-Data and I am running into errors. Here is how I am setting up the store and a test model:
window.App = Ember.Application.create();
App.Store = DS.Store.extend({ adapter: DS.WebSqlStorageAdapter.extend({ logQueries: true }) });
App.createTables = function() {
var db = store.adapter.create().db;
db.transaction(
function(tx) {
tx.executeSql('DROP TABLE IF EXISTS test_models;');
tx.executeSql('CREATE TABLE IF NOT EXISTS test_models (' +
'id INTEGER PRIMARY KEY AUTOINCREMENT,' +
'"string" TEXT,' +
'"number" REAL,' +
'"date" TEXT,' +
'"boolean" INTEGER' +
');');
},
function(err) {
console.error(err);
throw new Exception('Database error!');
},
function() {
App.dbCreated = true;
}
);
}
App.TestModel = DS.Model.extend({
number: DS.attr('number'),
date: DS.attr('date'),
string: DS.attr('string'),
boolean: DS.attr('boolean')
});
App.dbCreated = false;
window.store = App.Store.create();
setTimeout(App.createTables, 500);
Here's my test setup and my first test:
var m;
function waitForDbInit() {
waitsFor(function() { return App.dbCreated; }, 'DB initialization', 4000);
}
function waitsFor(fn, label, time) {
QUnit.stop();
var int2 = setInterval(function() {
throw new Error(label + 'was not completed after ' + time + ' ms.');
}, time);
var int = setInterval(function() {
if (fn()) {
clearInterval(int);
clearInterval(int2);
QUnit.start();
}
}, 50);
}
var inc = 0;
module('CRUD', {
setup: function() {
waitForDbInit();
m = store.createRecord('test_model', {
id: ++inc,
string: 'String!',
number: 1234,
date: new Date(),
boolean: true
});
}
});
asyncTest('creates a record', function() {
m.save().then(function() {
ok(m.get('number') === 12345);
start();
}, function(err) {
console.error(err);
ok(false);
start();
});
});
When I run the tests this is the error I get on every one:
Setup failed on retrieves a record: Cannot call method 'lookupFactory' of undefined
Source:
at DS.Store.Ember.Object.extend.modelFor (http://localhost:4567/lib/ember-data.js:2179:34)
at DS.Store.Ember.Object.extend.createRecord (http://localhost:4567/lib/ember-data.js:1343:17)
at Object.module.setup (http://localhost:4567/tests.js:24:15)
at Object.Test.setup (http://localhost:4567/lib/qunit.js:176:31)
at http://localhost:4567/lib/qunit.js:358:10
at process (http://localhost:4567/lib/qunit.js:1453:24)
at http://localhost:4567/lib/qunit.js:479:5

Related

How to pass the user name in Scheduled script to PDF template using SS 2.0?

I have made a scheduled script which is sending PDF though email.send()
I have get the filters as params from Suitelet. I want to get the name of the user (from runtime.getCurrentUser) and pass it to my PDF. I m just confused how to pass them and will that API be used in Suitelet or Sched script.
Can anyone help me with the code?
Here is my Scheduled script code:
/**
* #NApiVersion 2.x
* #NScriptType scheduledscript
*/
define(['N/ui/serverWidget', 'N/search', 'N/render', 'N/runtime', 'N/file', 'N/email'],
function (ui, search, render, runtime, file, email) {
function execute() {
try {
generateReport();
}
catch (e) {
log.error('generateReport ERROR', e);
}
}
function generateReport() {
var slfilters = runtime.getCurrentScript().getParameter({ name: 'custscript_searchfilter_report' });
log.debug('slfilters', slfilters);
if (!!slfilters) {
slfilters = JSON.parse(slfilters);
}
log.debug('slfilters2', slfilters);
var user = runtime.getCurrentUser();//Need this user to be passed to my xml template
var gender = slfilters.gender;//getting this from Suitelet
log.debug('gender', gender);
var item = slfilters.item;//getting this from Suitelet
log.debug('item', item);
var item_ = getItems(item, gender);
log.debug('getItems(item, gender)', item_);
//return item;
var xmlTemplateFile = file.load(3918);
//var template = script.getParameter({ name: 'custscript_template' });
var renderer = render.create();
renderer.templateContent = xmlTemplateFile.getContents();
var customSources = {
alias: 'searchdata',
format: render.DataSource.JSON,
data: JSON.stringify({
value: item_,
})
};
renderer.addCustomDataSource(customSources);
var xml = renderer.renderAsString();
var pdf = render.xmlToPdf({
"xmlString": xml
});
email.send({
author: 317,
recipients: 'aniswtf#gmail.com',
subject: 'Item Report',
body: 'Report Generated: ',
attachments: [pdf]
});
}
//
// ─── GET RESULTS ───────────────────────────────────────────────────
//
const getResults = function (set) {
var results = [];
var i = 0;
while (true) {
var result = set.getRange({
"start": i,
"end": i + 1000
});
if (!result) break;
results = results.concat(result);
if (result.length < 1000) break;
i += 1000;
}
return results;
};
//
// ─── GET ITEMS ───────────────────────────────────────────────────
//
function getItems(item, gender,user) {
try {
log.error('getItems Function started');
var itemSearch = search.load({
id: 'customsearch_mx_itemsearch'
});
var defaultFilters = itemSearch.filters;
itemSearch.filters.push(
search.createFilter({
name: "custitem5",
operator: 'anyof',
values: gender
}),
search.createFilter({
name: "internalid",
operator: 'anyof',
values: item
})
);
//defaultFilters = arrFilters;
//defaultFilters = defaultFilters.concat(arrFilters);
//log.error('Updated Filters', defaultFilters)
log.error('itemSearch', itemSearch);
//return defaultFilters;
var results = itemSearch.run().getRange({
start: 0,
end: 150
});
var result2 = results.map(function (x) {
// var results = getResults(itemSearch.run()).map(function (x) {
return {
'category': x.getText({
name: "custitem10",
join: "parent"
}),
'season': x.getValue({
name: "custitem11",
join: "parent"
}),
'riselabel': x.getText({
name: "custitem_itemriselabel",
join: "parent"
}),
'fit': x.getText({
name: "custitem9",
join: "parent"
}),
'name': x.getText({ //sku
name: "itemid",
join: "parent"
}),
'style': x.getText({
name: "custitem8",
join: "parent"
}),
'inseam': x.getText({
name: "custitem7",
join: "parent"
}),
'wash': x.getText({
name: "custitem_washname",
join: "parent"
}),
};
});
log.debug('Results', results.length);
log.debug('results', results);
log.debug('result2', result2);
// return results;//nabeeel's
return result2;//mine
} catch (e) {
log.error('error in getItems', e)
}
}
return {
execute: execute
};
});
There is no User in a Scheduled Script, so runtime.getCurrentUser() there will not return a value. You will need to retrieve the User via that method in the Suitelet (assuming it is not an anonymous external Suitelet).
From there you can add a Script Parameter to the Scheduled Script to hold the User, and then your Scheduled Script can read the Parameter and add the value as another Data Source on your template.

How is it possible for a string to be typed as Number in npm package commandLineArgs

In the below code from MongoDB's course Week 3's Query Operators in the Node.js Driver chapter :
var MongoClient = require('mongodb').MongoClient,
commandLineArgs = require('command-line-args'),
assert = require('assert');
var options = commandLineOptions();
MongoClient.connect('mongodb://localhost:27017/crunchbase', function(err, db) {
assert.equal(err, null);
console.log("Successfully connected to MongoDB.");
var query = queryDocument(options);
var projection = {
"_id": 1,
"name": 1,
"founded_year": 1,
"number_of_employees": 1,
"crunchbase_url": 1
};
var cursor = db.collection('companies').find(query, projection);
var numMatches = 0;
cursor.forEach(
function(doc) {
numMatches = numMatches + 1;
console.log(doc);
},
function(err) {
assert.equal(err, null);
console.log("Our query was:" + JSON.stringify(query));
console.log("Matching documents: " + numMatches);
return db.close();
}
);
});
function queryDocument(options) {
console.log(options);
var query = {
"founded_year": {
"$gte": options.firstYear,
"$lte": options.lastYear
}
};
if ("employees" in options) {
query.number_of_employees = {
"$gte": options.employees
};
}
return query;
}
function commandLineOptions() {
var cli = commandLineArgs([{
name: "firstYear",
alias: "f",
type: Number
}, {
name: "lastYear",
alias: "l",
type: Number
}, {
name: "employees",
alias: "e",
type: Number
}]);
var options = cli.parse()
if (!(("firstYear" in options) && ("lastYear" in options))) {
console.log(cli.getUsage({
title: "Usage",
description: "The first two options below are required. The rest are optional."
}));
process.exit();
}
return options;
}
I'm requiring command-line-args package, which has a method commandLineArgs. All good and fine...
Now, I see that the type of the objects passed to this method is set to Number. We can clearly see that they're Strings.
How is it possible?
From the command-line-args GitHub page:
The type value is a setter function (you receive the output from this), enabling you to be specific about the type and value received.
In other words, passing Number as type allows you to parse the arguments as numbers.

Javascript sinon.js spy working in one test case but not in other

This is my javascript class
var CommunicationInterface = inherit(Interface, {
__constructor: function() {
this.heartBeatDuration = 60 * 1000;
this.client = null;
this._isSetupped = false;
this.__base();
},
setup: function() {
// console.log('xmpp interface setup started');
var _this = this;
var deferred = Q.defer();
if (this._isSetupped) {
deferred.resolve();
return deferred.promise;
}
CommConfig.get('params')
.then(function(params) {
_this.client = new Client({
id: params.id + '#' + config('HOST'),
password: params.password,
host: config('HOST'),
port: config('PORT'),
reconnect: true
});
_this.client.on('online', _this.onOnline.bind(_this));
setInterval(function() {
_this.heartBeat.bind(_this)(params.id);
}, _this.heartBeatDuration);
_this._isSetupped = true;
deferred.resolve();
}, function(err){
console.log(err);
});
return deferred.promise;
},
heartBeat: function(Id) {
var stanza = this._makeMessage({
'to': id + '#' + config('HOST'),
'type': 'chat'
}, '{}');
console.log('foo');
this.client.send(stanza);
console.log('bar');
},
onOnline: function() {
console.log('online');
this.client.send('online');
this.emitter.emit('online');
},
});
and the test code is:
describe('CommunicationInterface', function() {
var commInterface;
var stubGetConfig, stubCommClient, stubCommClientConnect, spyCommClientSend;
var clock;
before(function () {
var deferred = Q.defer();
stubGetConfig = sinon.stub(CommConfig, 'get')
.withArgs('params')
.returns(deferred.promise);
deferred.resolve({
'id': 'test',
'password': '123456',
});
stubCommClientConnect = sinon.stub(CommunicationInterface.Client.prototype,
'connect');
clock = sinon.useFakeTimers();
});
beforeEach(function () {
commInterface = new CommunicationInterface();
stubCommClient = sinon.spy(commInterface.client);
});
afterEach(function () {
stubCommClientConnect.reset();
stubGetConfig.reset();
stubCommClient.reset();
clock.restore();
});
it('test 1', function(done) {
commInterface.setup()
.done(function () {
var spyCommClientSend = sinon.spy(commInterface.client, 'send');
commInterface.client.emit('online');
assert.isTrue(spyCommClientSend.calledOnce);
assert.isTrue(spyCommClientSend.calledWithExactly('online'));
done();
});
});
it('test 2', function(done) {
var spyHeartBeat = sinon.spy(commInterface.__proto__, 'heartBeat');
commInterface.setup().done(function() {
var spyCommClientSend = sinon.spy(commInterface.client, 'send');
clock.tick(commInterface.heartBeatDuration + 10);
assert.isTrue(spyHeartBeat.calledOnce);
assert.isTrue(spyCommClientSend.called);
spyHeartBeat.restore();
done();
});
});
});
The code in test 1 is working fine and spyCommClientSend is created properly, but the second assertion in test 2 fails and spyCommClientSend does not spy the actual object.
What could possibly be the reason here?
I am sure the send function is being called because the two console.log statements around it are printed.
At first glance I think the problem is in the fact that your spy is looking at commInterface.__proto__.heartBeat, which means that you are asserting that the heartBeat method on the CommunicationInterface prototype is called. This will not happen, because when you make sinon's clock tick, the heartBeat call is on the commInterface instance you created inside beforeEach.
This might be fixed by actually spying on heartBeat on the instance instead of the prototype, like so:
var spyHeartBeat = sinon.spy(commInterface, 'heartBeat');
Additionally, I would suggest that you cleanup commInterface inside your afterEach call by setting it to undefined or null -- just to ensure that you have a brand new, completely clean instance of CommunicationInterface with every test case.
Hope this helps!

Create session timeout warning for durandal single page application

I have a durandal/requirejs single page application. When a user sits idle, I need to display a warning to the user indicating that the session is about to time out. I have looked at several examples on the internet for asp.net apps, but can't find any examples for a single page application.
My application is similar to John Papa's code camper (MVC application).
How can I get a session timeout warning to the user if their session is 2 minutes away from timing out?
--EDIT
In my main.js file I have-
app.setRoot('viewmodels/shell', 'entrance');
router.guardRoute = function (instance, instruction) {
var sess_pollInterval = 60000;
//How many minutes the session is valid for
var sess_expirationMinutes = 2;
//How many minutes before the warning prompt
var sess_warningMinutes = 1;
var sess_intervalID;
var sess_lastActivity;
initSessionMonitor();
function initSessionMonitor() {
sess_lastActivity = new Date();
sessSetInterval();
$(document).bind('keypress.session', function (ed, e) { sessKeyPressed(ed, e); });
}
function sessSetInterval() {
sess_intervalID = setInterval('sessInterval()', sess_pollInterval);
}
function sessClearInterval() {
clearInterval(sess_intervalID);
}
function sessKeyPressed(ed, e) {
sess_lastActivity = new Date();
}
function sessPingServer() {
//Call an AJAX function to keep-alive your session.
alert('someAJAXFunction();');
}
function sessLogOut() {
alert('here');
//window.location.href = '/Account/LogOff';
}
function sessInterval() {
var now = new Date();
var diff = now - sess_lastActivity;
var diffMins = (diff / 1000 / 60);
if (diffMins >= sess_warningMinutes) {
//wran before expiring
//stop the timer
sessClearInterval();
//promt for attention
if (confirm('Your session will expire in ' + (sess_expirationMinutes - sess_warningMinutes) +
' minutes (as of ' + now.toTimeString() + '), press OK to remain logged in ' +
'or press Cancel to log off. \nIf you are logged off any changes will be lost.')) {
now = new Date();
diff = now - sess_lastActivity;
diffMins = (diff / 1000 / 60);
if (diffMins > sess_expirationMinutes) {
//timed out
sessLogOut();
}
else {
//reset inactivity timer
sessPingServer();
sessSetInterval();
sess_lastActivity = new Date();
}
} else {
sessLogOut();
}
} else {
sessPingServer();
}
}
return true;
};
}
now getting "Uncaught ReferenceError: sessInterval is not defined." Ideas?
Here's how I do it in my idle service. It uses some other services, but you should get the idea. Basically, I start tracking user activity in observable when he sings in and reset the timeout for idle handler everytime observable changes.
//idle.js
define(function (require) {
var ko = require('knockout'),
$ = require('jquery'),
router = require('lib/router'),
config = require('lib/config'),
dialog = require('lib/dialog'),
auth = require('auth/auth'),
lastActionDate = ko.observable(),
signoutHandle = null,
onIdle = function () {
console.log('user has been idle, signing out');
return auth.signOut()
.then(function () {
router.navigate('');
dialog.show('auth/idle');
});
},
init = function () {
var userActionHandler = function () {
lastActionDate(new Date());
};
auth.on('signin:success').then(function (user) {
$(document).on('click keydown scroll', userActionHandler);
userActionHandler();
});
auth.on('signout:success').then(function (using) {
$(document).off('click keydown scroll', userActionHandler);
});
lastActionDate.subscribe(function () {
if (signoutHandle) {
clearTimeout(signoutHandle);
}
signoutHandle = setTimeout(onIdle, config.get('idleTimeout') * 1000);
});
};
return {
init: init
};
});
Then I just call idle.init() my main.js file before app.start()
The approach I used was different than my post above. I used timeout-dialog.js and altered that script to use with durandal's router and any other service I needed within my application. I also used idle js. Here is the code-
main.js in app.start()-
var timeout = 100;
$(document).bind("idle.idleTimer", function () {
controls.timeoutDialog.setupDialogTimer();
});
$(document).bind("active.idleTimer", function () {
var sess = Security.GetKeepSessionAlive();
});
$.idleTimer(timeout);
timeout-dialog.js code-
String.prototype.format = function () {
var s = this,
i = arguments.length;
while (i--) {
s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i]);
}
return s;
};
define(['durandal/system', 'plugins/router', 'services/logger', 'services/SecurityDataService'],
function (system, router, logger, Security){
timeoutDialog = {
settings: {
timeout: 50,
countdown: 15,
title: 'Your session is about to expire!',
message: 'You will be logged out in {0} seconds.',
question: 'Do you want to stay signed in?',
keep_alive_button_text: 'Yes, Keep me signed in',
sign_out_button_text: 'No, Sign me out',
keep_alive_url: '',
keep_alive_function: function () {
},
logout_url: function () {
router.map([
{ route: 'ErrorPage', moduleId: 'ErrorPage', title: 'ErrorPage', title: 'ErrorPage', nav: false }
]).activate
router.navigate('ErrorPage');
},
logout_redirect_url: function () {
router.map([
{ route: 'ErrorPage', moduleId: 'ErrorPage', title: 'ErrorPage', title: 'ErrorPage', nav: false }
]).activate
router.navigate('ErrorPage');
},
logout_function: function () {
amplify.store("ErrorDetails", "Session Timed Out!");
router.map([
{ route: 'ErrorPage', moduleId: 'ErrorPage', title: 'ErrorPage', title: 'ErrorPage', nav: false }
]).activate
router.navigate('ErrorPage');
},
restart_on_yes: true,
dialog_width: 350
},
alertSetTimeoutHandle: 0,
setupDialogTimer: function (options) {
if (options !== undefined) {
$.extend(this.settings, options);
}
var self = this;
if (self.alertSetTimeoutHandle !== 0) {
clearTimeout(self.alertSetTimeoutHandle);
}
self.alertSetTimeoutHandle = window.setTimeout(function () {
self.setupDialog();
}, (this.settings.timeout - this.settings.countdown) * 1000);
},
setupDialog: function () {
//check for other modal forms on view
//$.element.modal('hide');
$('.modal').modal('hide');
var self = this;
self.destroyDialog();
$('<div id="timeout-dialog">' +
'<p id="timeout-message">' + this.settings.message.format('<span id="timeout-countdown">' + this.settings.countdown + '</span>') + '</p>' +
'<p id="timeout-question">' + this.settings.question + '</p>' +
'</div>')
.appendTo('body')
.dialog({
modal: true,
width: this.settings.dialog_width,
minHeight: 'auto',
zIndex: 10000,
closeOnEscape: false,
draggable: false,
resizable: false,
dialogClass: 'timeout-dialog',
title: this.settings.title,
buttons: {
'keep-alive-button': {
text: this.settings.keep_alive_button_text,
id: "timeout-keep-signin-btn",
click: function () {
self.keepAlive();
}
},
'sign-out-button': {
text: this.settings.sign_out_button_text,
id: "timeout-sign-out-button",
click: function () {
self.signOut(true);
}
}
}
});
self.startCountdown();
},
destroyDialog: function () {
if ($("#timeout-dialog").length) {
$("#timeout-dialog").dialog("close");
$('#timeout-dialog').remove();
}
},
startCountdown: function () {
var self = this,
counter = this.settings.countdown;
this.countdown = window.setInterval(function () {
counter -= 1;
$("#timeout-countdown").html(counter);
if (counter <= 0) {
window.clearInterval(self.countdown);
self.signOut(false);
}
}, 1000);
},
keepAlive: function () {
var self = this;
this.destroyDialog();
window.clearInterval(this.countdown);
this.settings.keep_alive_function();
if (this.settings.keep_alive_url !== '') {
$.get(this.settings.keep_alive_url, function (data) {
if (data === "OK") {
if (this.settings.restart_on_yes) {
self.setupDialogTimer();
}
}
else {
self.signOut(false);
}
});
}
},
signOut: function (is_forced) {
var self = this;
this.destroyDialog();
this.settings.logout_function(is_forced);
if (this.settings.logout_url !== null) {
$.post(this.settings.logout_url, function (data) {
self.redirectLogout(is_forced);
});
}
else {
self.redirectLogout(is_forced);
}
},
redirectLogout: function (is_forced) {
var target = this.settings.logout_redirect_url + '?next=' + encodeURIComponent(window.location.pathname + window.location.search);
if (!is_forced)
target += '&timeout=t';
window.location = target;
},
};
var dataservice = {
timeoutDialog: timeoutDialog
};
return dataservice;
});
I put the timeout-dialog.js in my own folder under the apps folder to bring in durandal and other services i needed. The idle-timer.js was left in the scripts folder and registered via bundle.config.

Windows Azure + DevExrpess (PhoneJs) getting ToDoList (Standart Sample)

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.

Categories