I am working on extJs 3.2.x
I have a simple function which returns a string.
public Object getRevenueCurrency(....) {
return "USD";
}
I have Jackson mapper to map the response type to JSON.
<bean name="jsonView" class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
<property name="contentType">
<!-- <value>application/json</value> -->
<value>text/html</value>
</property>
</bean>
An attempt to retrieve the data goes like this:
currencyStore = new Ext.data.JsonStore({
id:'currencystore',
url: 'xxxxxxx?action=getcurrency',
root: 'string',
listeners: {load: function(store) {
rev_currency=store.?????;
}
}
});
currencyStore.on('exception',function( store, records, options ){
alert('Exception was called');
},this);
Fiddler shows a response from server as :
{"string": "USD"}
Although I get no server or js exception,the exception alert is called.
1.How do i extract the currency value ?
2.What is a way to extract a meaningful information on the exception in the exception handler above?
Concerning your second question:
2.What is a way to extract a meaningful information on the exception in the exception handler above?
According to the docs for the exception event the signature and parameters are:
exception( store, type, action, options, response, arg )
By inspecting the parameters (e.g. by console.log(...)) you should retrieve some useful information.
I notice you have not configured any fields on your store, so when the reader takes your json respsonse its trying to find a field named 'string' to bind to.
Take a look at the ExtJS3 docs for the JsonStore -> http://docs.sencha.com/extjs/3.4.0/#!/api/Ext.data.JsonStore
The sample config shows you how the fields are defined, so your store should look like:
currencyStore = new Ext.data.JsonStore({
id:'currencystore',
url: 'xxxxxxx?action=getcurrency',
fields: ['string'],
listeners: {load: function(store, records, options) {
rev_currency=store.?????;
}
}
});
Note I have also changed the params to your load function handler, so the new record created from your json can be accessed using records[0]
You have configured the store by setting the root property which means the store's reader is expecting a root node in the json matching 'string'.
So the reader is actually expecting the below form of response.
{"string":[{"propertyname":"USD"}]}
Instead you could just take out the root property on your store config since all that's being returned is a flat object
Related
Kendo Grid is new to me, so I apologize for the ignorance. I'm writing an angular app that uses a separate service to update a local array. It is store in $scope.searchResults variable. I've initialized the grid using the dataSource ->transport property in the hopes that when the array mentioned above is updated, so too will the datasource and the grid updated accordingly. This is not the case. The array is updated, without any problems, but the datasource is never updated. I'll do my best to paste all the code snippets, and console output below.
Html:
<div class="margin-top-25" ng-show="searchResults">
<div id="report-grid" kendo-grid="grid" options="mainGridOptions"></div>
</div>
DataSource proper of the Grid configuration:
dataSource: {
transport: {
read: function read(options) {
options.success($scope.searchResults);
}
},
schema: {
model: {
id: "id",
fields: {
name: {type: "string"},
dataSource: {type: "string"}
}
}
},
pageSize: 10
}
Function for updating the datasource:
function runSearch() {
RetrieveReportsService.query({name: vm.searchData.name, dataSource: vm.searchData.dataSource},
function success(result) {
$log.log($scope.grid.dataSource);
$log.log($scope.searchResults);
$scope.searchResults = result.elements;
$log.log($scope.searchResults);
$scope.grid.dataSource.read();
$log.log($scope.grid.dataSource);
});
}
Now console output:
First time logging the data source:
O…e.e…d.init {options: Object, _map: Object, _prefetch: Object, _data: ObservableArray.extend.init[2], _pristineData: Array[2]…}
First time logging $scope.searchResults:
[Object, Object]
Second time logging $scope.searchResults:
[Object]
Second time logging the data source:
O…e.e…d.init {options: Object, _map: Object, _prefetch: Object, _data: ObservableArray.extend.init[2], _pristineData: Array[2]…}
Note that each data source has an observable array length of 2, before and after the $scope.searchResults has been updated.
I can drill down into the output if it is needed, but didn't want this post to get overwhelming.
Thanks!
Because you are referencing your data that is declared inside your options object by pointing k–options at your options object when the grid initially binds to the options object it's not populated with the data yet. If you reference your data source object separately using k-data-source it will bind to your data source and update your grid when the data source changes. If you want to make changes to your options object trigger a rerender you need to use k–rebind or alternatively the setOptions method. Be sure to read the docs for the latter as there are some caveats.
Well, I don't know why it is working the way that it is, but with the current project configuration this is the solution.
The grid configuration and the function for updating the data source are in the same module. The grid itself is initialized in another controller. I had move the function for updating to the controller that contained the grid and now it works perfectly.
If anyone knows why, feel free to chime in.
I have a Model defined with a mapping:
Ext.define('IM.model.Source', { extend : 'Ext.data.Model', fields : [
{
"name": "billref_id",
"mapping": "billref.id"
},...
If I create a Grid with a Store using this Model and load it, every record in the Store has the billref_id attribute correctly initialised to the value of billref.id from my JSON data.
If I instead load the same data using an Ext.Ajax.request, and then feed that data into a Model instance this way:
var response = Ext.Ajax.request({
async: false,
method:'GET',
url: 'im_read.json',
params:{pkValue:1}
});
var items = Ext.decode(response.responseText);
record = Ext.create('IM.model.Source', items.rows[0]);
the resulting record does not have the mapped fields populated from the JSON data.
Only the non-mapped members of the model have values assigned.
Is it a known issue that creating and populating a Model instance this way doesn't work with mapped fields, or am I doing something wrong?
Thanks,
The mapping of a model's field is only used by a Ext.data.reader.Reader, which is usually configured on your store. If you are creating an instance of the model yourself, the mapping is not considered, since the reader is not invoked and model expects you to provide the data by the fields' names (see docs).
To solve your problem, you can just call the reader's read function with your AJAX object:
var response = Ext.Ajax.request({
async: false,
method:'GET',
url: 'im_read.json',
params:{pkValue:1}
});
var resultSet = myStore.getReader().read(response);
That will create an Ext.data.ResultSet containing your correctly-mapped records.
I'm using Worklight framework to construct a mobile app for IOS, and is using Sencha Touch 2.3 to build the app.
Due to the environment, i cannot use proxy in Sencha Touch Store/Model objects to load data from the server, as i would need to use Worklight's adapter to retrieve the info. I have managed to do that using some boilerplate codes.
However, i wish that i could utilize the Sencha Model more, and as such, am thinking whether it is possible for me to load a JSON object into the Model object automatically, without specifying a proxy.
Currently i'm doing a lot of loop and setter call to load the data from the JSON object to a model, like below:
var profile = Ext.create('Profile', {
Id: rawProfile.Id,
Name: rawProfile.Name
Age: rawProfile.Age
.....
}
where rawProfile is the JSON object i loaded from the server.
Any way i can make this cleaner?
You could create a Model class which would contain the data contained in your rawProfile object.
Ext.define('MyModel', {
extend: 'Ext.data.Model',
fields: [{
name: 'Id',
name: 'Age',
...
}],
proxy: {
type: 'memory',
reader: 'json'
}
});
I've also set an in memory proxy which will read json objects.
You could then create a Store which would use the model you defined and the in memory proxy (meaning you wouldn't be using Ext's build in ajax messaging).
Ext.create('MyStore', {
model: 'MyModel',
autoLoad: false
});
Setting the autoLoad to be false, as you want to get the data from a different source.
Therefore, once you have your json object you can load it into the store by calling store.loadRawData(jsonObject).
In this case, the jsonObject would be the object containing all the json objects returned from the server, meaning that your code doesn't have to handle iterating through the records and you can leave it to the Sencha classes.
However you would need to figure out the writing data back to the server.
I'd recommend running through the Sencha Data Package tutorial as it gives a good intro to the data package
If all the fields map 1:1 with the json object, you can do this:
var profile = Ext.create('Profile', rawProfile);
Alternatively (or to avoid the convert functions for fields to be called), you can directly set the data property on the created model.
var profile = Ext.create('Profile');
profile.data = rawProfile;
Again, this requires a 1:1 on the fields and json object.
I have been trying out backbone.js and have been stymied when I create a new model object then call model.save(). I am expecting the backbone.js default behavior to update the model object with the id from the database but it is not. Is this not supposed to happen? I have verified that I am getting a post with the attributes in json format. My server saves the json to a table and then returns the json with a new id field to backbone.js. Is this correct? Should my server return the entire new object or just the id or what?
//contents of the POST from backbone.js
{ "text":"this is a test" }
//reply from my server
{ id:"15", text:"this is a test" }
My sample code is below
var SQLRow = Backbone.Model.extend({
table:"",
urlRoot:'db',
url:function () {
return "/" + this.urlRoot + "?table=" + this.table +
"&id=" + this.attributes.id;
}
});
var Xtra = SQLRow.extend ({
table:'Xtra'
});
var row = new Xtra({
text: "this is a test"
});
alert(row.url());
row.save()
alert("row:" + row.get("id"));
Tough to tell from your post. Two ideas :
1) the response from the server isn't successful What does your save call return ?
2) Your "id" attribute is named something other than ID. To account for the different name add the following to your model :
idAttribute : "MyModelsID",
EDIT
You're likely facing a timing issue, where the alert fires before the ID has returned. Instead of your last two lines try this :
row.save( null,
{
success : function(model, response) { alert(model.get('id'); }
}
);
ALTERNATIVE
As #mu_is_too_short mentioned, another way is to listen for the change even on the model and respond to the event. (i was just trying to keep the answer as close to your code as possible). But something like the following pseudo code should get you started...
var myView = Backbone.View.extend({
....
initialize : function () {
this.collection.bind('change', this.SOME_LISTENING_FUNC );
}
});
OR, if you're in a collection/view-less world something like this creates a listenr ...
row.on('change', function() { /* do stuff */ }, this);
This answer is based on one comment of Cjolly in the answer above.
It is essential for making the Backbone.Model.save([attributes],[options]) successful in assiging the model with the newly generated model's id from the server, that the server returns the model's id in a JSON string like this { "id" : <the id> }. (note it is "id" and not id).
In essence backbone rightly expects a JSON string and in contrast to how objects may be defined in Javascript without quoted keys, JSON requires the object keys to be quoted (see JSON Spec - does the key have to be surrounded with quotes?)
Since according to Cjolly's comment this has been the essential problem, I want to hightlight this solution in an second answer. Partially because I was hit by the very same problem and only by reading througth the comments I was able to receive the insight.
I've faced the same issue and what I've found is that my validate function of the saved model actually invalidates the model returned from the back end. That's why my fields were not updated properly.
Maybe its a little outtimed, but today I had the same missing id.
It turns out, that the server just sends a Header 'Location' with a redirect containing the new id, but dosen't return the persisted object.
Adding the object to the response was the solution.
It seems, that not returning the object is standard behavier with Roo(Spring) generated Json-Controllers.
For example, say I have a server API for loading people that handles requests like this: GET /people/?id=101,329,27
I'd like to build a Store (probably a custom class that extends Ext.data.Store) which--assuming it has a list of people IDs--causes the proxy to make a request like the one shown above so that the returned data is only for that subset of persons.
I saw the documentation regarding remote filtering, but my concern is that to use it I would first need to call store.load() which would load all persons, then call filter() to do remote filtering. I'd like to just load the subset of persons the first time.
Thanks for any advice!
Found a solution (although still open to hearing other ideas).
First, you can call a store's load() function with a config object that will be passed to an operation. The API docs for Ext.data.Operation make it clear that one of the config options is for an array of Filter objects, so you can do this:
var idFilter = Ext.create('Ext.util.Filter', {
property: 'id',
value: '100,200,300'
});
myStore.load({
filters: [ idFilter ]
});
This results in a request where the URL querystring contains ?filter=[{"property"%3Aid%2C"value"%3A100,200,300}] (in other words, a URL-encoded version of [{ property: 'id', value: '100,200,300'}]).
You can also just call myStore.filter('id', '100,200,300') without having called .load() first. Assuming you have remoteFilter=true in your store, this will make a request with the same query params shown agove.
Sidenote: you can change the keyword used for 'filter' by configuring the 'filterParam' config option for the proxy. For example, if filterParam=q, then the querystring shown above changes to: ?q=[{"property"%3Aid%2C"value"%3A100,200,300}]
Second, you can control "structure" of the filter in the querystring. In my case, I didn't want something like filter={JSON}, as shown above. I wanted a querystring that looked like this:?id=100,200,300
For this I needed to extend a proxy and override the default getParams() function:
Ext.define('myapp.MyRestProxy', {
extend: 'Ext.data.proxy.Rest',
/**
* Override the default getParams() function inherited from Ext.data.proxy.Server.
*
* Note that the object returned by this function will eventually be used by
* Ext.data.Connection.setOptions() to include these parameters via URL
* querystring (if the request is GET) or via HTTP POST body. In either case,
* the object will be converted into one, big, URL-encoded querystring in
* Ext.data.Connection.setOptions() by a call to Ext.Object.toQueryString.
*
* #param {Ext.data.Operation} operation
* #return {Object}
* where keys are request parameter names mapped to values
*/
getParams: function(operation) {
// First call our parent's getParams() function to get a default array
// of parameters (for more info see http://bit.ly/vq4OOl).
var paramsArr = this.callParent(arguments),
paramName,
length;
// If the operation has filters, we'll customize the params array before
// returning it.
if( operation.filters ) {
// Delete whatever filter param the parent getParams() function made
// so that it won't show up in the request querystring.
delete paramsArr[this.filterParam];
// Iterate over array of Ext.util.Filter instances and add each
// filter name/value pair to the array of request params.
for (var i = 0; i < operation.filters.length; i++) {
queryParamName = operation.filters[i].property;
// If one of the query parameter names (from the filter) conflicts
// with an existing parameter name set by the default getParams()
// function, throw an error; this is unacceptable and could cause
// problems that would be hard to debug, otherwise.
if( paramsArr[ queryParamName ] ) {
throw new Error('The operation already has a parameter named "'+paramName+'"');
}
paramsArr[ queryParamName ] = operation.filters[i].value;
}
}
return paramsArr;
}
});
You can also get your Model object to load a record of itself. From a controller you can do:
this.getRequestModel().load(requestID,{ //load from server (async)
success: function(record, operation) {
.....
}
}
where Request is a model class and requestID is an ID to look up. In this scenario Model object needs to define the proxy too:
proxy: {
type: 'ajax',
reader: {
type:'json',
root: 'data'
},
api: {
create : 'request/create.json', //does not persist
read : 'request/show.json'
}
}