I'm fairly new to ember and I'd like to know whats the fastest way to extract the data out of ember objects. I've loaded my model with a very large amount of records using this.store.find('modelName);` in my route.
I created a component on my view using {{kendo-ui.kendo-table descriptor=tableDescriptor data=model}}. My controller defined other arguments to be passed to my component (descriptor).
In my components.js I'm' getting the data passed over by using
export default Ember.Component.extend({
didInsertElement: function() {
var columns = this.get('descriptor.columns'); // this is right
var model = this.get('data')['content']; // this returns the objects of the model
var height = this.get('descriptor.height'); // this is ok too
Ember.$('#kendo-table').kendoGrid({
dataSource: {
data: model,
pageSize: 100
},
height: height,
scrollable: {
virtual: true
},
groupable: true,
sortable: true,
columns: columns
});
}
});
On the line var model = this.get('data')['content'];, this gives me an Array of Ember Classes. Inside each class, there is a _data object that holds the value of the actual class.
The easiest solutions is to just loop through and extract the _data but that is no good for larger model array. Is there a quick way to extract all the _data from my array of ember objects?
You could use getProperties method. http://emberjs.com/api/classes/Ember.Object.html#method_getProperties
To get the values of multiple properties at once, call getProperties with a list of strings or an array:
record.getProperties('firstName', 'lastName', 'zipCode');
// { firstName: 'John', lastName: 'Doe', zipCode: '10011' }
You could define computed property dataArray:
dataArray: function() {
return this.get('data').map( function(item) {
return item.getProperties('id', ... ); // your list of properties
});
}.property('data.[]'),
didInsertElement: function() {
//...
Ember.$('#kendo-table').kendoGrid({
dataSource: {
data: this.get('dataArray'),
//...
},
// ...
});
}
UPDATE:
for records (DS.Model) you could use toJSON method. Use DS.JSONSerializer to get the JSON representation of a record.
toJSON takes an optional hash as a parameter, currently supported options are:
includeId: true if the record's ID should be included in the JSON representation.
http://emberjs.com/api/data/classes/DS.Model.html#method_toJSON
Related
I have a form with many fields attached to a data - this.myData:
data: function() {
return {
isDataChanged: false,
myData: {},
myChangedData: {
default: '',
default1: {},
default2: []
},
}
},
myData is populated from a response from the server and it populates the form values.
myChangedData is for the new values, which are changed v-on:input="onChangeMyData($event, 'default')":
onChangeMyData(e, name, required = false){
const val = e.target.value.trim();
this.myChangedData[name] = val;
console.log(this.myChangedData)
this.checkIsmyDataChanged();
},
I can use the same method, providing a key as a second param. With the method checkIsmyDataChanged I am checking is it changed some field in the form. This method loops through myChangedData and compares its properties with changedData and if there is a difference this.isDataChanged = true.
The problem is that, I have a complicated structure of mydata/mydatachanged. default1 has objects in it and default1 is an array of objects. This means that, I can't use onChangeMyData, but other methods with different checks (validations) and now I need to call in all of them this.checkIsmyDataChanged();.
I created a watch for myChangedData:
watch:{
myChangedData: {
handler: function (newVal) {
console.log('change')
},
deep: true
},
},
, but it doesn't execute on change data
Did you try with Vue.set ? Source
Change this.myChangedData[name] = val; to
this.$set(this.myChangedData, 'name', val)
Thanks to that, the modification on the object should be detected and execute the watcher.
I need to access collection on client-side javascript files to sort data and perform various operations. Currently I'm just using find() or findOne(), but it's very inconsistent and sometimes doesn't return value quickly enough so it doesn't work.
Am I doing it the right way? Are there any other options provided to retrieve and manipulate collection data?
For example, I want to find this data:
data = Stats.findOne({
name: Session.get('name')
}, {sort: {date: -1}});
variable = data.variable;
And then I want to use variable to filter collection:
ranking = Stats.find({
variable: variable
}, {sort: {score: -1}}).fetch();
I can't find a reason why it doesn't work.
Meteor is reactive, which means that as that Stats collection populates with data from the server, code that uses it will be automatically re-run. If you're trying to display the data on screen, you want to use a helper, a reactive variable, and the autorun function.
JS:
Template.yourTemplate.onCreated( function() {
var template = this;
template.variable = new ReactiveVar( null );
template.autorun( function() {
var result = Stats.findOne({ name: Session.get('name') }, { sort: { date: -1 } });
if( result && result.variable ) {
template.variable.set( result.variable );
}
});
});
Template.yourTemplate.helpers({
ranking() {
return Stats.find({ variable: Template.instance().variable.get() }, { sort: { score: -1 } }).fetch();
}
});
HTML:
<template name="yourTemplate">
{{#each rank in ranking}}
Do stuff with {{rank}}
{{/each}}
</template>
This will ensure that your reactive variable changes as Stats.findOne() of your session variable changes, and that your template can appropriately get the rankings you want.
I have three stores in my application EmployeesTree, Empolyees, History.
First one is source for treegrid, second for combobox that is used to pass additional parameters while loading third stode.
My first approach was to create second store using standard proxy and load employees from server using request.
In my history grid I have:
me.c = Ext.create("MyApp.Store.Employees");
me.c.load();
me.tbar=
[
{
xtype: 'combo',
fieldLabel: 'Employee',
width: 300,
labelWidth: 65,
typeAhead: false,
forceSelection: true,
store: me.c,
queryMode: 'local',
displayField: 'Name',
valueField: 'Id',
listeners: {
select: function (combo, records) {
console.log(records[0].get('Id'));
me.store.proxy.extraParams.uid = records[0].get('Id');
me.store.load();
},
scope: this
}
}
];
But this cause additional request that I would like to avoid.
My second idea was to create empty store and add every leaf from first store to it (because it will contains the same list of employees but without hierarchy)
I've created method that should copy all leafs to new store:
cloneStore: function(source) {
var target = Ext.create('Sch.data.ResourceStore', {
model: 'MyApp.Model.Employee'
});
var node = source.getRootNode();
node.eachChild(function (myNode) {
if (myNode.isLeaf()) {
var newData = Ext.clone(myNode.copy().data);
var model = new source.model(newData, newData.id);
target.add(model);
}
});
Right now this copies first level of my store, I need to add recursive to this method so it will add leafs from subnodes.
Also when I do var newData = Ext.clone(myNode.copy().data); my new record will have all fields of that treestore, how can I copy only two fields from my source store to target. I need only 2 fields (id, Name).
How should I modify cloneStore method to recursively copy employees from treeStore to Store and how can I copy only needed fields? Do I must specify them in my method.
EDIT
I've modified my function so it can be called recursively.
cloneStore: function(source) {
var target = Ext.create('MyApp.Store.EmployeesForComboBox');
var node = source.getRootNode();
this._addLeafsToStore(target, node);
return target;
},
_addLeafsToStore: function(store, node) {
node.eachChild(function (myNode) {
if (myNode.isLeaf()) {
store.add({
Id: myNode.get('Id'),
Name: myNode.get('Name')
});
} else {
this._addLeafsToStore(store, myNode);
}
},this);
},
This works quite well, but I would like to create more universal function, because right now I have hardcoded Store and fields I want to copy.
Additional question:
What option is better: to add new record to store using Ext.clone (first solution) or adding only necessary fields (second solution)?
To create a more generic function, you'll need to change hardcoded stuff as function parameters:
cloneStore: function(source, target, fields) {
var node = source.getRootNode();
this._addLeafsToStore(target, node, fields);
return target;
},
_addLeafsToStore: function(store, node, fields) {
node.eachChild(function (myNode) {
if (myNode.isLeaf()) {
var config = {};
for (var i = 0; i < fields.length; i++) {
config[fields[i]] = myNode.get(fields[i]);
}
store.add(config);
} else {
this._addLeafsToStore(store, myNode, fields);
}
},this);
},
and call it like this:
var cloned = someObj.cloneStore(source, Ext.create('MyApp.Store.EmployeesForComboBox'), ['Id', 'Name']);
For the additional question, I think the second solution will perform better if your data set is large.
I am working with a Playlist object which has some properties defining itself as well as a PlaylistItem collection.
When I receive data from my server, I get its JSON response in my client-side success method:
success: function (data) {
console.log("JSON data:", data);
playlists = _.map(data, function (playlistConfig) {
return new Playlist(playlistConfig);
});
...
}
Here, I convert my JSON data into Playlist objects. Each Playlist object is a Backbone.Model.
Here's how my data looks:
And here's what the Playlist constructor looks like:
return function(config) {
var playlist = new Playlist(config);
return playlist;
};
var Playlist = Backbone.Model.extend({
defaults: function() {
return {
id: null,
userId: null,
title: 'New Playlist',
selected: false,
position: 0,
shuffledItems: [],
history: [],
items: Backbone.Collection.extend({
model: PlaylistItem
})
};
},
...
}
My problem:
If I create a Playlist object with defaults, it initializes with an empty Backbone.Collection for PlaylistItem. However, if I create a Playlist object with an already-defined collection, I get a basic array and not a Backbone.Collection. This is because I am working with JSON data from the server which has not been converted to Backbone entities yet. That data is extended over the Playlist's defaults and overwrites the Backbone.Collection entity.
What is a proper way to initialize with a populated Backbone.Collection? I could write code in Initializes which checks the type of my items array and if it is not a Backbone.Collection I could create a new Backbone.Collection and add the items to it and then replace the old array with the new one, but that seems really hoakey.
Don't define your PlalistItems Collection inside defaults, but beforehand.
Then, create an initialize method on your Playlist Model like so:
var PlaylistItems = Backbone.Collection.extend({
...
});
var Playlist = Backbone.Model.extend({
initialize: function() {
this.set('items', new PlaylistItems(this.items));
},
defaults: function() {
return {
id: null,
userId: null,
title: 'New Playlist',
selected: false,
position: 0,
shuffledItems: [],
history: [],
items: [] // don't define your PlaylistItems Collection here
};
}
});
Check out the fiddle here: http://jsfiddle.net/georgedyer/r2XKb/
(you'll need to open the console to see the collection)
Another issue I ran into was after you save your model to the server you will get back a response that will change your embedded collection into a regular javascript array. To remedy this I had to override the parse function on my model class like so:
var model = backbone.Model.extend({
urlRoot : "/rest/model",
initialize: function(){
this.set("myCollection", new MyCollection(this.myArray));
},
defaults: {
myArray: []
},
parse: function(response){
this.set(response);
this.set("myArray", new MyCollection(response.myArray));
}
});
We created a Collection of models from an array as given below. Our question is: will the Collection preserve the same order of elements/models as it was present in the original array? In other words, is the same order (of source array) guaranteed in Collection?
var colorsData = [
{
name: 'red'
},
{
name: 'blue'
},
{
name: 'green'
},
.....
.....
.....
//other colors
];
var Color = Backbone.Model.extend({
defaults: {
name: 'white'
}
});
var ColorCollection = Backbone.Collection.extend({
model: Color
});
var colorCollection1 = new ColorCollection(colorsData); //creating a collection from the source array colorsData.
From all that I can gather, yes a Collection preserves the original order of elements.
You can read the annotated source for collection to see for yourself that it isn't magically shuffled time to time. Such functions as at, unshift, pop, push, etc. are quite clear indications of this.
You can read the annotated source for parse, which is used to parse the response from server after for example fetch -oprations:
parse: function(resp, xhr) {
return resp;
},
To ascertain that whatever is passed on to your collection won't be shuffled anywhere. The resp will be passed on to the add -function that will process the response in natural order.
As stated in http://backbonejs.org/#Collection
collection.at(index) retrieves models in insertion order.