PDA

View Full Version : Model.load not sending id, loading all models



dewie
30 Jan 2011, 2:22 PM
I'm having a little trouble getting the hang of loading a single record from a model
I define a model:



var JobType = Ext.regModel('JobType', {
fields: [
{name: 'id', type: 'integer'},
{name: 'code' , type: 'sting'},
{name: 'name' , type: 'sting'}
],
proxy: {
type: 'rest',
url: '/job_types',
reader: {
type: 'json'
}

}
});


When i read the docs right, it should be possible to do :



JobType.load(1200,, {
success: function(jt) {
console.log(jt.getId()); //logs 1200
}
});


But the call that gets sent to the webserver is:

Started GET "/job_types?_dc=1296425984138" for 127.0.0.1 at Sun Jan 30 23:19:44 +0100 2011
Processing by JobTypesController#index as */*

The url generated should be GET "/job_types/1200" or am i reading the docs wrong ?

icflorescu
16 Feb 2011, 5:14 AM
Just wanted to bump this up, confirmed the same behavior...

ianc
24 Feb 2011, 4:49 AM
Me too, any suggestions as to what I am doing wrong?

gordonjl
28 Feb 2011, 2:01 PM
Same here with me. Here's my code:

The Model:

/**
* @class PublicResource
*/
Ext.regModel('PublicResource', {

idProperty:'id',
fields: [
{name:'id',type:'int'}, 'text', 'description', 'distance', 'latitude', 'longitude','website','category',
{
name: 'startDate',
type: 'date',
dateFormat: 'c'
},
{
name: 'endDate',
type: 'date',
dateFormat: 'c'
},
{
name: "phone",
type: "string"
}
],
proxy: {
type: 'rest',
url : 'vicinity',
format:'json',
reader:{
type:'json', root:'items'
}
}



});

The call to load:

var PublicResource = Ext.ModelMgr.getModel('PublicResource');

PublicResource.load(options.recordId, {success:function(resource) {
log.debug("fetched a record!");
},failure:function(record, operation) {
log.debug("failed to load! ")
} });

The issue seems to be in Ex.data.RestProxy.buildUrl(). It looks like the id is only appended if there is a record in the request:


buildUrl: function(request) {
var records = request.operation.records || [],
record = records[0],
format = this.format,
url = request.url || this.url;

if (this.appendId && record) { // <-- THE ID ISN'T APPENDED IF THERE IS NO RECORD.
if (!url.match(/\/$/)) {
url += '/';
}

url += record.getId();
}

if (format) {
if (!url.match(/\.$/)) {
url += '.';
}

url += format;
}

request.url = url;

return Ext.data.RestProxy.superclass.buildUrl.apply(this, arguments);
}
});


Any clever person come up with a workaround yet?

ksystems
1 Mar 2011, 6:11 AM
Having had a look at this it seems to me like operation.params is not being set when the load call is processed and the proxy read is being called. I have a config object with action: 'read' and id: <idval>, but the id passed through to the call to load never makes its way down to the operation.params.
Stepping into the code just before
this.proxy.read(operation, callback, this); and setting
operation.params = { id: <idval> } then letting the application continue running results in the id being passed down and the call returning me the values I expect.

Anyone any ideas on how to fix this?

gordonjl
1 Mar 2011, 6:26 AM
ksystems, I saw in my testing that the id was in the request that was sent to the
Ex.data.RestProxy.buildUrl() call (the request has the operation in it), it's just ignored.

ksystems
1 Mar 2011, 6:35 AM
ksystems, I saw in my testing that the id was in the request that was sent to the
Ex.data.RestProxy.buildUrl() call (the request has the operation in it), it's just ignored.

You're correct, of course. It just seems to never use the operation.id either in buildUrl or before that to apply it to operation.params before calling buildRequest(operation) where it uses operation.params to set the params property of the request.

JELaVallee
1 Mar 2011, 10:41 AM
From digging around in the source code for the Ext.data.RestProxy and the Ext.data.Model.load method, I don't see anywhere that the id is actually appended to the request.url in the operation.

I don't even see any references to the appendId parameter in the RestProxy config. It would appear that the docs are describing functionality that hasn't been implemented?

I've created my own CustomRestProxy extension that has an overridden buildUrl method which does the ID post-pending on the URL before calling the superclass buildUrl (ServerProxy's I believe).

Here's mine:



Ext.ux.CustomRestProxy = Ext.extend(Ext.data.RestProxy, {
/**
* Generates a url based on a given Ext.data.Request object. By default, ServerProxy's buildUrl will
* add the cache-buster param to the end of the url. Subclasses may need to perform additional modifications
* to the url.
* @param {Ext.data.Request} request The request object
* @return {String} The url
*/
buildUrl: function(request) {
var url = request.url || this.url;

if (!url) {
throw "You are using a ServerProxy but have not supplied it with a url. ";
}

// EXTENSION - BEGIN
// Append the node.id to the url if it exists and is not "root"
if( this.appendId ){
var nodeId = request.operation.node.id

if( nodeId !== "root" ){
request.url = url + "/" + nodeId;
}
}

// Call the super implementation for doing things like cache appending, etc.

return Ext.ux.CustomRestProxy.superclass.buildUrl.call(this, request);
// EXTENSION - END
}
});



Note that I'm honoring the appendId config parameter/property but unlike the documentation, I'm not differentiating between whether this is a Model.load call or a Store.load call... Mainly because I don't need such a differentiation in my app. Mileage may vary...

Anyone from Sencha Dev Team care to comment?

Thanks,
Etienne

ksystems
1 Mar 2011, 10:57 AM
Yes, clearly this seems like it's been overlooked and looking at preview 2 of Ext 4 it seems it will suffer from the same problem.

My question is how could this get past all those thousands of unit tests that were talked about at the conference as I can't see how this has never worked? I am assuming without hacking it ourselves there is no way to get this work?

I'll look at the solutions proposed above thanks.

JELaVallee
1 Mar 2011, 11:23 AM
I'm baffled too... really not the sort of thing I've come to expect from the Ext/Sencha crew after years of working with ExtJS. C'est la vie...

My solution above is not comprehensive, as I note. But it does get the job done. Since I don't use the Ext.data.Model structures directly for doing REST calls, I'm not that concerned, but it would only require a few minor overrides to the Model methods to get them in-line with using the id field properly.

word,
Etienne

sidragon
1 Mar 2011, 11:33 AM
Etienne, your solution works? I have tried appending the identifier value in my own RestProxy extension, but, while that makes the request valid, the Ext.data.Operation resultSet property is not populated after the request completes.

JELaVallee
1 Mar 2011, 11:51 AM
@sidragon: It's definitely working for me... I just put a breakpoint on Ext.data.Operation.setSuccessful and check the this.resultSet and it contained all of the correct response values from my JSON service.

Give it a shot an let me know what you're seeing. And not that my use of this is going through a Ext.data.ServerStore (TreeStore actually), so it may be different if you're using the Ext.data.Model.load(id, ...) method. But I think it should work this way too.

Cheers,
Etienne

sidragon
1 Mar 2011, 12:09 PM
ksystems, I back-ported the Ext.data.RestProxy#buildUrl implementation from Preview 2, and it worked. You can easily prepare for the fix without downstream surprises by registering the proxy type as follows.


Ext.data.ProxyMgr.registerType('rest', Ext.extend(Ext.data.RestProxy, {
buildUrl: function(request) {
var operation = request.operation,
records = operation.records || [],
record = records[0],
format = this.format,
url = request.url || this.url,
id = record ? record.getId() : operation.id;

if (this.appendId && id) {
if (!url.match(/\/$/)) {
url += '/';
}

url += id;
}

if (format) {
if (!url.match(/\.$/)) {
url += '.';
}

url += format;
}

request.url = url;

/* Deliberately skip the superclass implementation. */
return Ext.data.RestProxy.superclass.buildUrl.apply(this, arguments);
}
}));

sidragon
1 Mar 2011, 12:12 PM
I just put a breakpoint on Ext.data.Operation.setSuccessful and check the this.resultSet and it contained all of the correct response values from my JSON service.

If I wrap single-record retrieval results in arrays, it all works (once the identifier gets appended). Odd—and apparently necessary per certain examples (http://dev.sencha.com/deploy/DataDemo/)—but functional.

atwoodjw
2 Apr 2011, 5:48 PM
If I wrap single-record retrieval results in arrays, it all works (once the identifier gets appended). Odd—and apparently necessary per certain examples—but functional.

Can someone explain this a bit more? I'm using sidragon's RestProxy and now asynchronous calls have the correct ID in the URL, but the record being returned in the success callback is undefined.

I can see operation.response.responseText contains the correct data, but operation.records is empty.

Thanks for your help.

P.S. Has anyone posted a bug for this issue on the bug forum?

sidragon
3 Apr 2011, 6:29 AM
I can see operation.response.responseText contains the correct data, but operation.records is empty.

Only arrays get parsed, even when retrieving single records. Once I wrapped the response, everything worked as expected.

sandeep45
29 Aug 2011, 6:42 AM
model.load(id, {});

the url generated doesnt have the id in it.

sidragon
29 Aug 2011, 6:48 AM
model.load(id, {});

[T]he [URL] generated doesnt have the id in it.

Ext.data.Model class does, itself, not generate the URL—that responsibility lies with the Ext.data.Proxy implementation. Which type are you using? (Incidentally, there is an outstanding but with Ext.data.RestProxy that you may be encountering.)

sandeep45
29 Aug 2011, 7:09 AM
I am using a Rest Proxy.

The only way I could get the model.load(id,{}); send the id appended to the url is by having a custom implementation fo the buildUrl method.

After having a custom buildUrl request is made for:

http://localhost:3001/ajax/sampleCustomer.json/1234?_dc=1314629847728



proxy: {
type: 'rest',
url: 'ajax/sampleCustomer.json',
reader: {
type: 'json',
root: 'resources',
successProperty: 'meta.success',
totalProperty: 'meta.total',
idProperty: 'id'
},
writer: {
type: 'json',
root: 'resources'
},
appendId: true,
pageParam:null,
limitParam:null,
startParam:null,
buildUrl: function(request) {
console.info("customizing the url");
console.dir(request);
var url = request.url || this.url;

if (!url) {
throw "You dont have a URL configured??? Where should i make the request to";
}

// Append the node.id to the url if it exists and is not "root"
if( this.appendId ){
var nodeId = null;
if(request.action=="read"){
nodeId = request.operation.id;
}else if(request.action=="update"){
nodeId = request.records[0].data.id;
}
console.info(nodeId);

if( nodeId !== "root" ){
request.url = url + "/" + nodeId;
}
}
return Ext.data.RestProxy.superclass.buildUrl.call(this, request);
}
}

sidragon
29 Aug 2011, 7:34 AM
I posted earlier (http://www.sencha.com/forum/showthread.php?122595-Model.load-not-sending-id-loading-all-models&p=576217&viewfull=1#post576217) about overriding with the buildUrl implementation from Ext JS 4. Did you try that?

sandeep45
29 Aug 2011, 7:48 AM
Yes I just tried and it works like a charm. I like it and would use it.

How do you usually arrange and include bug fixes. I am thinking of having a separate file for each such bug fix and include it before the models, stores, views and controllers files. Just wanted to get your feedback on how you are arranging these fixes?

sidragon
29 Aug 2011, 8:10 AM
Yes I just tried and it works like a charm. I like it and would use it. … I am thinking of having a separate file for each such bug fix and include it before the models, stores, views and controllers files. Just wanted to get your feedback on how you are arranging these fixes?

That sounds about right. The idea is that you add no functionality and avoid coupling the backported blocks with your existing codebase, all so you can remove these patches safely if and when the fix comes with future Sencha Touch releases. Also, I was foolish in using extensions. Ext.override is preferable in this case. Use the following block instead.


Ext.override(Ext.data.RestProxy, {
buildUrl: function(request) {
var operation = request.operation,
records = operation.records || [],
record = records[0],
format = this.format,
url = request.url || this.url,
id = record ? record.getId() : operation.id;


if (this.appendId && id) {
if (!url.match(/\/$/)) {
url += '/';
}


url += id;
}


if (format) {
if (!url.match(/\.$/)) {
url += '.';
}


url += format;
}


request.url = url;

return Ext.data.RestProxy.superclass.buildUrl.apply(this, arguments);
}
});

Note that I still have not used the implementation from the latest Ext JS 4 release (4.0.2a as of this writing). It depends on additional instance methods which are not present in Ext.data.RestProxy as found in Sencha Touch 1.1.0.

sandeep45
29 Aug 2011, 9:27 AM
any specific reason why you commented the line which adds the trailing slash

//url += '/';

sidragon
29 Aug 2011, 9:40 AM
[A]ny specific reason why you commented the line which adds the trailing slash?

Egg on my face! 8-| If I recall correctly, that was specific to my project where the trailing slash was confusing our RPC processor. Never mind that, and I will remove it from my previous posts.

tthai
2 Nov 2011, 8:27 PM
Thanks a zillion for this patch. This seems like such a fundamental behavior that should just work with the sencha proxy! Anyhow, this will work for now until they fix it.

tthai
2 Nov 2011, 8:49 PM
Can someone explain this a bit more? I'm using sidragon's RestProxy and now asynchronous calls have the correct ID in the URL, but the record being returned in the success callback is undefined.

I can see operation.response.responseText contains the correct data, but operation.records is empty.

Thanks for your help.

P.S. Has anyone posted a bug for this issue on the bug forum?


I'm getting the same issues...the request is appending the id, bu the record coming back is undefined.