I have a nested json i eventually broke down to use with ko.mapping:
{ users: [ { k: 'key',
name: 'Alice'
},
],
roles: [ { k: 'key',
name: 'Standard',
regex: [ ( 'root', 'me', 'myself'),
],
},
]
}
...etc.
My situation is that:
my UI needs a data-bind on vm.users.k, but json data (via $.getJSON or $.ajax) is not jet loaded from server;
how can I define the UI, binding a dom element to e.g. vm.users.name, while waiting for ajax request to succeed?
I was looking at this question but maybe I miss the point.
My goal is to define the UI without modeling the initial viewModel, letting it be populated by an Ajax call to server. All nested items and array are needed to became observables.
My last code after some iterations:
function ViewModel( ) {
var self = this;
self.users = ko.observableArray();
self.roles = ko.observableArray();
// etc. logic
};
var viewModel = new ViewModel();
ko.applyBindings( viewModel );
$.getJSON('/api/call/from/server', function( data ){
ko.mapping.fromJS( data.users, {}, viewModel.clienti );
ko.mapping.fromJS( data.roles, {}, viewModel.procedure );
});
And html:
<select name="users" data-bind="options: users,
optionsCaption: '',
optionsText: name,
optionsValue: k">
Server side google app engine with headers:
self.response.headers['Content-Type'] = 'application/json; charset=utf-8'
self.response.out.write( json.dumps( object ) )
Actually, I don't want to use async: false.
I managed to broke ko.mapping invocation in three calls from js data.responseJSON, e.g. mapping users to vm.users, then roles to vm.roles, etc.
Related
From a JSON stored in a variable I can get the name of the current id from a router function called show: function(id). However, when I fetch collection from an URL instead of using a JSON variable I get an undefined TypeError.
console.log(this.collection.get(id).get('name'));
What I have seen is that when I use a JSON variable the show function works fine, but when I fetch from URL, show function executes after fetch succeed.
What I am doing wrong? Why fetching from URL gets undefined? How can I make it work?
The following code is fictional, it only shows the relevant part of my code. See the two cases at the end of the code block.
jsFiddle here
// Data 1 with variable
var heroes = [
{"id": "1", "name": "Batman"},
{"id": "2", "name": "Superman"},
];
// Data 2 from url: http://example.com/heroes.json
[
{"id": "1", "name": "Batman"},
{"id": "2", "name": "Superman"},
];
HeroesCollection = Backbone.Collection.extend({
model: HeroesModel,
url: 'http://example.com/heroes.json'
});
HeroesRouter = Backbone.Router.extend({
// I use two shows to graphic this example
routes: {
'': 'index',
':id': 'show'
},
initialize: function(options) {
this.collection = options.collection;
this.collection.fetch();
// this.collection.fetch({async:false}); this fixes my problem, but I heard it is a bad practice
},
index: function() {
},
show: function(id) {
console.log(this.collection.get(id).get('name'));
// Case #1: When Collection loads from a Variable
// id 1 returns: 'Batman'
// Case #2: When Collection fetchs from URL, id 1 returns:
// TypeError: this.collection.get(...) is undefined
}
});
// Case #1: collection loads JSON from a variable
var heroesCollection = new HeroesCollection(heroes);
// Case #2: collection loads JSON with fetch in router's initialize
// var heroesCollection = new HeroesCollection();
var heroesRouter = new HeroesRouter({collection: heroesCollection});
How about this? It's been awhile, but this seems like a better approach to what you are trying to achieve. The basic concept is that once you navigate to your show route, it will execute show. This method will create a new, empty collection, and then fetch the data for it. Along with that, we pass in a success method (as François illustrated) which will execute when the request is finished with the JSON (which creates a collection of Heros).
I believe the reason you were running into the issue with the remote data is that you were trying to access this.collection before it was populated with data from the request.
You have to remember the request is asynchronous, which mean code execution continues while the request is processing.
HeroesCollection = Backbone.Collection.extend({
model: HeroesModel,
url: 'http://example.com/heroes.json'
});
HeroesRouter = Backbone.Router.extend({
routes: {
'': 'index',
':id': 'show'
},
index: function() {
},
show: function(id) {
this.herosCollection = new HerosCollection();
this.herosCollection.fetch({
success: function(collection, response, options) {
console.log(this.get(id).get('name'));
}
});
}
});
you need to trigger the router 'show' function when the collection has ended to load.
this.collection.fetch({async:false}); fixes your problem because the whole javascript code is waiting (async:false) the ajax call to be ended before going further.
The other and best solution is to wait that your collection is fetched before you try to use the results.
Basically:
MyCollection.fetch({
success: function(model, reponse) {
// do wtv you want with the result here or trigger router show method...
}
});
I am using Json-server to test CURD operations in my extjs grid. I have put some dummy data on json-server and I am able to read, delete, update and edit that data.
But When I create data using my extjs app, I am not able to delete or edit that data cause auto generated Id is "nameOfMyModel + autoIncrementNumber".
My Store is:
Ext.define('ThemeApp.store.peopleStore', {
extend: 'Ext.data.Store',
model: 'ThemeApp.model.peopleModel',
storeId: 'peopleStore',
pageSize: 500,
autoLoad: true,
autoSync: true
});
Model is:
Ext.define('ThemeApp.model.peopleModel', {
extend: 'Ext.data.Model',
fields: [ 'id' ,'title','body'],
proxy: {
type: 'rest',
//format: 'json',
limitParam:"",
filterParam: "",
startParam:'',
pageParam:'',
url:'http://localhost:3000/posts',
api: {
read : 'http://localhost:3000/db',
create: 'http://localhost:3000/posts',
update : 'http://localhost:3000/posts' ,
destroy : 'http://localhost:3000/posts'
},
headers: {'Content-Type': "application/json" },
reader: {
type: 'json',
rootProperty:'posts'
},
writer: {
type: 'json'
}
}
});
And I am Adding user like:
var UserStore = Ext.getStore('peopleStore');
var user = Ext.create('ThemeApp.model.peopleModel',{title: "Test", body: "Testing" });
user.save(); //POST /users
UserStore.load();
And For Deletion:
var grid = button.up('gridpanel');
var selection = grid.getView().getSelectionModel().getSelection()[0];
if (selection) {
UserStore.remove(selection);
UserStore.load();
}
Can anyone tell me why I am not able to delete/update records which I generate via extjs app?
The Id of simple post is like
http://localhost:3000/posts/(number like 1 or 2)
and of app generated record is
http://localhost:3000/posts/ThemeApp.model.peopleModel-1
And I can see app generated record in database.json but browser is saying that this url doesn"t exist.
Kindly point out my mistake
Reason you can't delete them is because they are not created properly. First of all to generate sequential ids use
identifier: 'sequential' in your model, than it will generate numeric id's
You can read it in detail here Model identifier
After doing this generate new objects and see if you can handle them properly.
I use Model.save to save data from the ExtJs form. Sometimes server returns operation status in following format:
{"success": false, "error": {"name": "Invalid Name"}}
The following code sends data from form to server:
var model = formPanel.getRecord();
model.save({
callback: function(record, operation, success) {
// operation.response is null,
// and success === true
// how to read server response here?
}
})
Server response is treated as successful because HTTP status is 200. So I I have to read server response to check operation status. But operation.response is null in callback function.
Here is my Model:
Ext.define('My.Model', {
extend: 'Ext.data.Model',
idProperty: 'id',
fields: [
{name: 'id', type: 'auto'},
{name: 'name', type: 'auto'}
],
proxy: {
type: 'rest',
url: 'api/v1/models',
appendId: true,
reader: {
type: 'json',
},
writer: {
type: 'json'
}
}
});
Question: how can I access server response after Model.save's call?
More generic question: is it semantically correct to use Model.load or Model.save to populate/save the ExtJs form?
I'm using ExJs 5.0.1.1255.
I created some simple test code for this:
var Clazz = Ext.define('MyModel', {
extend: 'Ext.data.Model',
proxy: {
type: 'rest',
url: 'api/v1/models'
}
});
var instance = Ext.create('MyModel', {
name: 'MyName'
});
instance.save({
callback: function(record, operation) {
}
});
The server responds with:
{
success: true,
something: 'else'
}
You can see this in a fiddle here: https://fiddle.sencha.com/#fiddle/fhi
With this code, the callback has a record argument, and record.data contains the the original record merged with the server response. In addition, you can do operation.getResponse() (rather than just operation.response) to get full access to the server's response.
In regard to your question on load vs save, if you use view models and bind the model that way, it kind of becomes moot as your form should always reflect the state of the model.
Using model.save() and Model.load() is definitely the correct thing to do.
In addition to providing a custom callback, you should investigate configuring a custom proxy. In a custom proxy, you can provide your own implementation of the extractResponseData method. This would let you centralise your need to examine the server response.
Using Extjs 3.4
My web service respond with a json string: {"msg":"Some"}
I want to populate a grid with Some .
Ext.onReady(function(){
var store = new Ext.data.JsonStore({
url: "my/json/url.json",
fields: [{name:"msg"}]
});
function StoreLoadCallback(records, operation, success){
if (success) {
console.log(records); // record is undefined
alert(records); // show 'undefined'
} else {
console.log('error');
}
}
function ajaxSearch_function(){
var query = Ext.getCmp('search').getValue();
store.load({
params: {query: query},
callback: StoreLoadCallback
});
}
var form = new Ext.FormPanel({
defaultType: 'textfield',
items: [{
fieldLabel: 'search',
name: 'search',
id: 'search'
}],
buttons: [{
text: 'Search', handler: ajaxSearch_function
}]
});
form.render('ajax-search_form');
var grid = new Ext.grid.GridPanel({
store: store,
columns: [{
id :'title',
header : 'title',
sortable : true,
dataIndex: 'title'
}],
});
grid.render('ajax-grid');
});
The web service respond well, I have tested with Curl.
The problem is populate the grid with the json response.
If you say that records variable in callback method is undefined, there is probably a problem with parsing the response. I guess it expects array instead of one single record. Try to change the contents of the json file from {"msg" : "Some"} to [{"msg" : "Some"}]
After you cross this hurdle (i. e. the response is correctly parsed), I see that your datagrid column refer to "title" rather than "msg". Title is not a member of the store, so the columns will show empty value anyway.
Also, it is not common to start method name with capital letters (except they represent "classes"), so it is kind of nicer to call the method storeLoadCallback instead of StoreLoadCallback.
I have a JsonStore configured like so:
var store = new Ext.data.JsonStore({
restful: true,
url: '/categories',
remoteSort: true,
idProperty: 'Id',
totalProperty: 'total',
root: 'results',
writer: new Ext.data.JsonWriter({
encode: false
}),
fields: [ 'Id', 'Name' ]
});
I grab some data from the server, then edit one of the records. When I tell the store to save, it sends this JSON back to the server:
{
"results":
{
"Name":"Trivial123",
"Id":2
}
}
The store is wrapping the JSON inside the results property (the root property configured on the store). However, the server expects this:
{
"Name":"Trivial123",
"Id":2
}
In other words, the serialized entity should be put directly in the response body, and not wrapped in a property. Does anyone know how I can configure the store to do this?
You need to override the data rendering function in the JsonWriter, like so:
var rootlessRenderFunction = function (params, baseParams, data) {
if (this.encode === true) {
Ext.apply(params, baseParams);
params = Ext.encode(data);
} else {
params.jsonData = data;
}
};
var myWriter = new Ext.data.JsonWriter({
encode: false,
writeAllFields: true
});
myWriter.render = rootlessRenderFunction;
var myStore = new Ext.data.JsonStore({
// ... various config values ...
writer: myWriter
});
This "rootlessRenderFunction" implementation is the same as the Ext JsonWriter's render code except it doesn't interpose a root in the request data.
This is a hack, of course.
I'm assuming you can't just not set the root value for the store for some reason? That's the way I normally do it.