In my Ext JS 4.1 application, I have a model Project with an belongsTo association to ProjectCategory:
Ext.define('MyApp.model.Project', {
extend: 'Ext.data.Model',
fields: ['id', 'project_category_id', 'title'],
belongsTo: {
model: 'MyApp.model.ProjectCategory',
associationKey: 'ProjectCategory',
getterName: 'getCategory'
}
// ...
});
Ext.define('MyApp.model.ProjectCategory', {
extend: 'Ext.data.Model',
fields: ['id', 'title']
});
The project is read via a Direct proxy, and ProjectCategory details are included as nested values in the response (mostly for display purposes). When loading a store, the associated data is read correctly, and I'm able to read the ProjectCategory's title in a grid via a custom renderer:
renderer: function(v, p, rec) {
return v ? rec.getCategory().get('title') : '';
}
However, when editing and saving the parent Project record through form.updateRecord(record), the associated record's fields aren't updated with values from the server response, unlike the Project's "native" fields. So when changing the project_category_id, even though the server will respond with a correctly nested ProjectCategory field containing the new category, getCategory will still return the old category.
Why isn't this updated from the server response, and what do I have to do to update it?
I already found out that in the store's write listener, I have access to the record and the returned association data. But I can't figure out how to update the record silently without triggering any other events.
It seems like I found my solution. Add this to the Project store:
listeners: {
write: function(store, operation, opt) {
var c = operation.records[0].getCategory();
c.set(operation.response.result.ProjectCategory);
c.commit(true);
}
}
The key element is the commit(true) call, which will skip notifying the store/proxy about the changes.
It's still a bummer that this isn't done automatically, though.
Related
I have more than 50 fields those are input text and dropdowns in the reactive form. The fields are dependent to each other's value changes in order to trigger validation and to display related field after the selection.
I subscribed to the value changes in ngOnInit() as below:
ngOnInit() {
this.setPageValidation();
}
setPageValidation() {
this.NameSubscription = this.FormGroup.get('personnel').get('name').valueChanges.subscribe(data
=> {
this.enableOrders();
});
this.StateSubscription = this.FormGroup.get('personnel').get('state').valueChanges.subscribe(data
=>
{
this.enableAccount();
});
// more value changes subscription like 40 fields ............................
}
While loading the form, it is taking longer time to load due to subscribing for the value changes when the form loads.
I tried implementing it to move the code to ngOnChanges() but it is not triggering the enable and display of other fields depending on it's initial value that are filled from the table if there are values for those fields. It is just populating the first field and the rest does not display depending upon on its value.
I would like to thank you in advance. I really appreciate your help if there is any best approach to it to resolve without performance issue.
You can do with a single subscription.
this.personnelSubscription =
this.Formgroup.get('personnel').valueChanges.subscribe(data => {
if (data) {
//Console log the data here. It will print the formGroup of personnel
// then select the control and add your validations
// like this data.controls.state
}
})
The Ext.data.Model class represents the backend models. And just like in the server code, some of its fields can be of another declared model type via the reference property. I've found out that using a model's getAssociatedData() function returns an object with all those referenced fields. However they only contain the reference object's data object they are not full fledged initialized Ext.data.Models, which forces a primitive object access and there is no way to use the model's configured proxies etc for loading/saving. Is this the correct/only way of using this functionality? We've also been looking for a way to add columns from referenced fields on a grid but it doesn't seem to work... I'm starting to doubt the usefulness of declaring referenced fields.
Example code:
Ext.define('MyApp.ModelA', {
extend: 'Ext.data.Model',
fields: [{
name: 'modelb',
reference: 'MyApp.ModelB'
}]
});
Ext.define('MyApp.ModelB', {
extend: 'Ext.data.Model',
fields: [{
name: 'modelId',
type: 'int'
}]
});
//...
var modelA = new MyApp.ModelA().load();
var modelB = modelA.getAssociatedData().modelb; //This is the only way to access it.
var modelBId = modelB.get('modelId') //This returns undefined because the function .get doesn't exist.
var modelBId = modelB.id; //This works because it is a simple object property access.
//...
As Chad Peruggia said, it seems that ExtJS creates special getters for reference fields that match the field name. Using getAssociatedData() returns only the primitive form of those objects (only their data values) but using the special getter (in my case getModelb()) it returns a full fledged model initialized with the given data.
I have the following controller action:
public JsonResult AcceptChanges(RoomPricing currentData, RoomPricing lastSaveData)
{
...
}
This controller receives JSON encoded objects from my AJAX request:
$.ajax(
{
type: "POST",
url: _controllerURL,
data: JSON.stringify({ currentData: _currentData, lastSaveData: _lastSaveData }),
...
});
The AJAX request data is derived from a form in my partial view, which uses a ViewModel of type RoomPricing. Current and lastSave data are respectively the updated and stale serialized versions of the form. The purpose of these objects is to do form update checks server-side. Server-side because the RoomPricing model includes an enumerable and other junk that would make reliable update checking complex and obnoxious to perform client-side.
Now, when the controller receives the currentData and lastSaveData, it automagically creates the RoomPricing objects, BUT both objects are identical, taking on the values of currentData. To be clear: lastSaveData is created, but only in name, as its contents are identical to that of currentData (and so ignoring the data that was passed by AJAX, which I assume goes into the void).
I expect that this behavior is a side effect of MVC trying to be helpful with Model Binding. Does anyone have a suggestion for getting around this problem? Making a super-ViewModel with two RoomPricing objects to use as the controller argument did not resolve this issue.
Thanks in advance!
Edit: The current and lastSave data comes from the following JQuery code:
var $lastSaveFormData = null;
function AjaxSubmitForm() {
var $lastSaveSerialized = ($lastSaveFormData == null ? null : $lastSaveFormData)
var $form = $('#MyForm');
submitOverrideForm(
$form.serialize(), // currentData
$lastSaveSerialized, // lastSaveData
$form.attr('action'),
);
$lastSaveFormData = $form.serialize();
}
Through the above I'm able to keep a record of all changes since the last save. Although the models are somewhat complex, the size of the data being sent is quite small so I figured I'd compare the data server-side, and here we are. Last thing of note is that I've verified that at this point - JSON.stringify({ currentData: _currentData, lastSaveData: _lastSaveData } - the encoded data is as expected...currentData and lastSaveData are unique if form updates have taken place.
Edit: I just noticed something else. Here is the data being sent to the server as well as the data received server-side just prior to being bound to the model:
_currentData, # AJAX call:
"Book%5B0%5D.Author=1&Book%5B0%5D.Title=100&Book%5B0%5D.IsPaperback=false&Book%5B1%5D.Author=2&Book%5B1%5D.Title=2222&Book%5B1%5D.IsPaperback=false"
_lastSaveData # AJAX call:
"Book%5B0%5D.Author=1&Book%5B0%5D.Title=100&Book%5B0%5D.IsPaperback=false&Book%5B1%5D.Author=2&Book%5B1%5D.Title=77&Book%5B1%5D.IsPaperback=false"
JSON received server-side, just before model binding:
{"currentData": "Book[0].Author: 1,1 Book[0].Title: 100,100
Book[0].IsPaperback: false,false Book[1].Author: 2,2 Book[1].Title:
2222,77 Book[1].IsPaperback: false,false", "lastSaveData":"false"}
Its like MVC is trying to bind the distinct values to a single model, realizes it cant, and drops the 'lastSave' data. Then, when the model hits the controller, it discovers that two models are anticipated, so it uses this model for both. Why is it behaving like this and how can I fix it?
You can add another ViewModel:
public class CurrentAndLastSavedRoomPricingViewModel
{
RoomPricing CurrentData {get;set;}
RoomPricing LastSaveData {get;set;}
}
and get it in controller:
public JsonResult AcceptChanges(CurrentAndLastSavedRoomPricingViewModel data)
{
...
}
passing this model with ajax:
$.ajax(
{
type: "POST",
url: _controllerURL,
data: {data: { currentData: _currentData, lastSaveData: _lastSaveData }},
...
});
Or you can get last saved data on server from database and don't pass it from client.
I'm very new to developing mobile applications with telerik appbuilder. There are some things I have a hard time to understand with fetching data from Everlive.
Lets consider a simple example. Lets say I have Blog Posts and Comments that belong to those Posts. And both Posts and Comments are made by Users.
In one view I want to present the Post with corresponding Comments and I also need the Username of the User who posted the Comment (Comment table only contains userId).
Both the Post and the Comments are easy to fetch since I have the id of the Post. But how do I fetch the corresponding user for each Comment?
The FriendsApp example does something very similar but it uses this line to get the user:
var user = $.grep(app.Users.users(), function (e) {
return e.Id === userId;
})[0];
This fetches all users and filters them client side? I guess this is okay if you have like 10 users. But what if you have a million users?
I am guessing that the FriendsApp uses this way of resolving the relations just for keeping the simplicity of the sample. Everlive offers a far more meaningful toolset for resolving relation fields called Expand. You can explore the REST API here:
http://docs.telerik.com/platform/backend-services/development/rest-api/relations/simple-expanding
or the JS SDK function here:
http://docs.telerik.com/platform/backend-services/development/javascript-sdk/relations/simple-expanding.
As the Friends app uses the Kendo UI data source component you can send an Expand header with the request. The following configuration of the data source will return the DisplayName of the user in each Activity/Comments entity:
var expandObject = {
"CreatedBy": {
"ReturnAs": "User",
"SingleField": "DisplayName"
}
};
var dataSource = new kendo.data.DataSource({
type: "everlive",
transport: {
typeName: 'Activities', // replace this with Comments
read: {
beforeSend: function (xhr) {
xhr.setRequestHeader("X-Everlive-Expand", JSON.stringify(expandObject))
},
}
},
schema: {
model: {
id: Everlive.idField
}
}
});
dataSource.fetch(function (data) {
console.log(data.items);
});
Same could be applied for resolving the comments for each Blog post. Given the Friends data schema you will need to use the External Relation resolver of the Everlive API. Note that it is available only in a GetById scenario, e.g. when retrieving an Activity by Id, you can resolve the Comments that point to this activity, which is generally very handy in master-detail views.
I have a property called Copies which is defined on the server that represents the default number of copies allowed. And I can update this value and it will update an input field on my UI.
however, I would like to be able to reset the Copies property to the original value if the user resets this field on the UI.
My idea was to define a custom property on my kendo datasource model called originalValue that references the Copies property. but this just seems to override the Copies property if I do something like this.
schema: {
data: 'd',
total: function (data) {
return data.d.length;
},
model: {
originalCopies: "Copies"
}
}
how can I go about creating a custom property like this which is basically a immutable clone of my Copies property?
You can try to do it on the server side, just create a separate property "OriginalCopies" and set it to Copies. Once passed to the client side , it will lose its immutability.
Something similar could be done on the client side as well. JSON.stringify your Copies and set
OriginalCopies to the JSON.parse value of the stringified variable as:
var copies = JSON.stringify(data.Copies);
data.OriginalCopies = JSON.parse(copies);