How do i modify a raw data object returned by an ExtJS AJAX proxy into a JSON object to be consumed by a Tree Store - extjs

In an effort to create a treepanel, i configure it with a treestore whose AJAX proxy url receives json data i have no control of. But using Ext.data.reader.Json's transform property invokable before readRecords executes, gives an option to modify the passed raw (deserialized) data object from the AJAX proxy into a modified or a completely new data object. The transform config, gives the code snippet below:
Ext.create('Ext.data.Store', {
model: 'User',
proxy: {
type: 'ajax',
url : 'users.json',
reader: {
type: 'json',
transform: {
fn: function(data) {
// do some manipulation of the raw data object
return data;
},
scope: this
}
}
},
});
I would please like an example on how to go about modifying the return JSON object
[
{
"id": 3,
"attributes":
{},
"name": "user_one",
"login": "",
"email": "user_one#ats",
"phone": "0751223344",
"readonly": false,
"administrator": false,
"password": null
},
{
"id": 4,
"attributes":
{},
"name": "user_two",
"login": "",
"email": "user_two#ats",
"phone": "0751556677",
"readonly": false,
"administrator": false,
"password": null
}
]
into a JSON object fit for a treestore.
The hierarchical tree is to be rendered to show which user is under which admin using a condition administrator==true from the returned JSON, then a second AJAX request that returns that admin's users shown here.
[
{
"user_id": 3,
"admin_id": 1,
},
{
"user_id": 4,
"admin_id": 2,
}
]

Is the data nested at all? Otherwise why use a treepanel instead of a grid? To your question though, it'll depend on how you configure your treepanel but it would probably be something like this:
transform: {
fn: function(data) {
var treeRecords = Ext.Array.map(data, function(i){
return {
text: i.name,
leaf: true
//any other properties you want
}
});
var treeData = {
root: {
expanded: true,
children: treeRecords
}
};
return treeData;
},
scope: this
}

Related

Unable to transform the data for rendering line charts: Highcharts+React

I am actually new to this highcharts . Been trying to render a line chart . I am facing issues while transforming the data returned by back-end to the data required by highcharts.
Can someone suggest me how to transform the below data object to the data required by line charts.Trying to plot a graph that compares current and previous values
Help would be appreaciated.
Object
{"data":
[
{"currentVal":3488,"prevVal":0,"timestamp":1554181200000},
{"currentVal":3453,"prevVal":3,"timestamp":1554481200000},
{"currentVal":3456,"prevVal":2,"timestamp":1554581200000}
]
}
As per the documnentaion the line charts data accepts the following structure.
"data": [
{
"name": "currentVal",
"data": [ 7,7,8]
},
{
"name": "prevVal",
"data": [1,6,7]
}
]
}
I would want the help in transforming the object that mentioned in the top
The simplest way to transform the object:
var obj = {
data: [{
"currentVal": 3488,
"prevVal": 3000,
"timestamp": 1554181200000
}, {
"currentVal": 3453,
"prevVal": 3123,
"timestamp": 1554481200000
}, {
"currentVal": 3456,
"prevVal": 3341,
"timestamp": 1554581200000
}]
};
Highcharts.chart('container', {
xAxis: {
type: 'datetime'
},
series: [{
name: "currentVal",
data: obj.data.map(elem => [
elem.timestamp, elem.currentVal
])
}, {
name: "prevVal",
data: obj.data.map(elem => [
elem.timestamp, elem.prevVal
])
}]
});
Demo:
https://jsfiddle.net/BlackLabel/y8efg4hx/1/
https://jsfiddle.net/BlackLabel/0fzjsuLw/1/

ExtJS 6 inherited hasMany-Relation nested loading

I try to setup a hasMany relationship and want load my main entity with all the associated models in a single request.
But that seems not to work with "hasMany" relations that is inherited.
I have a BaseModel that defines all relations and fields and a "normal" model that defines the proxy to load from.
These are my (relevant) models:
Ext.define('MyApp.model.BaseUser', {
"extend": "Ext.data.Model",
"uses": [
"MyApp.model.UserEmail",
"MyApp.model.Account"
],
"fields": [
{
"name": "name"
},
{
"name": "accountId",
"reference": {
"type": "MyApp.model.Account",
"role": "account",
"getterName": "getAccount",
"setterName": "setAccount",
"unique": true
}
}
]
"hasMany": [
{
"name": "emails",
"model": "MyApp.model.UserEmail"
}
],
});
Ext.define('MyApp.model.User', {
extend: "MyApp.model.BaseUser",
proxy: {
type: 'rest',
url : '/api/user',
reader: {
type: 'json',
rootProperty: 'data',
}
}
});
Ext.define('MyApp.model.UserEmail', {
extend: 'Ext.data.Model',
"fields": [
{
"name": "id",
"type": "int"
},
{
"name": "email",
"type": "string"
},
],
proxy: {
type: 'rest',
url : '/api/user/email',
reader: {
type: 'json',
rootProperty: 'data',
}
}
});
// MyApp.model.Account looks like MyApp.model.UserEmail
This is my server's response:
{
data: [
{
name: 'User Foo'
accountId: 50
account: {
id: 50,
balance: 0
},
emails: [
{
id: 70,
email: 'hello#world.de'
}
]
}
]
}
The "account" relation is working on the "normal" User Model and I can access it via the auto-generated method user.getAccount() as I expected.
Now I tried to access the users emails with the auto-generated methods:
// user is of 'MyApp.model.User'
user.emails(); // store
user.emails().first(); // null
user.emails().count(); // 0
It seems that the "emails"-relation models were not loaded into my user model. Am I accessing them the right way?
I can access them via user.data.emails. But this is an array of plain objects, not of UserEmail-Objects.
Can anyone give me some advice? Is nested loading supported with keyless associations?
Kind regards,
czed
Edit:
Clearified what I ment.
It should work. Here's a working fiddle. Check console for nested loaded data.

Loopback, AngularJS and validation

I followed this tutorial to create a project with Loopback and AngularJs. https://github.com/strongloop/loopback-example-angular
Now, I have an application with:
HTML files (with Bootstrap)
AngularJS controllers
AngularJS service (generated with syntax lb-ng server/server.js client/js/services/lb-services.js)
Model (located in ./common folder)
MongoDB backend
The model "Device" is defined in ./common/models/device.js
module.exports = function(Device) {
};
And in ./common/models/device.json
{
"name": "Device",
"base": "PersistedModel",
"idInjection": true,
"properties": {
"name": {
"type": "string",
"required": true
},
"description": {
"type": "string",
"required": true
},
"category": {
"type": "string",
"required": true
},
"initialDate": {
"type": "date"
},
"initialPrice": {
"type": "number",
"required": true
},
"memory": {
"type": "number"
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": []
}
In the "AddDeviceController", I have an initialization part with:
$scope.device = new DeviceToBuy({
name: '',
description: '',
category: '',
initialPrice: 0,
memory: 8
initialDate: Date.now()
});
And I am able to save the $scope.device when executing the following method:
$scope.save = function() {
Device.create($scope.device)
.$promise
.then(function() {
console.log("saved");
$scope.back(); // goto previous page
}, function (error) {
console.log(JSON.stringify(error));
});
}
When everything is valid, the model is saved in the backend. If something is not valid in the $scope.device, I receive an error from my backend. So everything is working fine.
Now, I would like to use the model to perform client-side validation before sending my model to the backend and put some "error-class" on the bootstrap controls.
I tried something in the $scope.save function before sending to the backend:
if ($scope.device.isValid()) {
console.log("IsValid");
} else {
console.log("Not Valid");
}
But I get an exception "undefined is not a function" --> isValid() doesn't exist.
And I cannot find any example on how to execute this client-side validation.
LoopBack models are unopinionated and therefore do not provide client side validation out-of-box. You should use Angular validation mechanisms before calling $save.

Creating a Backbone.js Model with complex JSON

I have JSON response as follows
{
"results": [
{
"name": "FOO",
"containerName": "Foo",
"accounts": [
{
"id": "10445570_7601",
"shareeAccountInfo": "",
"siteAccountId": "271555",
"siteId": "271555",
"refreshMode": "NORMAL",
"isNetIncl": "true",
"propertyId": null,
"amount": [
"0.0",
"USD"
]
},
{
"id": "1070_20537601",
"shareeAccountInfo": "",
"siteAccountId": "271555",
"siteId": "271555",
"refreshMode": "NORMAL",
"isNetIncl": "true",
"propertyId": null,
"amount": [
"0.0",
"USD"
]
}
]
},
{
"name": "FOO123",
"containerName": "Foo123",
"accounts": [
{
"id": "10445570_20601",
"shareeAccountInfo": "",
"siteAccountId": "271555",
"siteId": "271555",
"refreshMode": "NORMAL",
"isNetIncl": "true",
"propertyId": null,
"amount": [
"0.0",
"USD"
]
},
{
"id": "10445570_37601",
"shareeAccountInfo": "",
"siteAccountId": "271555",
"siteId": "271555",
"refreshMode": "NORMAL",
"isNetIncl": "true",
"propertyId": null,
"amount": [
"0.0",
"USD"
]
}
]
},
{
"name": "FOO83838",
"containerName": "Foo3232",
"accounts": [
{
"id": "1601",
"shareeAccountInfo": "",
"siteAccountId": "271555",
"siteId": "271555",
"refreshMode": "NORMAL",
"isNetIncl": "true",
"propertyId": null,
"amount": [
"0.0",
"USD"
]
}
]
}
]
}
I am having issues creating a Backbone Model from this JSON response.
Should I be using a nested Model? and how should I be creating a collection based of my Model? Instead will it be a good idea to flatten this JSON structure? any ideas?
Your data structure naturally fits a Collection of Models (I'll call the model Group), where each Group contains a collection of Account models. This collection (and optionally its models) should have a reference to the parent Group.
var Account = Backbone.Model.extend({
})
var Accounts = Backbone.Collection.extend({
model: Account,
initialize: function(models, options) {
this.parent = options.parent;
}
});
var Group = Backbone.Model.extend({
initialize: function() {
this.accounts = new Accounts([], { parent: this });
}
});
var Groups = Backbone.Collection.extend({
model: Group,
// Assuming you make requests to `/group` to produce your result JSON
url: 'group',
// Construct models from the `results` attribute of the response
parse: function(response) {
return response.results;
}
});
There are two main implementation choices to make:
Persistence
If individual Accounts can be persisted seperately from the parent container, perhaps using an endpoint like /group/FOO83838/account/1601, the Acccount model can use the default Backbone.Model.save. The Accounts collection should override url to reference the parent URL:
Accounts = Backbone.Collection.extend({
// code from earlier
url: function() {
return this.parent.url() + '/account';
}
});
If accounts can only be saved as part of the overall Group model, you need to do two things:
First, override Account.save to delegate to the parent's save method:
Account = Backbone.Model.extend({
// code from earlier
save: function() {
this.collection.parent.save();
}
});
Second, override the Group.toJSON to include child accounts:
Group = Backbone.Model.extend({
// code from earlier
toJSON: function() {
var json = Backbone.Model.prototype.toJSON.call(this);
json.accounts = this.accounts.toJSON();
return json;
}
});
(In this example I have used the collection's parent reference. If you prefer you could also save a reference to the parent on this model).
Events
You could allow app code to directly listen to Group.accounts events, in which case no code changes are required:
// Example view code
this.listenTo(group.accounts, 'change', this.onAccountChange, this);
Or, if you prefer the extra encapsulation, you can forward child model changes:
Group = Backbone.Model.extend({
// code from earlier
initialize: function() {
this.accounts = new Accounts([], { parent: this });
this.listenTo(this.accounts, 'all', this.onChildEvent, this);
}
onChildEvent: function(eventName, model, options) {
// write logic to whitelist the events and parameters you are interested in
this.trigger('child:' + eventName, model, options);
}
});
// Example view code
this.listenTo(group, 'child:change', this.onAccountChange, this);
You could also look into Backbone extensions like DeepModel (no longer maintained) or Relational. I usually prefer the finer control of a custom implementation.

$resource and get array

I have an API which returns data with the following form (using tastypie):
{"meta":{
"limit": 20,
"next": null,
"offset": 0,
"previous": null,
"total_count": 4},
"objects": [
{
"id": 1,
"name": "name1",
"resource_uri": "/api/v1/operator/1",
"short_name": "NA1"
},
{
"id": 2,
"name": "name2",
"resource_uri": "/api/v1/operator/2",
"short_name": "NA2"
},
...
]
}
So I thought that to have my resource working, I should have used:
var Operator = $resource('http://127.0.0.1:8080\:8080/api/v1/operator/:operatorId',
{operatorId:'#id'},
{
query: {
method: 'GET',
transformResponse: function (data) {
console.log(JSON.parse(data).objects)
return JSON.parse(data).objects;
},
isArray: true
}
});
But when I set isArray to true : I got the following error:
TypeError: Object #<g> has no method 'push'
If I set isArray to false, I have no error but my object also contains all meta datas for this request...
Not sure I understand correctly this $resource feature
Since $resource automatically converts the JSON string to object, you don't have to do call JSON.parse().
Just do
var Operator = $resource('http://127.0.0.1:8080\:8080/api/v1/operator/:operatorId', {
operatorId: '#id'
}, {
query: {
method: 'GET',
transformResponse: function (data) {
return data.meta.objects;
},
isArray: true
}
});
The json data you are sending is not an array, and the meta property is not something created by angular framework. If you are expecting that it gets deserialized into the objects array you have to return only that part and isArray would work.
For now what #sza mentions to get the array you override query and return data.meta.objects

Resources