ExtJS - How do I pass ID when syncing store? - javascript

On ExtJS 6.2, I'm getting data from database like this: myStore.load({id: myId);.
How do I pass the ID while syncing?
This doesn't work: myStore.sync({id: myId);
I'm using a REST proxy and my URL is in this format: mydomain/users/1

You can simply use myStore.sync(), you don't need to specify id. The sync function will examine the store contents, and add/delete/update records as needed.
When using REST, you can set writeRecordId of Ext.data.write.Writer (see here) to false, since with REST id is usually appended to the URL, as in your case.

I solved by extending Ext.data.proxy.Rest and overriding the buildUrl method to be able to extract the ID from the request parameters. Then I used this overridden proxy in my model:
Ext.define('MYPROJ.proxy.Settings', {
extend : 'Ext.data.proxy.Rest',
alias : 'proxy.settings',
buildUrl(request) {
const url = this.callParent([request]);
const id = request.getParams().id;
if (id) {
delete request.getParams().id;
const proxyUrl = request.getProxy().getUrl();
return url.replace(proxyUrl, `${proxyUrl}/${id}`);
}
return url;
}
});
Ext.define('MYPROJ.model.Setting', {
extend : 'Ext.data.Model',
alias : 'model.setting',
clientIdProperty: 'clientId',
fields: [{
name : 'id',
type : 'int',
persist : false
}, {
name : 'name',
type : 'string',
unique : true
}],
proxy: {
type : 'settings', // <- Use overridden proxy here
url : `/something/settings`
}
});
Then in my code I can use sync() like this:
const myID = 10;
this.settingsStore.load({id: myID});
//...
this.settingsStore.sync({params: {id: myID}});
When sync() is called myID will be extracted from params on MYPROJ.proxy.Settings and added to the URL (/something/settings/10).

Related

Upsert data with a dynamic field name

I just try to do something simple with Mongo but it doesn't work:
I want to upsert datas in an object like: module.xxx.yyy then I tried many things like :
UsersRights.upsert({
condoId: condoId,
userId: manager._id,
}, {
condoId: condoId,
userId: manager._id,
module: {
[defaultRight.xxx] : {
[defaultRight.yyy] : defaultRight.default
}
}
});
but when I want to add a new xxx or a new yyy, it will erase and replace the entire module object and not only add a new key.
I also tried this :
UsersRights.upsert({
condoId: condoId,
userId: manager._id,
}, {
condoId: condoId,
userId: manager._id,
["module." + defaultRight.module + "." + defaultRight.right] : defaultRight.default,
});
but the server show me an error like: MinimongoError: Key module.xxx.yyy must not contain '.'
You need to use the following form:
YourCollection.upsert({
_id: id, (can be other selectors as well)
}, {
$set: setter
});
Setter is an object you create before and should have the following form:
const setter = {};
setter[`${#1Level}.${#2Level}`] = data;
Where #1Level & #2Level are vars naming the fields you want to modify or to add.

Ext JS dynamic store insert Cannot read property 'isModel' of undefined

I dynamically create my store from a model relation and attach this store to a grid.
var map = record.getCommunity().mappings();
map.load({
url: '/myUrl/mappings',
scope: this,
callback: function(records, operation, success) {
map.group('type');
//ExtJS bug https://www.sencha.com/forum/showthread.php?265674
me.getOutgoingGrid().destroy();
childGrid = Ext.create('hds.view.outgoingGrid', {
store: map
});
me.getGridHolder().add(childGrid);
me.getOutgoingGrid().getSelectionModel().select(0, false) ;
}
});
When I want to create a new model instance and insert it into this dynamic store I get the following error:
Cannot read property 'isModel' of undefined
Here is the code that triggers the error:
var newMap = Ext.create('hds.model.MappingModel', {
indetifier : "something",
});
me.getOutgoingGrid().store.insert(0, newMap);
I cannot found the reason of this problem....Any ideas?
It is hard to know what is breaking your code but here are a couple of things:
1 - You need to define the model identifier in the class prototype not in the instance.
2 - You misspelled identifier
So your model should look like:
Ext.define('hds.model.MappingModel', {
identifier : "something",
});
var newMap = new hds.model.MappingModel({
//...your config here
});
The error that you are seeing Cannot read property 'isModel' of undefined is thrown when the store tries to check if model is an instance of Ext.data.Model but the model being passed is undefined.
This can happen for several reasons but usually it's because you are trying to create a model before the prototype has been defined or because there is a typo on your code.
Please, try creating a a fiddle (https://fiddle.sencha.com) reproducing this error or it will be very hard to help you.
Regards,
If there was an fiddle, It would be more helpful. As I understand from your question, you have some data and you can't set the data to store or model correctly. If you had defined your model or store before set to grid, there would not be a problem. I added a fiddle how model proxy works with store and etc. Hope it helps. If the fiddle does not explain your problem, please change the fiddle codes. So, we can understand what your problem exactly. Here is the fiddle: https://fiddle.sencha.com/#fiddle/tvq
Ext.define('model.Users', {
extend: 'Ext.data.Model',
fields: [
{ name: 'Name', type: 'string'},
{ name: 'City', type: 'string'},
{ name: 'Country', type: 'string'},
],
//idProperty: 'Name',
proxy: {
type: 'ajax',
rootProperty: 'records',
rootUrl: 'users', // used when updating proxy url
url: 'users',
reader: {
type: 'json',
rootProperty: 'records'
}
}
}); //model
var modelExt = Ext.create('model.Users', { Name: 'Ernst Handel'});
var storeExt = Ext.create('Ext.data.Store', {
requires: 'model.Users',
model: 'model.Users'
});
modelExt.load({
scope: this,
success: function(record) {
var colObjects = [];
Ext.each(Object.keys(record.data), function(key) {
colObjects.push({
text: key,
dataIndex: key
});
});
storeExt.loadData([record]);
//console.log(record, storeExt);
var grid = Ext.create('Ext.grid.Panel', {
store: storeExt,
columns: colObjects,
renderTo: Ext.getBody()
});
},
failure: function (err) {
}
});
Create model like this and then use.
var newMap = Ext.create('Ext.data.model', {
identifier : "something"
});
Add a new file in Model folder named like MappingModel.js and define model like this.
Ext.define('hds.model.MappingModel', {
extend: 'Ext.data.Model',
fields: [
'something'
]
});
Add this in app.js and then use MappingModel in your dynamic store.

Populate Backbone.js JSON response into nested collections inside nested collections/models

My problem is that I am just starting out with Backbone.js and are having trouble wrapping my head around a complex problem. I want to save a form that have infinite fields, and some of the fields also needs to have infinite options. I'm just worried I might have started at the wrong end with a JSON response, instead of building the models/collections first. Here is a short pseudocode of what I try to achieve.
id:
parent: <blockid>
fields: array(
id:
title:
helpertext
options: array(
id:
type:
value:
)
)
Currently I am working with a faked JSON response from the server, which I built from scratch, and now I want to divide it into models and collections on the client side.
//Fake a server response
var JSONresponse = {
"formid":"1",
"fields":[
{
"fieldid":"1",
"title":"Empty title",
"helper":"Helper text",
"type":"radio",
"options":[
{
"optionid":"1",
"value":"Empty option.."
},
{
"optionid":"2",
"value":"Empty option.."
}
]
},
{
// fieldid2
}
]
};
The idea is to add fields as I see fit, and then if the field type is radio/checkbox/ul/ol there must also be an "options" array within the field.
My work so far:
var app = {};
app.Models = {};
app.Collections = {};
app.View = {};
app.Models.Option = Backbone.Model.extend({
});
app.Collections.Options = Backbone.Collection.extend({
model: app.Models.Option
});
app.Models.Field = Backbone.Model.extend({
options: new app.Collections.Options()
});
app.Collections.Fields = Backbone.Collection.extend({
model: app.Models.Field
});
app.Models.Form = Backbone.Model.extend({
formid : "1",
fields: new app.Collections.Fields(),
initialize: function() {
}
});
How do I split up my JSON response into all these models and collections?
(Perhaps I should re-evaluate my approach, and go for something like form.fieldList and form.optionList[fieldListId] instead. If so, how would that look like?)
Edit: Here is a little jsfiddle after many fixes, but I still don't really know how to make the inner options list work.
The easiest solution would be using Backbone Relational or Backbone Associations.
The documentation should be enough to help you get started.
If you don't want to use a library you could override the parse function on the Form model.
app.Models.Form = Backbone.Model.extend({
defaults: {
fields: new app.Collections.Fields()
},
parse: function(response, options) {
return {
formid: response.formid,
fields: new app.Collections.Fields(_.map(response.fields, function(field) {
if (field.options) {
field.options = new app.Collections.Options(field.options);
}
return field;
}))
};
}
});
Now if you fetch a form from the server, the response will be parsed into an object graph of models and collections.
form.get('fields') will return an app.Collections.Fields collection. form.get('fields').first().get('options') will return an app.Collections.Options collection, if any options exist.
Also, you could create the form model like this:
var form = new app.Models.Form(JSONresponse, {
parse: true
});
This would result in the same object structure.
It's quite hard to handle the case of nested models and collections right in plain Backbone.
Easiest way of handling this will be something like this:
var Option = Nested.Model.extend({
idAttribute : 'optionid',
defaults : {
optionid : Integer
value : ""
}
});
var Field = Nested.Model.extend({
idAttribute : 'fieldid',
defaults : {
fieldid : Integer,
title : "",
helper : "",
type : "radio",
options : Option.Collection
}
});
var Form = Nested.Model.extend({
idAttribute : 'formid',
defaults : {
formid: Integer,
fields: Field.Collection
});
https://github.com/Volicon/backbone.nestedTypes
And that's it. Yep, you'll get direct access to the attributes as free bonus, just form.fields.first().options.first().value, without that get and set garbage.

Foreign key populated with an object

I would like to make a relation between two models User and Task using backbone-relational.
The relation between the two models is the following:
taskModel.creator_id = userModel.id
// TaskModel
var TaskModel = Backbone.RelationalModel.extend({
relations: [
{
type: Backbone.HasOne,
key: 'creator',
keySource: 'creator_id',
relatedModel: Users
}
],
// some code
});
// Task collection
var TaskCollection = Backbone.Collection.extend({
model: TaskModel,
// some code
});
// User Model
var User = Backbone.RelationalModel.extend({
// some code
});
Actually the problem is in the collection.models, please see the attached images:
Please check this jsfiddle: http://jsfiddle.net/2bsE9/5/
var user = new User(),
task = new Task(),
tasks = new Tasks();
task.fetch();
user.fetch();
tasks.fetch();
console.log(user.attributes, task.attributes, tasks.models);
P.S.:
Actually I am using requireJs to get the UserModel, so I cannot include quotes in relatedModel value.
define([
'models/user',
'backbone',
'relationalModel'
], function (User) {
"use strict";
var Task = Backbone.RelationalModel.extend({
relations: [
{
type: Backbone.HasOne,
key: 'creator',
keySource: 'creator_id',
relatedModel: User
}
],
});
);
Edit 2:
http://jsfiddle.net/2bsE9/13/
I updated the jsfiddle to reflect the changes I suggested below. As long as you are calling toJSON on your task, what gets to the server is a json object with the creator_id property set to the actual id of the user. The keyDestination here is redundant as the documentation states it is set automatically if you use keySource.
Edit:
https://github.com/PaulUithol/Backbone-relational#keysource
https://github.com/PaulUithol/Backbone-relational#keydestination
https://github.com/PaulUithol/Backbone-relational#includeinjson
The combination of the three above might solve your issue.
var Task = Backbone.RelationalModel.extend({
relations: [
{
type: Backbone.HasOne,
// The User object can be accessed under the property 'creator'
key: 'creator',
// The User object will be fetched using the value supplied under the property 'creator_id'
keySource: 'creator_id',
// The User object will be serialized to the property 'creator_id'
keyDestination: 'creator_id',
// Only the '_id' property of the User object will be serialized
includeInJSON: Backbone.Model.prototype.idAttribute,
relatedModel: User
}
],
});
The documentation also states that the property specified by keySource or keyDestination should not be used by your code. The property cannot be accessed as an attribute.
Please try this and comment if that fixes your issue.
Btw, here is a nice blog post that uses backbone-relational end to end.
http://antoviaque.org/docs/tutorials/backbone-relational-tutorial/
Edit
Updated jsfiddle
The problem is that Backbone-Relational explicitly deletes the keySource to 'prevent leaky abstractions'. It has a hardcoded call to unset on the attribute, in Backbone-Relational:
// Explicitly clear 'keySource', to prevent a leaky abstraction if 'keySource' differs from 'key'.
if ( this.key !== this.keySource ) {
this.instance.unset( this.keySource, { silent: true } );
}
You will need to overwrite the unset method in your Task model:
var Task = Backbone.RelationalModel.extend({
urlRoot: ' ',
relations: [
{
type: Backbone.HasOne,
key: 'creator',
relatedModel: User,
keySource: 'creator_id'
}
],
unset: function(attr, options) {
if (attr == 'creator_id') {
return false;
}
// Original unset from Backbone.Model:
(options || (options = {})).unset = true;
return this.set(attr, null, options);
},
sync: function (method, model, options) {
options.success({
id: 1,
name: 'barTask',
creator_id: 1
});
}
});
Obvious problems with this approach are that you will need to modify your code if either Backbone changes its Backbone.Model.unset method or Backbone-Relational changes its keySource behavior.

ExtJS: return total rows/records in json store

I have a json store that returns values in json format. Now I need to get the number of rows/records in the json string but when I use store.getCount() function it returns 0, but the combobox is populated with rows, and when I use store.length I get undefined, probably because its not an array anymore, its returning from store, which is calling php script. Anyways, whats the best approach for this problem?
Try this out:
var myStore = Ext.extend(Ext.data.JsonStore, {
... config...,
count : 0,
listeners : {
load : function(){
this.count = this.getCount();
}
}
Ext.reg('myStore', myStore);
and then use inside panels:
items : [{
xtype : 'myStore',
id : 'myStoreId'
}]
Whenever you need to get the count then you can simply do this:
Ext.getCmp('myStoreId').count
Your Json response from server, can be something like this...
{
"total": 9999,
"success": true,
"users": [
{
"id": 1,
"name": "Foo",
"email": "foo#bar.com"
}
]
}
Then you can use reader: {
type : 'json',
root : 'users',
totalProperty : 'total',
successProperty: 'success'
} in your store object.
As from docs if your data source provided you can call getTotalCount to get dataset size.
If you use ajax proxy for the store, smth like
proxy : {
type : 'ajax',
url : 'YOUR URL',
reader : {
type : 'json',
root : 'NAME OF YOUR ROOT ELEMENT',
totalProperty : 'NAME OF YOUR TOTAL PROPERTY' // requiered for paging
}
}
and then load your store like
store.load();
There will be sent Ajax asynchronous request, so you should check count in callback like this
store.load({
callback : function(records, operation, success) {
console.log(this.getCount()); // count considering paging
console.log(this.getTotalCount()); // total size
// or even
console.log(records.length); // number of returned records = getCount()
}
});

Categories