In my Ember app, I have a complex model that looks like below (kind of contains 2-dimensional array)
[
[
{
id: 'Section1_123',
label: 'abc'
},
{
id: 'Section1_456',
label: 'xyz'
}
]
],
[
[
{
id: 'Section2_123',
label: 'abc'
},
{
id: 'Section2_456',
label: 'xyz'
}
]
]
There are a lot of other attributes, but this is the overall structure.
Now my question is can I drill-down & find a specific object. It has unique ids (as shown in the example above)
So I need something like model.findBy(Id)
I then need to change/set some values for that object. Say I want to change the obj.label from 'abc' to 'abc_NEW'
Just to add, The main model is actually a simple JS array...but the inside objects (e.g. those with id: 'Section1_123', etc) are actually Ember objects
Most common approach to work with data in Ember is EmberData. And because the main credo of Ember is "convention over configuration" then a common way in Ember is the best way, in my opinion.
There are many ways how to deal with your data format. I would recommend to create model for each item:
import DS from 'ember-data';
export default DS.Model.extend({
label: DS.attr()
// other properties
});
Then you can make a custom serializer according this article. The goal is to convert your arrays to list of EmberData models.
After this you can use standard EmberData functions to work with data (including access by object id, of course).
Related
I create backbone collection from server JSON. Data is from mongo so each item has same objects and backbone remove this duplicates. It's unwanted behavior for me so, I can't find solution to keep this instances. After fetch my items has only 'section1' in secound object (id:2). I need the same section also in first object. For example my server response is:
items: [{
id:1,
sections: [{
id: 1.//this object is removed
name: 'section1'
}]
}, {
id: 2,
sections: [{
id:1.
name: 'section1'
}]
}]
My section model is just:
Section = Backbone.RelationalModel.extend({
});
and Item model:
Item = Backbone.RelationalModel.extend({
relations: [
{
'type': 'HasMany',
'key': 'sections',
'relatedModel': 'Section',
'includeInJSON': 'id',
'reverseRelation': {
'key': 'item',
'includeInJSON': 'id'
}
}
]
});
If I recall correctly, this plugin doesn't support many-to-many. So, what's happening is that it is attaching Section 1 to the first Item, then attaching it to the second and removing it from the first.
In fact, from the docs:
Backbone.HasMany
Defines a HasMany relation. When defining a reverseRelation, the type
will be HasOne.
Your options:
Create a SectionItem model that HasOne Section and HasOne Item. Someone posted a fiddle with this sort of setup http://jsfiddle.net/mmacaula/XaESG/2/
Use another library - or an extension of the one you use, like https://github.com/jj-studio/Backbone-JJRelational
Add a property to the Section model key that would make each one unique. This is not a good way to achieve what you are trying to do, though.
Here is a pretty good reference answer: Implementing a Many-to-Many relationship with Backbone-Relational
Note: I imagine that this will be a super easy question for anyone with Ember experience. Don't be daunted by the length of my question. Skip down to the bottom if you don't want to read all of the overhead.
Overhead
My company has a project coming up which requires the use of front-end technologies to accomplish what I would otherwise prefer to do with PHP.
We looked into a few different JavaScript frameworks, and the solution that we agreed upon was Ember.js.
I followed the TodoMVC tutorial on their website, and learned the very basics.
With this project, we will be using an AJAX request to pull in our data at the start of the application, and then put everything into fixtures.
I'm having difficulty figuring out how to pass multiple fixtures into my template at the same time. I started with adding two fixtures. Here are their data and definitions:
App.Students = DS.Model.extend({
first: DS.attr('string'),
last: DS.attr('string'),
classes: DS.hasMany('ClassGroup')
});
App.ClassGroup = DS.Model.extend({
className: DS.attr('string'),
isActive: DS.attr('number'),
students: DS.hasMany('Students',{ async: true })
});
App.ClassGroup.FIXTURES = [
{
id: 1,
className: 'Class 1',
isActive: 1,
students: [1, 2]
},
{
id: 2,
className: 'Class 2',
isActive: 0,
students: [2]
}
];
App.Students.FIXTURES = [
{
id: 1,
first: 'Student',
last: 'One',
classes: [1]
},
{
id: 2,
first: 'Student',
last: 'Two',
classes: [1, 2]
}
];
This is just a very simple implementation. The actual data will have dozens of relations, but I've simplified things for the purpose of learning this framework.
Here is how I am currently setting up my router:
App.IndexRoute = Ember.Route.extend({
model: function() {
return this.store.find('ClassGroup');
}
});
Doing it this way allows me to access the data via Handlebars by using {{#each model}} and then something like {{className}} to access it's data members. I am also able to jump to the students array by nesting another each statement like {{#each students}}.
However, I cannot figure out how to get more than one entry point into my data. For example, by passing ClassGroup via the router, I am unable to access the Students fixture by itself; I must first loop through ClassGroup, and from there, access a student.
Likewise, if I change my router to pass in the students fixture, I can loop through the students, and then jump to the classes via a nested each statement, but I lose the ability to simply loop through a list of all classes.
Is there a way that I can pass all of my fixtures into the template? Am I going about this the right way?
The Long Story Short
How can I pass ALL of my fixtures into the template at once, in such a way that I can access my students array or my classes array? How can I access said fixture data (i.e., if I want to display the first name of the student with ID 2, represented as students[2]['first'] in a language like PHP, how can this be done with handlebars)?
That's right, the Template only has access to what it's been passed by the Controller. In this case, since you don't explicitly set up the controller, and the model is an array, it'll be an array controller, hence you ability to do {{#each}} to iterate over the ClassGroups (you actually don't even need model). You haven't passed in the students array anywhere explicitly, nor created it in the controller, so you don't have access to it in the template. Fortunately, Ember has a setupController route hook which does exactly this kind of thing. In your example:
App.IndexRoute = Ember.Route.extend({
model: function() {
return this.store.find('ClassGroup');
},
setupController: function(controller, model){
this._super(controller, model);
controller.set('students', this.store.find('Students'));
}
});
Now you'll have a students property available on your controller and therefore your template.
The server responds with something on the form:
{'dates':
{'2013.05-17':
{'activities':
{'activity 1':
{time: 0, 'synced': false},
'activity 2':
{time: 5, 'synced': false},
'activity 3':...
},...
},
'2013.05-18':
{ ...}, ...},
'id': id}
I currently put everything in a single Backbone model, which doesn't seem like the proper way to do it. The examples I've read around the web all use very simple models where there's no nestled structures and the mapping is pretty simple e.g. {x: 1, y: 2} being mapped to a coordinate model and so on.
What's the "correct" way to map the above JSON structure to Backbones models/collections?
As Protostome mentions, Backbone Relational is good for this kind of thing.
However looking at the JSON data that you have used as an example, from my viewpoint you have only one model and collection as follows:
Activity Model
Activities Collection
Even though you have a nested set of data you could look at it in a different way which gives you a simple backbone model, for example:
var activity = {
id: "Activity 1"
time: 0,
synced: false,
date: "2013.05-17"
batchId: id // this corresponds to the id property in your example
}
This represent all of the data contained in your complex nested hierarchy more simply, and more importantly suited to the way Backbone works.
If you cannot alter what the server provides you, you could use the Underscore library functions (from memory _.map()) to map the JSON you receive into simple JSON objects ready for use with Backbone.
I am hoping that maybe you can simplify your design by thinking outside the the context of sticking with a hierarchy?
edit: can I pass a parameter to a new view declaration ? so something like
new articleView({
template: "my desired template",
})
Suppose I have an array of objects, where each object represents a topic and contains a few properties: a title, a template type, and an array of articles. All the topics render nearly identical minus a few template differences.
I am using backbone and I have a general question: should each "topic" be a separate instance of the same collection type? Where would I declare the template type to be used for each topic? Should the collection have a variable template type property?
var topics = [
{
title: "Topic One",
template: "detailedView",
articles: [
{
title: "A very good article",
timestamp: "2013-01-24"
},
{
//more articles here
}
]
},
{
//another topic here...
}
];
To answer your first question, you can certainly pass parameters when instantiating a new view. The relevant part of the documentation reads as follows:
When creating a new View, the options you pass — after being merged
into any default options already present on the view — are attached to
the view as this.options for future reference.
So your template parameter would be available in your view instance like so:
var template = this.options.template;
To answer your general question, I think what you mean is should I define a single collection containing a separate instance of the same model type to represent each topic? In which case, based on your description of your data structure, I would suggest that this is a good way to go about it. The Topic model can certainly contain a property to identify its template.
In the context of a web app, I have a server which sends or receives JSON strings based on the input from the client. On client consumption, these JSON strings are immediately converted into JavaScript objects where they will live out their lives as objects. These objects are not arrays; they represent complex, arbitrary data models, each property of which can have an arbitrary number of unique subproperties or objects.
var myView = {
name: 'root'
id: 'root_0'
children: {
child_1: {
arbitraryid: 'root_0_child_1',
type: 'Department',
name: 'Produce',
quadrant: 1,
children: {
child_1: {
arbitraryid: 'root_0_child_1_child_1',
type: 'Aisle',
number: 3,
length: 12,
children: { }
}
}
},
child_2: {
arbitraryid: 'root_0_child_2',
name: 'West',
type: 'Region',
children: {
child_1: {
arbitraryid: 'root_0_child_2_child_1',
name: 'Wegmans',
type: 'Store',
children: {
child_1: {
arbitraryid: 'root_0_child_2_child_1_child_1',
type: 'Department',
children: { }
}
}
}
}
}
}
};
When I build the JSON string server side, I guarantee that all objects will have 'children' and 'arbitraryid' properties; but everything else is dynamically generated and the properties and values are completely arbitrary.
If this were XML, I could use jQuery to var someChild = myView.find('#root_0_child_1_child_1'). This would get me a jQuery object with the results of the find AND not only a reference to myView but a position from which to move omnidirectionally through the object: var someChild = myView.find('#root_0_child_1_child_1').parent().
Does a utility exist to solve this problem for native, JavaScript objects or is there a preferable way/methodology to do this? I'd like to avoid writing a bunch of this type of code to simply get at my property and then potentially loop again to update the parent object.
while (obj.hasOwnProperty('children')) {
for (var child in obj) {
//..etc, etc
}
}
Most of the SO questions I see on this subject deal with searching arrays, frequently with predictable data table style construction.
Mapping is possible, but these objects quickly become deep and that option seems little better than dumb looping.
Ideas?
Edit: rolling my own utility class.
I'm still exploring other libraries/utilities, but I wrote a generic helper class to do searches:
ObjectHelper
While useful, I think it illustrates some of the difficulty with getting at other jQuery-like functionality. Not only would I like to search, but I'd like to be able to crawl up/down the object property structure similarly to the way you can chain .parent().children().find() operators together. Doable, but complicated.
I found a few JSON Query Languages:
JSONPath
JAQL
LINQ to Javascript
JSINQ
jLinq
SQLike (despite the name, it's used for querying JSON)
TrimQuery (uses a SQL-like language to query JSON objects)
This page sums them up and goes through their pros and cons.
There is a DOJO module for querying JSON as well:
JSONQuery
I'm not sure how stable/standard these are though.
I solved this by rolling my own classes. ShadesJS is pretty basic right now, but it has some methods for crawling JavaScript objects and working with Web Storage. On the to-do is implement parent/child methods to get some JQuery-esque flexibility. It's not hard to do, but it's tricky to get it right and performant.