How to parse nested arrays in a model in emberjs - ember.js

I'm having trouble figuring out how to parse some json data from our backend server.
To start of, here's the data it returns:
{
"newsitem": {
"id": "1",
"title": "Some title",
"images": [
"IMG_0147.JPG",
"desert1.jpg"
],
"videos": [
"AEOpX8tmiUI",
"kxopViU98Xo"
]
}
}
I'm trying to parse this in my model:
App.Newsitem = DS.Model.extend({
title: DS.attr('string'),
images: DS.attr('array'),
videos: DS.attr('array')
});
But this give me an error that 'array' is not supported. How should I go with parsing this data and how should I print out the values of images and videos in the DOM through a handlebars template? I'm looking for a best-practise answer.

A lot of credits to nerdyworm on the #emberjs channel for the answer: you have to create your own serialize/deserialize methods for your new data type like this:
DS.JSONTransforms.array = {
serialize: function(jsonData) {
return Ember.typeOf(jsonData) == 'array' ? jsonData : [];
},
deserialize: function(externalData) {
return Ember.typeOf(externalData) == 'array' ? externalData : [];
}
}
Then in your handlebars template you can do:
<script type="text/x-handlebars" data-template-name="newsitem">
<div class="detail">
{{#each image in images}}
{{image}}<br/>
{{/each}}
</div>
</script>

Related

Ember-data return a single json object with store.find

Is this possible? I know I can do:
this.store.find('model', 1)
but that's not what I want. I would like to retrieve the json in this format: (working if I retrieve it in the route like this ):
App.OptionsRoute = Ember.Route.extend({
model: function () {
return {
"options":{
"id": "1",
"headline": "A Headline",
"results": [
{
"id": "1",
"title": 'Option 1',
},
{
"id": "2",
"title": "Option 2"
}
]
}
};
}
});
options model:
App.Options = DS.Model.extend({
headline: DS.attr(),
results: DS.attr()
});
options.hbs
<h5>{{options.headline}}</h5>
{{#each item in options.results}}
<h5>{{item.title}}</h5>
{{/each}}
I am using the RESTAdapter. And that is the only model that will be retrieved on that route. I would like to be able to use ember-data, but store.find expects an array.
You're missing a point here. First of all you're using bad format for your response. You need custom serializer. You can also use a bit more dirty workaround like this(but it works). Route:
App.OptionsRoute = Ember.Route.extend({
model: function() {
that = this;
return new Promise(function (resolve, reject) {
url = that.store.adapterFor('option').buildURL('option');
Ember.$.getJSON(url).then(function (json) {
body = json.options;
correct = {
options: [
body
]
};
that.store.pushPayload('option', correct);
resolve(that.store.all('option').get('firstObject'));
});
});
}
});
Template:
<h5>{{model.headline}}</h5>
{{#each item in model.results}}
<h5>{{item.title}}</h5>
{{/each}}
Application outputs:
A Headline
Option 1
Option 2
Working demo - please notice that I'm using $.mockjax to recreate your response from server, but it matches format you provided.

How to get List for hasMany relation in Fixture adapter in ember-data

I am new to ember.. I have 2 models..
Music.Artist = DS.Model.extend({
name: DS.attr('string'),
dob : DS.attr('date'),
songs : DS.hasMany('song',{async:true})
});
Music.Artist.FIXTURES=[
{
id:1,
name:'John',
dob:new Date(),
songs:['1','2']
},
{
id:2,
name:'Robbin',
dob:new Date(),
songs:['1','2']
}
];
Music.Song = DS.Model.extend({
title:DS.attr('string'),
artists:DS.hasMany('artist',{async:true})
});
Music.Song.FIXTURES = [
{
id:1,
title:'A day to remember',
artists:[1,2]
},
{
id:2,
title:'Cant live without you',
artists:[1,2]
}
];
I want for url "/songs/id"... I get all the songs that has an artist with the given id.
Music.Router.map(function(){
this.resource('songs',{path:'/songs/:id'});
});
Music.SongsRoute = Ember.Route.extend({
model:function(param){
var artist = this.store.find('artist',param.id);
return artist.get('songs');
}
});
But it returns undefined... How to get the list of songs that are related to the Artist.
Is there any way i can achieve this by using only routes.
How to read the array of songs, if not through get.
Based on the current versions of Ember (1.6.1) and Ember-Data (1.0.0-beta.9), here's how I got your example working. I changed your route naming, I think you really want something like /artists/:artist_id which will list the artist's data, including all his songs.
Your Artist and Song model declarations seem fine, but I declared the fixtures like so:
Music.Artist.reopenClass({
FIXTURES: [
{
id:1,
name:'John',
dob:new Date(),
songs:['1','2']
},
{
id:2,
name:'Robbin',
dob:new Date(),
songs:['1','2']
}
]
});
Music.Song.reopenClass({
FIXTURES: [
{
id:1,
title:'A day to remember',
artists:[1,2]
},
{
id:2,
title:'Cant live without you',
artists:[1,2]
}
]
});
For the router:
Music.Router.map(function() {
this.resource('artists');
this.resource('artist', { path: '/artists/:artist_id' });
});
For the routes:
var Music.ArtistsRoute = Ember.Route.extend({
model: function() {
return this.store.find('artist');
}
});
var Music.ArtistRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('artist', params["artist_id"]);
}
});
For your templates:
// artists.hbs
<ul>
{{#each}}
<li>{{#link-to 'artist' this}}{{name}}{{/link-to}}</li>
{{/each}}
</ul>
// artist.hbs
<h1>{{name}}</h1>
<hr>
<h2>Songs</h2>
<ul>
{{#each songs}}
<li>{{title}}</li>
{{/each}}
</ul>
Hope this helps!

Unable to access multiple nodes of model object in handlebarjs template

I have posted my code here.
Here i am trying to access data of inner node of fixture but it is not working and it is showing blank results.
template code :
<ul>
{{#each item in model}}
{{#each item in item.list}}
{{#each item in item.contents}}
<li>{{item.product_name}}</li>
{{/each}}
{{/each}}
{{/each}}
</ul>
Can anyone tell me how to access data of inner node of object in template using each helper?
I have updated my code here.
Here i am getting data of first node of home_products i.e.
"contents": [
{
"id": "1",
"product_name": "abc11"
},
{
"id": "2",
"product_name": "abc12"
}
]
Failed to get data of second node i.e.
"contents": [
{
"id": "1",
"product_name": "abc21"
},
{
"id": "2",
"product_name": "abc22"
}
]
every time i am getting same data i.e. data of first node.
Can any one help me to solve this issue?
You need to change your template name to index to match the route where the data is returned by model hook:
// that route belongs to index template
Astcart.IndexRoute = Ember.Route.extend({
model: function() {
return Astcart.Application.find();
}
});
Just change the template to:
<script type="text/x-handlebars" data-template-name="index">
And change the mapping of the Astcart.Contents to product_name instead of name. To match with your fixture:
Astcart.Contents = Ember.Model.extend({
product_name: Ember.attr()
});
Fixtures:
...
"contents": [
{
"product_name": "abc",
},
{
"product_name": "pqr",
}
]
...
Working example http://jsfiddle.net/marciojunior/NKTcc/

How should I iterate over an array in emberjs and update the route to reflect the change?

I'm trying to make an image slideshow with next/previous buttons but I don't understand how I should make it work all together. Can someone help me glue the pieces?
This is how the data looks like:
{
"newsitems": [
{
"id": "1",
"title": "Amai het wordt bangelijk!",
"mediaitems": [
{
"id": "AEOpX8tmiUI",
"newsitem_id": "1",
"value": "AEOpX8tmiUI"
},
{
"id": "kxopViU98Xo",
"newsitem_id": "1",
"value": "kxopViU98Xo"
},
{
"id": "1",
"newsitem_id": "1",
"value": "1276468632.JPG"
},
{
"id": "3",
"newsitem_id": "1",
"value": "1278286227.jpg"
}
]
}
]
}
I'd like to let the user iterate the mediaitems array. So when the user enters a route:
http://domain.com/#!/newsitems/1/overlay/kxopViU98Xo
That item should be in the center of the screen and a next/prev button should take the user to the next or previous item in the array.
This is how my route looks like:
App.MediaitemRoute = Ember.Route.extend({
model: function(params) {
return App.Mediaitem.find(params.mediaitem_id);
},
setupController: function(controller, model) {
this.controllerFor('overlay').set('content', model);
},
renderTemplate: function(controller, model) {
this.render('overlay', { into: 'application', outlet: 'overlay', controller: 'overlay'});
}
});
And this is the ObjectController for the overlay:
App.OverlayController = Ember.ObjectController.extend({
prev: function(){
var mediaitems = this.get('model').get('newsitem.mediaitems');
var currentMediaItemID = this.get('model').get('id');
var currentIndex = 0;
var i = 0;
mediaitems.forEach(function(object){
if(object.get('id') == currentMediaItemID) currentIndex = i;
i++;
});
// How should I transition to the next object?
}
});
The route renders a template called "overlay' which has two actions: "prev" and "next" (I'm only showing prev in my controller to keep things clean).
My two concerns are: the code to lookup the currentitem in the array (and code to get the next or previous object) just feels 'wrong' or overly 'complex'.
I don't know how to update the route and redirect to the next/previous item.
Some more useful stuff:
Handlebars template:
<script type="text/x-handlebars" data-template-name="overlay">
<div class="overlay">
<a {{action "prev" target="controller"}}>PREV</a>
<a {{action "next" target="controller"}}>NEXT</a>
</div>
</script>
My models:
App.Newsitem = DS.Model.extend({
title: DS.attr('string'),
mediaitems: DS.hasMany('App.Mediaitem')
});
App.Mediaitem = DS.Model.extend({
value: DS.attr('string'),
newsitem: DS.belongsTo('App.Newsitem')
});
My routes:
App.Router.map(function(){
this.resource('newsitems', function(){
this.resource('newsitem', {path:':newsitem_id'}, function(){
this.resource('mediaitem', {path:'/overlay/:mediaitem_id'});
});
});
});

emberjs pre-4 and ember-data: no data on browser-refresh

If I browse from the index to a game, the data is shown, because of {{#linkTo}}s context, but if I'm refreshing the site, every time the game name doesn't show up.
EDIT: Here is a fiddle, but unfortunately the fiddle-version with the fixture adapter works properly, even with ken's suggestion to remove the model from the game-template.
the returned data from /api/games is as follows:
{
"games":[
{
"id":1,
"name":"First Game"
}
]
}
and the returned data from /api/games/1
{
"game": [
{
"id": 1,
"name": "First Game"
}
]
}
<script type="text/x-handlebars" data-template-name="application">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="games">
<ul>{{#each game in controller}}
<li>{{#linkTo 'game' game}}{{game.name}}{{/linkTo}}</li>
{{/each}}</ul>
</script>
<script type="text/x-handlebars" data-template-name="game">
<h1>Name:{{model.name}}</h1>
</script>
<script type="text/javascript">
App = Ember.Application.create();
App.Game = DS.Model.extend({
name: DS.attr('string')
});
App.Store = DS.Store.extend({
revision: 11,
adapter: DS.RESTAdapter.create({
namespace: 'api'
})
});
App.GamesController = Ember.ArrayController.extend({
content: []
});
App.GameController = Ember.ObjectController.extend({
content: null
});
App.Router.map(function(match) {
this.route("games", { path : "/" });
this.route("game", { path : "/games/:game_id" });
});
App.GameRoute = Ember.Route.extend({
model: function(params) {
return App.Game.find(params.game_id);
}
});
App.GamesRoute = Ember.Route.extend({
setupController: function(controller) {
controller.set('content', App.Game.find());
}
});
</script>
Has anyone an idea?
Remove model from your game template, try this instead
<script type="text/x-handlebars" data-template-name="game">
<h1>Name:{{name}}</h1>
</script>
JSFiddle your code to see where the problem is
The response from my server for a single game was wrong. I had an array with a single object in it, but I used the Object controller, so I fixed it with just responding with
{
"game": {
"id": 1,
"name": "First Game"
}
}