Ember.js nested ArrayController and Models - javascript

I have an nested data structure as follows:
Job:{
JobId: 1,
NumberTrackr: 2,
Trackrs: [
1 : { TaskTrackrID: a,
NumberSlots:1,
slots: [
slot1: {uniqueId:foo, you: get, the:[point, by, now]}
]
},
2 : { TaskTrackrID: b,
NumberSlots:1,
slots: [
slot1: {uniqueId:bar, you: get, the:[point, by, now]}
]
}
]
}
And my application uses this data hierarchy (calculating the sum of a specific Trackr's 'children's' foo attribute) as well as each data level as a whole (for example, calculating statistics from the content of all slots regardless of their 'parent' TaskTracker).
Im new to Ember, but my I was thinking of creating a model for each object level (TaskTrackr, Slot etc). This model would consist of all the attributes shown above as in addition to some sort of array of sub models (using arraycontroller?). Said submodels would also have attributes as well as their own array of sub models. It is important that the highe level objects can have computed properties calculated from their sub objects. It is also important that I can access each level as a whole and modify specific attributes.
What would the code look like that would enable me to access this data both as a tree as well as on a level basis?
Thank you so much for your help

Take a look at Ember Data. It supports these types of relationships.
App.Job = DS.Model.extend({
trackers: DS.hasMany('App.Tracker')
})
App.Tracker = DS.Model.extend({
job: DS.belongsTo('App.Job');
})
// ... and so on
var job = App.Job.createRecord();
job.get('trackers').pushObject(App.Tracker.createRecord());
Just to note. You mentioned using ArrayController and you will likely use that inside of your Ember application. However, when modeling your data/relationships you will not use any controllers. You should be able to do do what you pasted above with using only DS.Model

Related

How to send Rails parent/child to front end JS so it's accessible as a nested object

In backend, my object relationship is that an Item has_many Options. I'd like to be able to access all the attributes on the item and its child options as a hash in the front end:
items = [
{
id: 1,
item_attribute_name: item_attribute_value,
options: [
{id: 1, option_attribute_name: option_attribute_value},
{id: 2, option_attribute_name: option_attribute_value},
]
},
{
id: 2,
item_attribute_name: item_attribute_value,
options: []
}
]
I'm sending data either via a json object in response to an ajax request or using the handy gon gem. I noticed that if I were JUST interested in sending the parent items, the formatting automatically happens such that I can just send back Item.all and in the front end, get an array of items with each item being a hash that represents its attributes exactly as I want.
But if I want to send the children is there a standard way of doing it? I realize I can construct the child attributes myself as below, but wondering if there's a more straight forward direct way.
How I would make this work by constructing the child attributes:
items = Item.all
items.each do |i|
child_attr = {"options" => i.options }
i.attributes.merge(child_attr)
end
A totally acceptable answer, by the way, is that there's no... automagical way of doing this without doing what I'm doing now, which is converting each parent object to attributes in backend, and then stitching together the child attributes.
I'm only asking this question, frankly, because it'd be nice to keep the object relationships in the backend for reuse elsewhere, if possible, rather turning things into a hash.
I think the only way to get the expected result is with some monkey patching. So you can use a serializer, or include, or use ActiveModel::Serializers::JSON which is included by default in your models.
For exemple with ActiveModel::Serializers::JSON, you can do something like:
items = Item.all
items.map! do |i|
i.serializable_hash(include: { options: {} })
end
This is due to rails eager loading, which avoids having to load all children from an association(has_many for example). Where you'd like to serialize is up to your use case.

Sequelize.build with associations

I'm creating a multipage survey with Node.js 6, Express.js 4 and Sequelize 4.4.2. While the user fills out the survey several model objects are build, but not persisted, this will not happen until the survey is completely done.
Some of these models are associated with each other and I want to know if it's possible to use the .build() function of an defined model with initial values (such as "name" or "address") as well as an previously build but not persisted model object.
Maybe a simple example, what I mean:
const comp = Company.build({
Name: 'My Company',
Location: 'Ireland',
Employees: [] // Employee would be another model in this case
});
It seems, that Employees is ignored while the obejct is built. Is there a way to attach properties which are NOT defined as field for the model (in this case: Company) but as association?
Hope you got, what I mean ...
Thank you in advance! :)
Solved it by myself! I just append the built models after to the dataValues property of the previously built model object and not directly whilst the build process.

Backbone.js What is the purpose of specifying a model in collection

Here is what I am trying to understand.
Often times I find myself writing backbone like this:
var CallModel = Backbone.Model.extend({
});
var CallsCollection = Backbone.Collection.extend({
model: CallModel,
url: 'url/to/external/json'
});
It is a very basic example but as you can see, there is nothing really in the model all the data is coming into the Collection via an external url call to a json file that is build from a database.
So whats the purpose of the model? I am sure that I am probably not using backbone.js to its fullest extent which is why I am here asking you guys.
First of all, "there is nothing really in the model all the data is coming into the Collection via an external url call" - this is not true.
Let's assume you've the following:
//Model
var CallModel = Backbone.Model.extend({
defaults: {
cost:0,
duration:0
}
});
(without custom attributes or methods, there is no point in extending the original Backbone.Model)
//Collection
var CallsCollection = Backbone.Collection.extend({
model: CallModel,
url: 'url/to/external/json'
});
And the json data returned from service, probably something like:
//Response
{
callSummary: {
missed: 2,
received: 3,
totalCalls:5
totalDuration: 20
}
calls: [{
id:001,
caller:"Mr.A",
callee:"Mr.B",
cost:1,
duration:5
},{
id:002,
caller:"Mr.X",
callee:"Mrs.Y",
cost:1,
duration:7
},{
id:003,
caller:"Mr.A",
callee:"Mrs.B",
cost:1,
duration:8
}],
//and more additional information from your db
}
Now you populate your collection with data by calling it's fetch method:
CallsCollection.fetch();
Your collection should look something like:
{
models: [{
attributes: {
callSummary: {},
calls: [{},{},{}],
...
},
...
}],
length:1,
url: "url/to/external/json",
...
}
The data will be added to a model's attribute hash. If you don't specify a particular model, as Bart mentioned in his answer, backbone will populate the collection with a Backbone.Model instance: Which is still not much useful - Wew... A collection with single model having entire response data inside it's attributes as it is...
At this point, you're wondering why did I even bother creating a model, and then a collection..?
The problem here is Collections are derived from Arrays, while Models are derived from Objects. In this case, our root data structure is an Object (not an Array), so our collection tried to parse the returned data directly into a single model.
What we really want is for our collection to populate its models from the "calls" property of the service response. To address this, we simply add a parse method onto our collection:
var CallsCollection = Backbone.Collection.extend({
model: CallModel,
url: 'url/to/external/json',
parse: function(response){
/*save the rest of data to corresponding attributes here*/
return response.calls; // this will be used to populate models array
}
});
Now your collection will be something like the following:
{
models: [{
...
attributes: {
...
id:001,
caller:"Mr.A",
callee:"Mr.B",
cost:1,
duration:5
}
},{
...
attributes: {
...
id:002,
caller:"Mr.X",
callee:"Mrs.Y",
cost:1,
duration:7
}
},{
...
attributes: {
...
id:003,
caller:"Mr.A",
callee:"Mrs.B",
cost:1,
duration:8
}
}],
length:3,
url: "url/to/external/json",
...
}
This - is what we want! : Now it is very easy to handle the data: You can make use of the add, remove, find, reset and handful of other collection methods effectively.
You can pass this models array into your templating library of choice, probably with two way bindings: When the respective view for one of the call model changes, the particular model will be updated, events will propagate from your models to the collection, and the particular model will be passed into the handler functions.
You can now call fetch, save, destroy, clear and a lot of other methods with ease on single unit's of data (each model), rather than hurdle with the entire data saved in a single model - which is pretty much useless, you've to iterate through the response data manually and perform CRUD and similar operations by your own, and in most cases: re-render the entire collection view. which is very, very bad and totally unmaintainable.
To conclude: If your data source doesn't return an array of objects, or you don't parse the response and return an array of objects from which n number of models are to be populated - Then defining a collection is pretty much useless.
Hopefully, now you get the idea.
Very helpful source of info:
Backbone, The Primer: Models and Collections
Developing Backbone.js Applications
backbonejs.org
You don't need to specify a model. A Backbone collection will default to using Backbone.Model if you don't specify this option. The following would work equally well if you don't need the models of the collection to be of a particular instance.
var CallsCollection = Backbone.Collection.extend({
url: 'url/to/external/json'
});
Reference
EDIT
In essence, specifying the model option within a collection is just a way to ensure that objects added to this collection will be instances of that particular model class. If the models being added to your collection don't have any custom behaviour outside of what is available to Backbone.Model, you don't need to create and specify a model as Backbone collections will default to using an instance of Backbone.Model as I have already mentioned. If, however, you wanted to ensure that models added to a particular collection were of a particular type and shared customized behaviour (e.g. validations, defaults, etc.), you would create your own model class by extending Backbone.Model and specifying this in the collection. I hope this clears things up for you.
Sounds Weird but this is the way.
Every collection in backbone, must represent a model, so basically a collections is a list of models.
Even if your model has no data, you need to indicate it when you create a Collection.
This is how backbone works for collections.

Ember.js: How one model can observe other models?

I have a model for my sources and a model for each segment. Each source has many segments. Each segment has an action that toggles an isSelected property.
I need to keep an updated list of selected segments. My first thought was to do something like...
App.Source = Ember.Object.extend({
selectedSourceList = Em.A(),
selectedSourcesObserver: function() {
// code to update array selectedSourceList
}.observes('segment.isSelected')
});
.. but that observes() function isn't right. I'm new to ember, so my approach may be completely wrong.
How can a method in one model observe the properties of many other models?
EDIT: corrected names to indicate that the segment model is for a single segment - not a collection of segments (that is what I plan to do in the sources model).
I think there are three parts to your question:
how to observe a collection
observers in general
managing relationships
observing a collection
The #each property helps observe properties for items in a collection: segments.#each.isSelected
observers in general
.observes() on a function is a shorthand to set up an observer function. If your goal for this function is to update a collection you might be better served by using .property() which sets up an observer and treats the function like a property:
selectedSegments: function() {
return this.get('segments').filterProperty('isSelected', true);
}.property('segments.#each.isSelected')
This means selectedSegments is the subset of segments from this object that are selected and is automatically managed as items are dropped or marked selected.
managing relationships
For plain Ember Objects you will need to manage the relationships, pushing new items into the array, etc.
segments = Em.A(), //somehow you are managing this collection, pushing segments into it
Also note the difference between Ember Objects and Ember Models. Ember Data is an optional additional library that allows specifying models and relationships and helps to manage the models for you. With Ember data you might have something like:
App.Source = DS.Model.extend({
//ember-data helps manage this relationship
// the 'segment' parameter refers to App.Segment
segments: DS.hasMany('segments'),
selectedSegments: function() {
return this.get('segments').filterProperty('isSelected', true);
}.property('segments.#each.isSelected')
});
And App.Semgent
App.Segment = DS.Model.extend({
selection: DS.belongsTo('selection')
});

Implementing Backbone.Subset.js in Backbone.js to filter Models from a parent Collection

In this stackoverflow post i read about filtering backbone collections and using subsets.
One answer (by sled) recommends using backbone.subset.js (usage example).
I could not find any further resources on backbone.subset.js and I failed implementing it into my project.
It seems like backbone.subset.js is the perfect solution for what i'm trying to achieve.
(Having one "parent" collection that holds all models at all times, and depending on user input filtering the relevant models from the parent collection into a backbone.subset collection.)
My "parent" collection, holding all tasks:
var TasksAll = Backbone.Collection.extend({
url: '/tasks', // the REST url to retrieve collection data
model: Task // the models of which the collection consists of
});
var allTasks = new TasksAll();
Now i want to create a subset collection for e.g. tasks where task.status = 0:
var TasksTrash = new Backbone.Subset({
superset: allTasks,
filter: function(Task) {
return Task.isTrash();
}
});
var trashTasks = new TasksTrash();
Whereas inside the Task model, the method "isTrash" returns true if:
this.get('status') == 0
a) Are there any more resources on backbone.subset.js?
b) How do I implement above scenario?
c) Can I pass 'superset' and 'filter' options as params to the Backbone.Subset init function?
d) I looked into the backbone.subset.js code, when I 'reset' my parent Collection my subset Collections should be updated straight away, right?
PS: I'm fairly new to Backbone. Thanks for your help.
Looking at the source for backbone-subset, it looks as though there is a pre-initialization hook which you could utilize in order to make the 'sieve' or filter available as an option or argument:
https://github.com/masylum/Backbone.Subset/blob/master/backbone.subset.js#L50
As for providing parent as an argument, there is an outstanding patch to add that exact functionality:
https://github.com/masylum/Backbone.Subset/pull/5
With it, you can pass in parent as an option, if it is not an option the library will fall back to looking for it on the object Prototype

Categories

Resources