Serialize Object into Form Data - extjs

I am trying to submit a request with form data like this:
playdate: 2017-08-06
players[1][name]: Alex
players[1][username]: Alex
players[1][score]: 51
However, if I add a players object like this:
var players = grid.getSelectionModel().getSelection().map(function(record) {
return record.getData();
});
form.submit({
url:'log.php'
params: {
players: players
}
});
then ExtJS submits it like this:
playdate: 2017-08-06
players: [object Object]
Doesn't ExtJS support serializing an object for a form-urlencoded request?
Relevant fiddle

Have a look at the documentation.
It should work using the property jsonSubmit:true in your submit-call:
form.submit({
url:'log.php'
params: {
players: players
},
jsonSubmit: true
});

Related

Looping through array of object data within Falcor

Let's say I have the following routes:
{
route: "usersById['length']",
get: function(pathSet) {}
},
{
route: "usersById[{integers:ids}]['firstName', 'lastName']",
get: function(pathSet) {}
}
With the following in my angular1 controller:
Model.get(
'usersById.length',
'usersById[0..2]['firstName', 'lastName']'
).then(function(response) {
$scope.$apply(function() {
vm.entities = response.json.usersById;
});
});
The response from the server is going to look something like:
{
jsonGraph: {
usersById: {
"0": {
firstName: 'Jiminy',
lastName: 'Cricket'
},
"1": {
firstName: 'Jafar',
lastName: 'Husain'
},
"length": 123123
}
}
}
In my angular 1 template, I want to loop through the list of users:
<tr ng-repeat="entity in users.entities">
<td>{{entity.firstName}} {{entity.lastName}}</td>
</tr>
The problem is that there aren't just users in the response, firstly it contains length and secondly it seems other meta data is returned by Model's promise, of which looks to be part of the path data: usersById
What is the preferred way of looping through the list of users? Should I doing something like this in my promise?
vm.entities = response.json.usersById.filter(function(value) {
return typeof value === 'object';
});
I'm not seeing any API call for fetching raw values anywhere.
Ok so it seems the correct way to handle this is to create another route: users which just returns usersById references, that way you have an array of just entities, not containing length etc.
I'm guessing having the path data in the array of data was just a bug.
{
users: {...},
usersById: {...}
}
<li ng-repeat="user in users">{{ user.firstName }}</li>

$q.all(promises)and structure of promises object to collect the returned data

I am using Angularjs $q.all(promises) to make multiple REST call and then collecting the data once promise is successful. I have following following.
If "promises is simple array then it works Plunker
var promises = [
Users.query().$promise,
Repositories.query().$promise
];
If "promises" is simple object then also it works Plunker
var promises = {
users: Users.query().$promise,
repos: Repositories.query().$promise
};
If "promises" is nested object then it is not working. For my requirement I need nested object to remember the input parameters. Plunker
var promises = {
users: {"phx":Users.query().$promise},
repos: {"phx":Repositories.query().$promise}
};
These plunkr are just to simulate my problem. However I want this approach in real project for following requirement.
I have list of 12 product
Each product has "details", "benefits" and "offers" data
I have separate REST API services for "details", "benefits" and "offers" having :productID as parameter
I am making call in following order
a. Loop for each cards
b. For each card, make a REST API call for "details", "benefits" and "offers"
c. Add #b steps into "promises" object
d. call
$q.all(promises).then(function(results) {
// Here need logic to compile the result back to product
// and corresponding "details", "benefits" and "offers" mapping
}
and get the data back
Following is json structure I needed to collect my response.
{
"prod1": {
"benefits": {},
"offers": {},
"pages": {
"productPage": {}
}
}
},
"common": {
"benefits": {},
"pages": {
"commonBenefit": {}
},
"others": {}
}
How can I achieve this?
If you really need it, you can wrap the nest with $q.all like this:
var promises = {
users: $q.all({"phx": Users.query().$promise}),
repos: $q.all({"phx": Repositories.query().$promise})
};
plnkr.co

Ember.js binding on array inside a property

Hello emberjs experts :)
There is something that i don't understand.
Given the following route:
Evibe.MemberShowRoute = Ember.Route.extend({
model: function(params) {
return Ember.$.getJSON('/api/user').then(function(user) {
return Ember.Object.create(user);
});
}
});
The call to the api simply returns a user object containing properties. One of this property is an array of picture objects. Like that:
{
username: "A nice user",
pictures: [
{id: 1, is_main: true, url: 'http://www.test.com/img1.jpg'},
{id: 2, is_main: false, url: 'http://www.test.com/img2.jpg'},
{id: 3, is_main: false, url: 'http://www.test.com/img3.jpg'},
{id: 4, is_main: false, url: 'http://www.test.com/img4.jpg'},
]
}
In my controller, i have something like this:
Evibe.MemberShowController = Ember.ObjectController.extend({
nb_pictures: function() {
return this.pictures.length;
}.property('pictures'),
addPictureObject: function(picture) {
this.get('pictures').addObject(picture);
}
});
And in my template, i have something like this:
{{ nb_pictures }} pictures
I don't understand why nb_pictures is not updated, as i'm adding an object into my "pictures" property with the addPictureObject function.
Also, when i try to do something like this:
this.get('pictures').setEach('is_main', false); // Works
this.get('pictures').findBy('id', pictureId).is_main = true; // Doesn't work
this.get('pictures').findBy('id', pictureId).set('is_main', true) // Doesn't work
The first line works as expected.
But... for the second line, i get the error: "Assertion failed: You must use Ember.set() to access this property (of [object Object])"
And for the third one, i get the error: "Uncaught TypeError: Object # has no method 'set' "
Any ideas that can help clarify this would be greatly appreciated.
In your nb_pictures computed property, you have set the dependent key with property('pictures'), the correct is property('pictures.length').
This is the updated code:
Evibe.MemberShowController = Ember.ObjectController.extend({
nb_pictures: function() {
return this.get('pictures.length');
}.property('pictures.length'),
addPictureObject: function(picture) {
this.get('pictures').addObject(picture);
}
});
Using just property('pictures') will make the framework observe just the array replacement, like set('pictures', [...]), not the changes in the array structure get('pictures').pushObject(...). this the reason that your ui don't update.

Handling Subsidiary Views in Backbone.js

I have a basic Backbone application which obtain an array of JSON objects from a remote service and displays them: all good so far. However, each JSON object has an array of tags and I want to display the tags in a separate area of the webpage.
My question is: what is the most Backbone-friendly way of doing this? I could parse the existing data again in a second view, which is cleaner but takes up more computation (processing the entire array twice).
An alternative is gathering up the tag information in the primary view as it is working through the array and then passing it along to the subsidiary view, but then I'm linking the views together.
Finally, I'd like to filter based on those tags (so the tags will become toggle buttons and turning those buttons on/off will filter the information in the primary view); does this make any difference to how this should be laid out?
Bonus points for code snippets.
Hm. I'm not sure if this is the Backbone-friendly way, but I'll put the logic to retrieve a list of tags (I think that's what you meant by "parse") in the collection.
Both the main view and the subview will "listen" to the same collection, and the subview will just call collection.getTags() to get a list of tags it needs.
// Model that represents the list data
var ListDataModel = Backbone.Model.extend({
defaults: function() {
return {
name: null,
tags: []
};
}
});
// Collection of list data
var ListDataCollection = Backbone.Collection.extend({
model: ListDataModel,
initialize: function() {
var me = this;
// Expires tag collection on reset/change
this.on('reset', this.expireTagCache, this);
this.on('change', this.expireTagCache, this);
},
/**
* Expires tag cache
* #private
*/
expireTagCache: function() {
this._cachedTags = null;
},
/**
* Retrieves an array of tags in collection
*
* #return {Array}
*/
getTags: function() {
if (this._cachedTags === null) {
this._cachedTags = _.union.apply(this, this.pluck('tags'));
}
return this._cachedTags;
},
sync: function(method, model, options) {
if (method === 'read') {
var me = this;
// Make an XHR request to get data for this demo
Backbone.ajax({
url: '/echo/json/',
method: 'POST',
data: {
// Feed mock data into JSFiddle's mock XHR response
json: JSON.stringify([
{ id: 1, name: 'one', tags: [ 'number', 'first', 'odd' ] },
{ id: 2, name: 'two', tags: [ 'number', 'even' ] },
{ id: 3, name: 'a', tags: [ 'alphabet', 'first' ] }
]),
},
success: function(resp) {
options.success(me, resp, options);
},
error: function() {
if (options.error) {
options.error();
}
}
});
}
else {
// Call the default sync method for other sync method
Backbone.Collection.prototype.sync.apply(this, arguments);
}
}
});
var listColl = new ListDataCollection();
listColl.fetch({
success: function() {
console.log(listColl.getTags());
}
});
I guess two reasons for handling this in the collection:
It keeps the View code cleaner (This is given that we are not doing very complex logic in the tag extraction - It's just a simple _.pluck() and _.union().
It has 0 business logic involved - It can arguably belong to the data layer.
To address the performance issue:
It does go through the collection twice - However, if the amont of data you are consuming is too much for the client to process even in this case, you may want to consider asking the Backend to provide an API endpoint for this. (Even 500 pieces of data with a total of 1000 tags shouldn't bee too much for a somewhat modern browser to handle nowadays.)
Hmm. Does this help?
JSFiddle to go with this with the collection and the model: http://jsfiddle.net/dashk/G8LaB/ (And, a log statement to demonstrate the result of .getTags()).

Backbone-relational fetchRelated not sending request

I'm using backbone.js and backbone relational 0.5.0 with a Rails 3.2 backend. I have a Card model which has_many Notes.
Here are my JS models and collections:
Workflow.Collections.Cards = Backbone.Collection.extend({
model: Workflow.Models.Card,
url: '/cards'
});
Workflow.Models.Card = Backbone.RelationalModel.extend({
modelName : 'card',
urlRoot : '/cards',
relations: [
{
type: Backbone.HasMany,
key: 'notes',
relatedModel: 'Workflow.Models.Note',
collectionType: 'Workflow.Collections.Notes',
includeInJSON: false,
reverseRelation: {
key: 'card',
includeInJSON: 'id'
}
}]
});
Workflow.Collections.Notes = Backbone.Collection.extend({
model: Workflow.Models.Note,
url: '/cards/74/notes' // intentionally hard-coded for now
});
Workflow.Models.Note = Backbone.RelationalModel.extend({
modelName : 'note',
urlRoot : '/notes'
});
Normal fetching works great, but when I try fetchRelated in the console, I get an empty array:
card = new Workflow.Models.Card({id: 74}) // cool
card.fetch() // hits the sever with GET "/cards/74" - works great
card.fetchRelated('notes') // [] - didn't even try to hit the server
What's weird is that this works:
card.get('notes').fetch() // cool - GET "/cards/74/notes"
I could use that method and parse the response text, but it feels really dirty.
Anyone know what I'm missing here?
Thanks in advance, this one is really torturing me!
Stu
You should create Card with Note ids array: card = new Workflow.Models.Card({id: 74, notes: [74, 75]}); and change the url method of Notes accordingly:
Workflow.Collections.Notes = Backbone.Collection.extend({
model: Workflow.Models.Note
});
Workflow.Models.Note = Backbone.RelationalModel.extend({
modelName : 'note',
urlRoot : function () {
return this.get('card').url() + '/notes';
}
});
card = new Workflow.Models.Card({id: 74, notes: [74, 75]});
card.fetchRelated('notes');
http://jsfiddle.net/theotheo/5DAzx/
I should have posted my solution a while back - there might well be a better way, but this is the convention I've gone with:
All of the following code is in the card view (which is where the notes are displayed).
First, I bind a renderNotes method to the 'reset' event on the card's notes collection:
initialize: function () {
_.bindAll(this);
this.model.get('notes').on('reset', this.renderNotes);
var self = this;
this.model.get('notes').on('add', function(addedNote, relatedCollection) {
self.renderNote(addedNote);
});
}
I also bind to the 'add' on that collection to call a singular renderNote.
The renderNotes and renderNote methods work like this:
renderNotes: function () {
if (this.model.get('notes')) {
this.model.get('notes').each(this.renderNote);
}
},
renderNote: function (note) {
var noteView = new Workflow.Views.Note({ model: note });
this.$('.notes').append(noteView.render().el);
},
Then, the last piece of the puzzle is to actually hit the server up for the card's notes (which will in turn fire the 'reset' event I bound to above). I do this in the card view's render method:
render: function () {
// render all of the eager-loaded things
this.model.get('notes').fetch();
return this;
},
As #user1248256 kindly helped me work out in the comments on my OP, the confusion was mainly in that I expected fetchRelated to pull down lazy-loaded records - that's actually not the case.
As a side-note, this view is actually a modal and be opened and closed (removed from the page). To prevent the zombie events problem described in this excellent post, I also manually unbind the events mentioned above.

Resources