ExtJS 4.1 - Retrieve hasOne information for Nested JSON - javascript

I am attempting to retrieve the information for a Model and an associated model that contains a hasOne association. I was referencing the following Sencha documentation page (http://docs.sencha.com/ext-js/4-1/#!/guide/data). I currently have the following sample code working:
var Mtd = Ext.ModelMgr.getModel('Mtd');
Mtd.load(4, {
success: function(mtd){
console.log("Loaded! " + mtd.get('id'));
mtd.getTreatmentdesign(function(treatment,operation){
console.log(treatment.get('id'));
}, this);
}
});
Now, when I call mtd.getTreatmentdesign(), I notice that two requests are made to retrieve information. The first one is to retrieve the Mtd information which I am expecting but then it's also making a request to retrieve the Treatmentdesign information. The response for the Mtd contains the Mtd information as well as the Treatmentdesign information. So I want to process the Mtd and Treatmentdesign information with one request. It puzzled me that the documentation stated the following:
You may be wondering why we passed a success function to the User.load call but didn't have to do so when accessing the User's posts and comments. This is because the above example assumes that when we make a request to get a user the server returns the user data in addition to all of its nested Posts and Comments. By setting up associations as we did above, the framework can automatically parse out nested data in a single request.
So how can I retrieve associated information without having to make another request? I simply just want to use all the json from a single request as opposed to having to make multiple requests.

Be sure to set the associationKey config on the HasOne association to the property that contains the data for the associated model. By default, this is the name of the associated model class in all lowercase letters.
For instance, if the data for an Mtd record is returned by the server in the form
{
...
treatmentDesign: {
...
}
}
set the associationKey to 'treatmentDesign'.
Here's an example in action: http://jsfiddle.net/HP6fq/3/

Yes, associationKey works
Ext.define('User', {
extend:'Ext.data.Model',
fields: ['id', 'name', 'status'],
associations: [{ type: 'hasOne', model: 'Status', associationKey: 'status' }]
});
Ext.define('Status', {
extend:'Ext.data.Model',
fields: ['id', 'title'],
});
Demo here

Related

Extjs store sync is not working when i insert value in store

I have treePanel with widgetColumn which includes combobox widget in it wuth default text. My requirement is when i select the defalt text, one new record should get inserted in store & also get saved in database.
{
text: 'TC',
dataIndex: 'scrTC',
xtype: 'widgetcolumn',
widget: {
xtype: 'combo',
store: 'TCStore',
valueField: 'id',
displayField: 'name',
matchFieldWidth: false,
listeners: {
select: 'selectDefault'
}
}
}
Controller Method:
selectDefault: function(combo){
loadData(combo, id, name); //there is a logic to get id & name, then pass it to loadData method
}
loadData: function(combo, id, name){
var store = combo.getStore();
store.insert(0,{id: id, name: name});
store.sync();
combo.setValue(id);
}
Issue is when i first time select default text, store sync method is not inserting the data in database but the combo show the new value & store also the new value(seen using debugger).
When i select again then the data is inserted into database.
I Debugged code, the execution flow is correct, only thing is sync is not calling backend to insert data at first instance, but works for second time.
Can someone help.
If you assign id to the inserted record, sync thinks, that it already exists in the DB and won’t fire add event (inserted record won't get phantom property). Either rename id field or set idProperty for the model to something else.
The store will use it's proxy to sync the data with your backend, so ensure that it is configured to the type of backend that you are using. The store itself may be configured with a proxy, or it may be defaulting to the proxy on it's model. Without seeing the store configuration, I can't say for sure.
For example, if you are using a REST backend, then use a REST proxy on the model that the store is configured with: https://docs.sencha.com/extjs/6.6.0/modern/Ext.data.proxy.Rest.html

Update a OnDemandGrid based on dstore/Rest result in POST and not PUT

I am puzzled.
I have an OnDemandGrid editable, and under it i have a dstore/Rest collection pointing to my backend (in PHP).
The OnDemandGrid is mixed with the Editor... I can edit my data, but i cannot save it. What i receive on the server side is a "POST" request to insert a full row in the collection... And i never recieve the update.
Should'nt i receive a PUT request instead? I am using id's in the data...
This is the client-side part:
function (...)
{
var EditGrid = declare([ OnDemandGrid, Keyboard, Editor ]);
var coll = new Rest({
target: 'my.php/x/',
idProperty: 'id',
rangeStartParam: 'range',
rangeCountParam: 'limit',
sortParam: 'sort'
});
var grid = new EditGrid({ columns: {
user_name:{
label: 'User name',
editor: 'text',
editOn: 'click, dbclick',
autoSave: true,
}},
collection: coll }, 'grid' );
grid.startup();
}
I correctly receive the GET query to populate the table... Then, after editing a row and hitting "return", i get a POST!
The server side is a bit more complex to show here... Basically, on GET i do an SQL query and json-ize the results, while on POST i just return this:
http_response_code(201);
header("location: ".$_SERVER['PATH_INFO'].$id);
Where $id is the same ID i received from the request...
After the POST, i don't receive anything else. And in the POST data, i only ever receive a copy of the old, not modified, row... I never receive the "new" edited data.
It seems to me i should receive a PUT request at some point... I tried the browser debugger, server logs, nothing anywhere.
Can anybody help me out here?
I finally fixed it.
It's very convoluted and very little documented all this mess. I had to dig into the browser debugger and dgrid/Rest source code a bit.
So the problem was all in my REST backend. It turns out dgrid does a GET before the PUT/POST requesting the item to be modified, and it does a GET with only one record by asking for the specific "id". It make sense.
Well, my backend would return an ARRAY with one element in it, in JSON format. This was the error! This is not properly parsed by dgrid and it led to the POST instead of PUT.
Once i fixed the GET backend to return a single JSON item instead of an array with one JSON item inside, dgrid started sending the correct PUTs.

How to copy data from OData v4 to JSON with SAP UI5?

I'm using OData v4 to load data from my backend to my frontend (developed with SAP UI5) and I am using a form to display a detail page. When I click the "edit" button I'm able to edit the data. My implementation is similar to this example: https://sapui5.hana.ondemand.com/explored.html#/sample/sap.ui.layout.sample.Form354/code/Page.controller.js
When editing something, the data is directly edited at the model and, therefore, updated at the backend. However, I want to be able to choose if I want to save the changes or if I want to cancel the edit before it is updated at the backend.
I read on other questions that one can copy the ODataModel to a JSONModel and use that copy instead, by doing something like:
var oModel = this.getView().getModel();
var oModelJson = new sap.ui.model.json.JSONModel();
oModel.read("/Data", {
success: function(oData, response) {
oModelJson.setData(oData);
sap.ui.getCore().setModel(oModelJson, "oJSONModel");
alert("Success!");
},
error: function(response) {
alert("Error");
}
});
However, the read method seems not to be available for OData v4. the code of my controller where the data is loaded looks like following:
onInit: function() {
this.oModel = new ODataModel({
groupId : "$direct",
synchronizationMode : "None",
serviceUrl : '/odata/'
});
this.getView().setModel(this.oModel, 'oModel');
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.getRoute("details").attachPatternMatched(this._onObjectMatched, this);
this._showFormFragment("display");
},
_onObjectMatched: function (oEvent) {
this.getView().bindElement({
path: "/Data(" + oEvent.getParameter("arguments").dataPath + ")",
model: "oModel"
});
//I want to copy the data from the ODataModel to the JSONModel here
},
What's the best way to accomplish this? And how to do it with OData v4?
I suppose you want to resetChanges in case user cancels the save.
For V2 ODataModel, there is deferedGroup concept which you can use to resetChanges or submitChanges.
I have not much experience with V4. Though from the documentation, it is possible.
Please try to pass a updateGroupId in the constructor. Then you can choose resetChanges or submitBatch by group Id.
mParameters.updateGroupId? The group ID that is used for update requests. If no update group ID is specified, mParameters.groupId is used. Valid update group IDs are undefined, '$auto', '$direct' or an application group ID, which is a non-empty string consisting of alphanumeric characters from the basic Latin alphabet, including the underscore.
Thank you!

Fetch Backbone Collection by Model IDs list

I have a REST API serving a few URLs:
/rest/messages
provides all messages. A message is a JSON/Backbone Model
{
title: 'foo',
body : 'bar'
}
To get a single message I have:
/rest/messages/:id
Is it possible to fetch a Backbone Collection using message IDs array? I don't want the whole message list, but just a few messages I specify by ID.
I could fetch Models one-by-one and fill up the Collection, but I'm wondering if Backbone has a cleaner way to do this.
Thanks
According to documentation, you can pass ajax options to the fetch call. So, you can pass ids as data attribute to the fetch call being done and based on it, return the respective models from the server.
For example (when doing fetch),
collection.fetch({
data : {
message_ids : [1, 3, 5] // array of the message ids you want to retrieve as models
}
})
This message_id array will be accessible as parameters (not sure of the name in your case) in the server code being executed at /rest/messages, from there you can return only specific models based on ids you receive as message_ids. The only thing you need is, client side must be aware of the ids of all the message models it needs.
You can use any data structure instead of array to send message_ids.
The url property of collection reference to the collection location on the server. When you use fetch, backbone uses that url.
The url property can be also a function that returns the url. So you can do something like that:
var ids = [1,2,3]
var messages = new MessegecCollection();
messages.url = function() {
return "/rest/messages/"+ids.join("-"); //results "/rest/messages/1-2-3"
}
messages.fetch();
You can also create a method in your collection that generated and set the url, or even fetchs a set of models.
Now all you have to do is to support this url: /rest/messages/1-2-3
Hope this helps!

Backbone-relational: Association key won't work unless it's the same as the foreign key

I'm trying to get the backbone-relational plugin working with an association between tasks and messages. (A task has many messages).
The information is pulled from a standard rails/activerecord site, which has a task_id field as the foreign key.
The problem is, backbone-relational won't populate the 'messages' field with any messages on teh Task model unless I set the key as "task_id" in the reverse relation...but that means that, when accessing the task from the Message model, the task_id field is populated with the actual task object, not the 'task_id' integer, which is overwritten.
I'm guessing there's a simple way to specify task_id as the foreign key with which to determine the parent task, yet have the object that key represents placed in a different field (eg 'task' on the messages object)...but I can't figure out how. Any ideas appreciated. Code below
class Backbonescaffolddemo.Models.Task extends Backbone.RelationalModel
paramRoot: 'task'
relations: [{
type: Backbone.HasMany,
key: "messages",
relatedModel: "Backbonescaffolddemo.Models.Message",
collectionType: "Backbonescaffolddemo.Collections.MessagesCollection",
includeInJSON: true
reverseRelation: {
key: "task_id"
includeInJSON: true
}
}]
You may be able to use keySource or keyDestination to address your particular problem.
Example
In the following example, suppose we are getting data from an old-school relational database, where there is a one-to-many relationship between Monster and Loot_Item. This relationship is expressed by a Monster_Id foreign key in the Loot_Item table. Let us also suppose that our REST service doesn't do any fancy-pants data nesting for us, since that seems to match the situation in your question fairly closely.
keySource
Now, let's set set "keySource" to my foreign key ("Monster_Id") and "key" to the name of the attribute where I want the actual data to go (say, "Monster"). If you break in the debugger, you will see in the attributes object that there is, in fact, a field called "Monster", and that it does point to the monster model data. Hey, cool!
includeInJSON
However, if you toJSON that puppy, guess what? It has put all the monster data in Monster_Id, just like you didn't want! GAH! We can fix that by setting "includeInJSON" to "Monster_Id". Now, when it is converted to JSON, it puts the proper ID back into the Monster_Id field, when it is serializing your data to JSON, to send up to the server.
Problem solved? Er, well, actually, not necessarily...
CAVEAT: This all sounds super-useful, but there's one fairly glaring problem that I have found with this scenario. If you are using a templating engine (such as the one in Underscore.js) that requires you to convert your model to JSON, before passing it into the template, whoops -- you don't have access to your relational data. Alas, the JSON that we want for our messages is not necessarily the same JSON that we want to feed into our templates.
If you want the "task_id" in the message JSON to be the id, not the full JSON for the task, then set the "includeInJSON" to be the Task's ID property ("task_id")
class Backbonescaffolddemo.Models.Task extends Backbone.RelationalModel
paramRoot: 'task'
relations: [{
type: Backbone.HasMany,
key: "messages",
relatedModel: "Backbonescaffolddemo.Models.Message",
collectionType: "Backbonescaffolddemo.Collections.MessagesCollection",
includeInJSON: true
reverseRelation: {
key: "task_id"
includeInJSON: "task_id"
}
}]
The "true" value for includeInJSON says to use the full JSON for the related model.
Edit: After re-reading your question, I'm not sure my answer relates to your issue.
My original answer is for posting a message back to the server where you want the JSON to be something like:
{
"message_title": "My Title",
"message_body": "Blah blah blah...",
"task_id": 12345
}
I'm not sure what exactly you're looking to happen, but the way that Backbone Relational is supposed to work is that the Task's collection of messages will be a collection of the full models, so you can iterate over them and pass them to views for rendering, etc.
If you want to output one of the Message's id's in a template or something, then you'd take the Message model's "id":
myTask.get('messages').first().id -> returns the first message's id

Categories

Resources