How can I add the model data from an ajax request to the content of a Ember.CollectionView so that I can create a list of items? I would like to render a list displaying the title from each object in the array returned from the API. I am using Ember Data as I am trying to learn that along with Ember.
Here is a fiddle to my current code. http://jsfiddle.net/ahzk5pv1/
Here is my JavaScript, Templates, and the data I am returning from an API:
JS:
App = Ember.Application.create();
App.ListView = Ember.CollectionView.extend({
tagName: 'ul',
//How do I set the content to be the data from the API???
content: App.Page,
itemViewClass: Ember.View.extend({
template: Ember.Handlebars.compile('the letter is = {{view.content}}')
})
});
App.ApplicationAdapter = App.RESTAdapter = DS.RESTAdapter.extend({
host: 'https://api.mongolab.com/api/1/databases/embertest2/collections',
//Construct query params for adding apiKey to the ajax url
findQuery: function(store, type, query) {
var url = this.buildURL(type.typeKey),
proc = 'GET',
obj = { data: query },
theFinalQuery = url + "?" + $.param(query);
console.log('url =',url);
console.log('proc =',proc);
console.log('obj =',obj);
console.log('theFinalyQuery =',theFinalQuery);
return this._super(store, type, query);
}
});
App.ApplicationSerializer = DS.RESTSerializer.extend({
normalizePayload: function(payload) {
var pagesArray = [];
payload[0].pages.forEach(function(element, index) {
element.id = index;
pagesArray.push(element);
})
return {pages: pagesArray};
}
});
App.Page = DS.Model.extend({
character: DS.attr('string'),
title: DS.attr('string')
});
App.HomeRoute = Ember.Route.extend({
model: function() {
return this.store.find('page', {apiKey: 'somekey'});
}
});
App.Router.map(function() {
this.route('home', {path: '/'});
});
Template:
<script type="text/x-handlebars">
<nav>
{{#link-to 'home'}}Home{{/link-to}}
</nav>
<div class="container">
{{view 'list'}}
</div>
</script>
Data from API:
{
"_id": {
"$oid": "54640c11e4b02a9e534aec27"
},
"start": 0,
"count": 5,
"total": 1549,
"pages": [
{
"character": "Luke Skywalker",
"title": "Star Wars"
},
{
"character": "Sauron",
"title": "Lord Of The Rings"
},
{
"character": "Jean Luc Piccard",
"title": "Star Trek: The Next Generation"
}
]
}
You can use an Ember.ArrayController.
App.items = Ember.ArrayController.create()
App.items.set('content',yourArray)
App.ListView = Ember.CollectionView.extend({
contentBinding: 'App.items'
itemViewClass: Ember.View.extend({
template: Ember.Handlebars.compile('the letter is = {{view.content}}')
})
})
Look at this example
It took some time but this is what I eventually used.
JavaScript:
App = Ember.Application.create();
App.Router.map( function() {
});
App.IndexController = Ember.ArrayController.extend({
});
App.IndexRoute = Ember.Route.extend({
model : function(){
return this.store.find('page', {apiKey: 'keyForApi'});
},
})
App.HomeView = Ember.CollectionView.extend({
tagName: 'ul',
contentBinding: 'controller',
itemViewClass : Ember.View.extend({
tagName : "li",
template : Ember.Handlebars.compile('<p>Name:{{view.content.title}}</p>')
})
});
App.ApplicationAdapter = App.RESTAdapter = DS.RESTAdapter.extend({
host: 'https://api.mongolab.com/api/1/databases/embertest2/collections',
//Construct query params for adding apiKey to the ajax url
findQuery: function(store, type, query) {
var url = this.buildURL(type.typeKey),
proc = 'GET',
obj = { data: query },
theFinalQuery = url + "?" + $.param(query);
console.log('url =',url);
console.log('proc =',proc);
console.log('obj =',obj);
console.log('theFinalyQuery =',theFinalQuery);
return this._super(store, type, query);
}
});
App.ApplicationSerializer = DS.RESTSerializer.extend({
normalizePayload: function(payload) {
var pagesArray = [];
payload[0].pages.forEach(function(element, index) {
element.id = index;
pagesArray.push(element);
})
return {pages: pagesArray};
}
});
App.Page = DS.Model.extend({
character: DS.attr('string'),
title: DS.attr('string')
});
Templates:
<script type="text/x-handlebars" data-template-name="application">
<nav>
Example
</nav>
<div class="container">
{{outlet}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="index">
{{view 'home'}}
</script>
Related
What I am trying to accomplish is pass this Post model ID to a collection so I can populate this specific Post id with other models that are associated with it. For example: In a Blog Post, contains a bunch of Comments. and I want to display those comments pointing to this Blog Post only.
I must be missing something fundamental here.
Below is my Post Model and i am instantiating a new CommentCollection and passing along models and options arguments.
var PostModel = Backbone.Model.extend({
initialize: function() {
/*
* we are connecting Comments collection
* to each post item by passing along post id
* */
this.comments = new CommentsCollection([], { id: this.id });
},
defaults: {
title: 'title here',
body: 'body here'
},
urlRoot: localserver + "/posts"
});
Below is my Comment Collection. console.log(this.id); returns undefined.
var CommentsCollection = Backbone.Collection.extend({
initialize: function(models, options) {
this.id = options.id;
console.log(this.id);
return this;
},
url: function() {
console.log(this.id);
return localserver + "/posts/" + this.id + "/comments";
},
model: CommentModel
});
my console is returning this:
Uncaught TypeError: Cannot read property 'id' of undefined
3
Try this code:
var CommentModel = Backbone.Model.extend({});
var CommentsCollection = Backbone.Collection.extend({
model: CommentModel,
initialize: function(models, options) {
this.id = options.id;
if(typeof this.id === 'undefined') { return; }
this.url();
},
url: function() {
var localserver = "localhost";
console.log('from comment url: ', this.id);
return localserver + "/" + this.id + "/comments";
}
});
var PostModel = Backbone.Model.extend({
urlRoot: "http://jsonplaceholder.typicode.com" + "/posts",
initialize: function(option) {
this.comments = new CommentsCollection([], { id: option.id });
}
});
//var pm = new PostModel();
//pm.comments.fetch();
//console.log('from pm: ', pm.comments.url());
var PostsCollection = Backbone.Collection.extend({
model: PostModel,
url: "http://jsonplaceholder.typicode.com" + "/posts?_sort=views&_order=DESC",
initialize: function() {
this.on('reset', this.getComments, this);
},
getComments: function() {
this.each(function(post) {
post.comments = new CommentsCollection([], { post: post });
post.comments.fetch();
});
}
});
var pc = new PostsCollection();
pc.fetch();
What I did is that I use the option parameter of the PostModal. Below is the code.
var PostModel = Backbone.Model.extend({
urlRoot: "http://jsonplaceholder.typicode.com" + "/posts",
initialize: function(option) {
this.comments = new CommentsCollection([], { id: option.id });
}
});
I'm trying to figure out how to fetch data with Ember Data and render. After several problems I got to this point where I get no error on the console but the store is empty after the data is loaded.
window.App = Ember.Application.create();
App.IndexRoute = Ember.Route.extend({
model: function(){
return this.store.find('games');
}
});
App.GamesModel = DS.Model.extend({
name: DS.attr('string')
});
App.GamesAdapter = DS.RESTAdapter.extend({
host: 'http://private-0f6a1-ember37.apiary-mock.com'
});
App.GamesSerializer = DS.RESTSerializer.extend({
normalizePayload: function(payload){
var result = { games: payload };
return result;
}
});
And this is the template:
<script type="text/x-handlebars" data-template-name="index">
<ul>
{{#each model}}
<li>{{name}}</li>
{{/each}}
</ul>
</script>
Here is the link for the code on CodePen: http://codepen.io/tarmann/pen/GJMJxq
I also tried different versions of Ember Data and Ember but with no luck.
Your problem is pluralization: you specify GamesModel instead of GameModel, you find games instead of game etc. I changed all these occurences to be aligned with what Ember expects(and you can read more about Ember standards in guides) and it works, CodePen:
App.IndexRoute = Ember.Route.extend({
model: function(){
return this.store.find('game');
}
});
App.GameModel = DS.Model.extend({
name: DS.attr('string')
});
App.GameAdapter = DS.RESTAdapter.extend({
host: 'http://private-0f6a1-ember37.apiary-mock.com'
});
// https://www.youtube.com/watch?v=HL2bMjndviE
App.GameSerializer = DS.RESTSerializer.extend({
normalizePayload: function(payload){
var result = { games: payload };
return result;
}
});
App.IndexController = Ember.Controller.extend({
init: function(){
this.store.push('game', {
id: 100,
name: "Added from controller"
});
}
});
Screenshot with results:
I have this bb app that I'm trying to search and return the results of the search, then when cleared, so all results again. I was able to get everything to show before adding the search feature, but now nothing showing up. I think the collection isn't available at the time it's trying to populate, but can't seem to get it to wait. I've tried moving the fetch around to no avail. Any help would be greatly appreciate. For the sake of ease, I've put everything in a fiddle that can be found here...
//Campaign Model w defaults
app.model.Campaign = Backbone.Model.extend({
default: {
title: '',
img: '',
id: '',
slug: '',
image_types: 'small',
tagline: ''
}
});
//Campaign Collection from model
app.collection.Campaign = Backbone.Collection.extend({
//our URL we're fetching from
url: 'https://api.indiegogo.com/1/campaigns.json?api_token=e377270bf1e9121da34cb6dff0e8af52a03296766a8e955c19f62f593651b346',
parse: function(response) {
console.log('parsing...');
return response.response; //get array from obj to add to collection based on model
},
currentStatus: function(status){
return _(this.filter(function(data){
console.log('currentStats', status);
return data.get('_pending') == status;
}));
},
search: function(searchVal) {
console.log('search...');
if (searchVal == '') {
return this;
}
var pattern = new RegExp(searchVal, 'gi');
return _(this.filter(function(data) {
return pattern.test(data.get('title'));
}));
}
});
app.collection.campaigns = new app.collection.Campaign();
app.collection.campaigns.fetch({
success: function(){
console.log('Success...');
var sHeight = window.screen.availHeight - 200 + 'px';
$('#container ul').css('height', sHeight);
},
error: function() {
console.log('error ',arguments);
}
});
//List view for all the campaigns
app.view.CampaignList = Backbone.View.extend({
events: {
'keyup #searchBox': 'search'
},
render: function(data) {
console.log('campaignList',$(this.el).html(this.template));
$(this.el).html(this.template);
return this;
},
renderAll: function(campaigns) {
console.log('campList renderAll', campaigns, $('#campaignList'));
$('#campaignList').html('');
campaigns.each(function(campaign){
var view = new app.view.CampaignItem({
model: campaign,
collection: this.collection
});
console.log(view);
$('#campaignList').append(view.render().el);
});
return this;
},
initialize: function() {
console.log('init campList',app);
this.template = _.template($('#campaignList-tmp').html());
this.collection.bind('reset', this.render, this);
},
search: function(e) {
console.log('listView search');
var searchVal = $('#searchBox').val();
this.renderAll(this.collection.search(searchVal));
},
sorts: function() {
var status = $('#campSorting').find('option:selected').val();
if(status == '') {
status = false;
};
this.renderAll(this.collection.currentStatus(status));
}
});
//Item view for single campaign
app.view.CampaignItem = Backbone.View.extend({
events: {},
render: function(data){
console.log('campItem render...', data);
this.$el.html(this.template(this.model.toJSON()));
return this;
},
initialize: function(){
console.log('campItem init');
this.template = _.template( $('#campaignItem-tmp').html());
}
});
//Router
app.router.Campaign = Backbone.Router.extend({
routes: {
'': 'campaigns'
},
campaigns: function(){
this.campListView = new app.view.CampaignList({
collection: app.collection.campaigns
});
$('#container').append(this.campListView.render().el);
this.campListView.sorts();
}
});
app.router.campaigns = new app.router.Campaign();
Backbone.history.start();
http://jsfiddle.net/skipzero/xqvrpyx8/
I've gone over the other posts on the same topic and perhaps I'm missing something in my own code but it seems to me things should be working. I've never worked with localStorage and backbone and seem to be missing something here. Any thoughts are greatly appreciated!
my instances:
var Address = {
run: function() {
this.router = new AddressRouter();
this.contactscollection = new AddressCollection();
this.addContact = new AddressAddView();
this.listView = new AddressListView();
Backbone.history.start();
}
};
my collection:
var AddressCollection = Backbone.Collection.extend({
model: AddressModel,
localstorage: new Store('backbone-addressbook')
});
my model:
var AddressModel = Backbone.Model.extend({
defaults: {
id: '',
name: '',
email: ''
}
});
and my view:
var AddressAddView = Backbone.View.extend({
el: '#content',
template: _.template($('#addContactTemplate').html()),
events: { 'submit form#addContactForm': 'createContact'},
createContact: function(){
Address.contactscollection.create(this.newAttributes());
this.save();
this.input.val('');
},
newAttributes: function() {
return {
id: $('#id').val(),
name: $('#name').val(),
email: $('#email').val()
}
},
initialize: function() {
_.bindAll(this, 'addContactPage','render');
},
addContactPage: function(id) {
var contact = {},
model = Address.contactscollection.get(id);
if (id !== undefined && model !== undefined) {
contact = model.toJSON();
}
this.$el.html(this.template({contact: contact}));
}
});
Case matters.
localstorage: new Store('backbone-addressbook')
needs to be
localStorage: new Store('backbone-addressbook')
If localStorage isn't set, your collection is assumed to persist to a RESTful API, and a url is required.
App.BaseRoute = App.AuthenticatedRoute.extend({
setupController: function(controller, model){
var cuser = this.controllerFor('application').get('currentUser');
controller.set('model',model);
this.controllerFor('base').set("currTags",App.User.getUserTags(cuser._id.$oid));
}
});
App.IndexRoute = App.BaseRoute.extend({
model: function(params) {
return {"adsf" :"blah"};
},
setupController: function(controller, model){
this._super(controller,model);
this.controllerFor('posts').set('model',model);
}
});
App.User.reopenClass({
getUserTags: function(userid) {
var currTags = []; //create an empty object
currTags.set('isLoaded',false);
console.log(currTags);
$.ajax({
type: "GET",
url : "/user/"+userid+"/default_tags",
dataType: "application/json",
//contentType : "application/json"
}).then(this,function(data){
data = JSON.parse(data.responseText);
data.models.tags.forEach(function(tag){
var model = App.Tag.create(tag);
model.value = model.name;
currTags.addObject(model); //fill your array step by step
});
console.log(currTags);
currTags.set("isLoaded", true);
});
console.log(currTags);
return currTags;
}
});
App.IndexController = Ember.ArrayController.extend({
needs: ['application','base' ,'posts'],
currentUser: Ember.computed.alias("controllers.application.currentUser"),
posts : Ember.computed.alias("controllers.posts"),
currTags: Ember.computed.alias("controllers.base.currTags"),
actions: {
initCurrentTags: function() {
}.observes('currTags.isLoaded')
}
});
I update currTags but initCurrentTags is never being hit.
computed properties, and observes shouldn't live in the actions hash.
Incorrect
App.IndexController = Ember.ArrayController.extend({
needs: ['application','base' ,'posts'],
currentUser: Ember.computed.alias("controllers.application.currentUser"),
posts : Ember.computed.alias("controllers.posts"),
currTags: Ember.computed.alias("controllers.base.currTags"),
actions: {
initCurrentTags: function() {
}.observes('currTags.isLoaded')
}
});
Correct
App.IndexController = Ember.ArrayController.extend({
needs: ['application','base' ,'posts'],
currentUser: Ember.computed.alias("controllers.application.currentUser"),
posts : Ember.computed.alias("controllers.posts"),
currTags: Ember.computed.alias("controllers.base.currTags"),
initCurrentTags: function() {
}.observes('currTags.isLoaded')
actions: {
}
});