I am attempting to develop a Mobile app using Kendo Mobile's MVVM and JayData Data Access Library. I have run into an issue that I have worked on for about a week now with no luck. I am looking for a simple example consisting of Kendo two way binding using a model that is created by JayData's (asKendoDataSource) having an inverseProperty navigation property. I am not sure that JayData kendo.js Module supports models containing inverseProperty and in the process of testing, even after getting the data to save with the relationship, retrieval of the same record does not pull the relational data back into the viewmodel.
Can anyone provided a simple example of Saving and Retrieving such a model using the webSql provider?
Any help is greatly appreciated.
JayData Models (simplified):
//Expense Category Model
$data.Entity.extend('data.types.ExpenseCategory', {
Id: { type: 'Edm.Guid', key: true },
CategoryName: { type: 'string', required: true, minLength: 3, maxLength: 26 },
Expenses: { type: Array, elementType: "data.types.Expense", inverseProperty: "Category" }
});
//Expense Model
$data.Entity.extend('data.types.Expense', {
Id: { type: 'Edm.Guid', key: true },
ExpenseDescription: { type: 'string', required: true },
Category: { type: "data.types.ExpenseCategory", inverseProperty: "Expenses" }
});
// Entity Context
$data.EntityContext.extend('data.types.DbContext',
{
ExpenseCategories: { type: $data.EntitySet, elementType: data.types.ExpenseCategory },
Expenses: { type: $data.EntitySet, elementType: data.types.Expense },
});
// Database Context
data.context = new data.types.DbContext({ name: 'sqLite', databaseName: 'cDb' });
Kendo Viewmodel (simplified):
views.expenseCategoryPicker = kendo.observable({
app: null,
categories: db.context.ExpenseCategories.asKendoDataSource(),
expense: null,
itemClick: function(sender) {
var expense = views.expenseCategoryPicker.expense;
expense.set('Category', sender.data);
...add/update logic
expense.sync();
},
loadExpense: function(dataItem) {
views.expenseCategoryPicker.set('expense', undefined);
views.expenseCategoryPicker.set('expense', dataItem);
},
});
EDIT
I figured out why the data won't save and a work around. There is a bug in JayData's kendo.js module when using the Kendo MMVM binding and inverseProperty relationships. They(JayData) simply don't support their own Attach method when an object relationship is being set through their Kendo module. Therefor when you call Kendo's SET on the model passing in the related Object the Entity state on the Object being passed in is set to 20 (New) and JayData tries to create a new record and in my case failing due to primary key conflict. The Attach method sets the Entity state to unmodified.
I know there is probably a more elegant way to fix this in JayData's code, but for me simply adding the following line right before I use the Kendo set method to set the object relationship allows the record to be saved without error.
itemClick: function(sender) {
var expense = views.expenseCategoryPicker.expense;
//manually set the state so SQL won't try to create a new record
sender.data.innerInstance()._entityState = 10;
expense.set('Category', sender.data);
...
Subsequent reads require the Include('model') method to load the relational data as mentioned by Robesz (THANK YOU)
It would be nice to see JayData fix the data save issue in their Kendo module.
JayData doesn't load the related entities by default, you have to use the include() operator:
data.context.Expenses.include('Category').toArray(...)
The parameter of the include should be the name of the navigation property.
Related
I have this code:
Ext.define('storeBusiness',{
extend: 'Ext.data.Store',
autoLoad: true,
autoSync: true,
model: 'Business',
proxy: {
type: 'ajax',
api: {
read: '../crud/ler_filter.php?db='+database+'&tbl=mercados',
create: '../crud/criar.php?db='+database+'&tbl=mercados',
update: '../crud/update.php?db='+database+'&tbl=mercados',
destroy: '../crud/apagar.php?db='+database+'&tbl=mercados'
},
reader: {
type: 'json',
root: 'rows'
},
writer: {
type:'json'
}
}
});
var storeBusinessCombo = Ext.create('storeBusiness');
var storeBusiness = Ext.create('storeBusiness');
storeBusiness.add({id: 0, business: "All"});
I have 2 grids. One has storeBusiness and the other has storeProducts.
The way the work is when I click on the business grid it filters the produts grid so that it shows the products of that business.
On the business grid it has the storeBusiness that it fetches the records from a database. I want to fetch all business from the database and add one more record (named 'All') without writing it to the database.
I dont want to add 'All' to the database because in the Product's grid I want to have a combobox that has all the business (storeBusinessCombo) without the 'All' record.
Does anyone has any idea how I can do this?
(the code above isn't doing what I want, storeBusiness shows all business without the 'All' in the grid)
Important: This works if the Ext.define('storeBusiness', has a proxy that is of type: 'memory'
Two approaches come to mind:
Add the "all" record to the store after load and don't sync so the
record isn't sent to the server.
Use memory proxy, retrieve the
businesses records via Ajax request and assign them to the store via its data config.
To resolve this I have done:
Put the Ext.define('storeBusiness',{ to autoLoad: false,
Put this code:
var storeBusinessCombo = Ext.create('storeBusiness', {autoLoad: true});
var storeBusiness = Ext.create('storeBusiness');
storeBusiness.on('beforeload', function(){
storeBusiness.loadData([{id: 0, business: "All"}]);
}
And you should put
storeBusiness.load({addRecords: true});
when you want to load storeBusiness.
I'm getting this error:
[E] Ext.data.Session.checkModelType(): Unable to use anonymous models
in a Session
when trying to use a Session when binding a Grid with a Store via ViewModel:
ViewModel:
Ext.define('App.view.gridViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.gridview',
stores:{
gridstore: {
model: 'gridView',
autoLoad: true,
//This triggers the Exception:
session: true,
listeners: {
beforeload: function(store, operation, eOpts) {
var oProxy = this.getProxy();
oProxy.setExtraParams({
tableName: 'dbo.SomeTable'
, identityKey: "id"
, primaryKey: ["id"]
, mode: "readonly"
, lang: "es"
, output: 'json'
});
}
}
}
}
});
View:
Ext.define('App.view.gridView', {
extend: 'Ext.form.Panel',
//...
viewModel: {
type: 'gridview'
},
controller: 'gridview',
// Create a session for this view
session:true,
items: [{
xtype: 'grid',
reference: 'myGrid',
bind: '{gridstore}',
columns: [
//...
]
}]
//...
});
Model's data is fetch through a Proxy:
Model:
Ext.define("App.model.gridView", {
extend: 'Ext.data.Model',
schema: {
namespace: 'App.model'
},
proxy: {
//proxy remote api stuff......
}.
idProperty: 'id'.
primaryKeys: 'id'.
fields: [
//fields
]
});
I have no idea what an anonymous model is and I haven't found anything related in the web, any ideas?
Thanks in advance!
The reason seems to be that in my Server's response I have a JSON Object called "metaData", which collides with the one available in JSON Reader:
Response MetaData
The server can return metadata in its response, in addition to the
record data, that describe attributes of the data set itself or are
used to reconfigure the Reader. To pass metadata in the response you
simply add a metaData attribute to the root of the response data. The
metaData attribute can contain anything, but supports a specific set
of properties that are handled by the Reader if they are present:
http://docs.sencha.com/extjs/5.0/apidocs/#!/api/Ext.data.reader.Json
The curious thing is that I don't use any of the available metaData options for JSON Reader, nothing related to any anonymous model, therefore this might be considered a bug
I'm working on my first Backbone.js app and have run into some weird behavior that I'm concerned could indicate a problem in my design. My data looks like this:
Syllabus
Dance
Figure
Figure
Figure
Dance
Figure
Figure
etc.
I have created this model to represent it:
$.syllabus.Syllabus = Backbone.RelationalModel.extend({
urlRoot: '/api/syllabus',
idAttribute: 'id',
relations: [{
type: Backbone.HasMany,
key: 'danceAssignments',
relatedModel: '$.syllabus.DanceAssignment',
collectionType: '$.syllabus.DanceAssignmentCollection',
reverseRelation: {
key: 'syllabus',
includeInJSON: 'id'
}
}]
});
$.syllabus.DanceAssignment = Backbone.RelationalModel.extend({
urlRoot: '/api/danceassignments',
idAttribute: 'id',
relations: [{
type: Backbone.HasMany,
key: 'figureAssignments',
relatedModel: '$.syllabus.FigureAssignment',
collectionType: '$.syllabus.FigureAssignmentCollection',
reverseRelation: {
key: 'danceAssignment',
includeInJSON: 'id'
}
}],
});
$.syllabus.DanceAssignmentCollection = Backbone.Collection.extend({
model: $.syllabus.DanceAssignment,
urlRoot: '/api/danceassignments',
comparator: function(danceAssignment) {
return danceAssignment.get('index');
},
});
$.syllabus.FigureAssignment = Backbone.RelationalModel.extend({
urlRoot: '/api/figureassignments',
idAttribute: 'id'
});
$.syllabus.FigureAssignmentCollection = Backbone.Collection.extend({
model: $.syllabus.FigureAssignment,
url: '/api/figureassignments',
comparator: function(figureAssignment) {
return figureAssignment.get('index');
},
});
The FigureAssignmentCollection is sorting automatically when I change the index property of one of its members, but the DanceAssignmentCollection isn't, nor does it sort when I explicitly tell it to. The comparator is called, but if I print out the contents of the collection after the sort they're in the wrong order, and the interface renders them out of order.
Any thoughts?
I found the solution. My views were rendering the danceAssignments collection as part of SyllabusView. Once I added a DanceListView with the collection as its data and had it rendering the DanceViews instead of SyllabusView doing it the dances started sorting properly, and some other very odd behaviors resolved as well.
I would like to be able to validate single fields at a time in my forms using backbone forms and backbone validation, however I am having problems getting this to work if I put my requirements in the model validation, rather than the schema.
My model is:
class Event extends Backbone.Model
url: ->
'/events' + (if #isNew() then '' else '/' + #id)
validation:
title:
required: true
start_date:
required: true
end_date:
required: true
schema: ->
title:
type: "Text"
start_date:
type: "DateTime"
title: "Start Date"
DateEditor: "DatePicker"
end_date:
type: "DateTime"
title: "End Date"
DateEditor: "DatePicker"
The code in my View that uses these is
class Events extends Backbone.View
...
initialize: ->
#form = new Backbone.Form(model: #model).render()
Backbone.Validation.bind #form;
validateField: (field) ->
#form.on "#{field}:change", (form, fieldEditor) =>
#form.fields[field].validate()
render: ->
...
#validateField for field of #form.fields
...
However, my problem is that it only seems to validate if I move the required: true into the schema like so:
schema: ->
title:
type: "Text"
validators: ["required"]
However I'd rather not do this since, backbone.validation has a broader range of built-in validators which I would like to make use of outside this example.
I've noticed that backbone-forms states
There are 2 levels of validation: schema validators and the regular built-in Backbone model validation. Backbone Forms will run both when either form.commit() or form.validate() are called.
However I'm not sure it's running the regular validation when I validate the individual fields? The reasoning behind wanting to do this is that when a user starts creating an event I do not want to validate fields that they are yet to fill in.
Is there any way I can still validate individual fields without having to move the validation into the schema?
Thanks in advance.
======= UPDATE =======
On looking at the form editors source code for individual fields, the validate function is as so:
validate: function() {
var $el = this.$el,
error = null,
value = this.getValue(),
formValues = this.form ? this.form.getValue() : {},
validators = this.validators,
getValidator = Form.helpers.getValidator;
if (validators) {
//Run through validators until an error is found
_.every(validators, function(validator) {
error = getValidator(validator)(value, formValues);
return error ? false : true;
});
}
return error;
},
It does not appear to use the model validation, so I wonder how I can incorporate this?
I have a store being used by several charts. I get my data remotely with an ajax call. In the php script that I link it too, I'm just going to change the parameters of my query to adjust for the different charts.
Here's my idea: I pass the title of the chart's panel as a parameter to my php script. That'll tell me which chart it is.
How do I access the title of whatever chart the store is being used by?
var my_store = Ext.create('Ext.data.JsonStore', {
fields: ['project', 'accepted', 'rejected', 'deleted', 'undefined'],
proxy: {
type: 'ajax',
url: 'generate_proj.php',
extraParams: {foo: **chart.id**},
reader: {
type: 'json'
}
},
autoLoad: true,
listeners: {
beforeload: function(store,operation) {
//operation.params.foo = this.idname;
},
load: function(obj,records) {
var text = Ext.decode(obj.responseText);
Ext.each(records,function(rec) {
});
}
}
});
Here's what I've done so far. Getting the name of a single chart/panel is no problem. I want the store to be able to dynamically read the name of what's using it. How?
Somewhere, you have some code that switches between the various charts. During that code, you could do something like
activeChart.getChartStore().proxy.extraParams.foo = activeChart.getId();
where activeChart is whatever reference you have to the chart you are about to show. Then when you load the store, it'll send the correct parameter.