Ember.js observer not firing after asynchronous update - javascript

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: {
}
});

Related

Backbone.Paginator infinite mode, with Marionette

In my Marionette app, I have a Collection view, with a childView for it's models.
The collection assigned to the CollectionView is a PageableCollection from Backbone.paginator. The mode is set to infinite.
When requesting the next page like so getNextPage(), the collection is fetching data and assigning the response to the collection, overwriting the old entries, though the full version is store in collection.fullCollection. This is where I can find all entries that the CollectionView needs to render.
Marionette is being smart about collection events and will render a new childView with it's new model when a model is being added to the collection. It will also remove a childView when it's model was removed.
However, that's not quite what I want to do in this case since the collection doesn't represent my desired rendered list, collection.fullCollection is what I want to show on page.
Is there a way for my Marionette view to consider collection.fullCollection instead of collection, or is there a more appropriate pagination framework for Marionette?
Here's a fiddle with the code
For those who don't like fiddle:
App = Mn.Application.extend({});
// APP
App = new App({
start: function() {
App.routr = new App.Routr();
Backbone.history.start();
}
});
// REGION
App.Rm = new Mn.RegionManager({
regions: {
main: 'main',
buttonRegion: '.button-region'
}
});
// MODEL
App.Model = {};
App.Model.GeneralModel = Backbone.Model.extend({});
// COLLECTION
App.Collection = {};
App.Collection.All = Backbone.PageableCollection.extend({
model: App.Model.GeneralModel,
getOpts: function() {
return {
type: 'POST',
contentType: 'appplication/json',
dataType: 'json',
data: {skip: 12},
add: true
}
},
initialize: function() {
this.listenTo(Backbone.Events, 'load', (function() {
console.log('Load more entries');
// {remove: false} doesn't seem to affect the collection with Marionette
this.getNextPage();
})).bind(this)
},
mode: "infinite",
url: "https://api.github.com/repos/jashkenas/backbone/issues?state=closed",
state: {
pageSize: 5,
firstPage: 1
},
queryParams: {
page: null,
per_page: null,
totalPages: null,
totalRecords: null,
sortKey: null,
order: null
},
/*
// Enabling this will mean parseLinks don't run.
sync: function(method, model, options) {
console.log('sync');
options.contentType = 'application/json'
options.dataType = 'json'
Backbone.sync(method, model, options);
}
*/
});
// VIEWS
App.View = {};
App.View.MyItemView = Mn.ItemView.extend({
template: '#item-view'
});
App.View.Button = Mn.ItemView.extend({
template: '#button',
events: {
'click .btn': 'loadMore'
},
loadMore: function() {
Backbone.Events.trigger('load');
}
});
App.View.MyColView = Mn.CollectionView.extend({
initialize: function() {
this.listenTo(this.collection.fullCollection, "add", this.newContent);
this.collection.getFirstPage();
},
newContent: function(model, col, req) {
console.log('FullCollection length:', this.collection.fullCollection.length, 'Collection length', this.collection.length)
},
childView: App.View.MyItemView
});
// CONTROLLER
App.Ctrl = {
index: function() {
var col = new App.Collection.All();
var btn = new App.View.Button();
var colView = new App.View.MyColView({
collection: col
});
App.Rm.get('main').show(colView);
App.Rm.get('buttonRegion').show(btn);
}
};
// ROUTER
App.Routr = Mn.AppRouter.extend({
controller: App.Ctrl,
appRoutes: {
'*path': 'index'
}
});
App.start();
You could base the CollectionView off the full collection, and pass in the paged collection as a separate option:
App.View.MyColView = Mn.CollectionView.extend({
initialize: function(options) {
this.pagedCollection = options.pagedCollection;
this.pagedCollection.getFirstPage();
this.listenTo(this.collection, "add", this.newContent);
},
// ...
}
// Create the view
var colView = new App.View.MyColView({
collection: col.fullCollection,
pagedCollection: col
});
Forked fiddle

How render model which I get as AJAX data

Here are my code:
Model:
var app = app || {};
app.Film = Backbone.Model.extend({
defaults: {
poster: 'http://placehold.it/320x150',
title: 'No name',
genre: 'Unknown',
runtime: 'Unknown',
imdbRating: 0
},
parse: function( response ) {
response.id = response._id;
return response;
}
});
Collection:
var app = app || {};
app.Films = Backbone.Collection.extend({
model: app.Film,
url: '/api/films'
});
Views:
Model:
var app = app || {};
app.FilmView = Backbone.View.extend({
tagName: 'div',
className: 'filmContainer',
events: {
},
initialize : function() {
this.template= _.template( $('#filmTemplate').html() );
},
render: function() {
this.$el.html( this.template( this.model.toJSON() ));
return this;
}
});
Collection:
ar app = app || {};
var global = 'global';
app.FilmsView = Backbone.View.extend({
el: '#films',
events:{
},
initialize: function() {
this.collection = new app.Films();
this.collection.fetch();
this.render();
this.listenTo( this.collection, 'add', this.renderFilm );
this.listenTo( this.collection, 'reset', this.render );
this.listenTo( Backbone.Events, 'findFilm', this.findFilm );
},
render: function() {
this.collection.each(function( item ) {
this.renderFilm( item );
}, this );
console.log('render');
},
renderFilm: function( item ) {
var filmView = new app.FilmView({
model: item
})
this.$el.append( filmView.render().el );
console.log('renderFilm');
},
findFilm: function () {
console.log('findFilm');
$.ajax({
type: "POST",
url: "/film/find",
data: {
name: "Gotham"
},
success: function(data){
app.FilmsView.collection = new app.Films();
app.FilmsView.collection.add (data[0]);
console.log(app.FilmsView.collection)
}
});
}
});
Idea of my problem is - I must get string from field and use it for searching of the film in DB. "/film/find" request response me with object which include fields of the model. I need some way to display this model in page. As I use fetch() method when initialize: page display all DB models. So I need clear collection and display only 1 model which I get from the server. this.collecion.reset() don't work and don't trigger render() event.
Indetesting think: app.FilmsView.collection returnt "undefined" after rendering, so I need to create new collection for AJAX response.
Try to change this string in itnitialize():
this.listenTo( Backbone.Events, 'findFilm', this.findFilm );
to
this.listenTo( Backbone.Events, 'findFilm', this.findFilm, this );
And then in findFilm:
success: function(data){
this.collection.reset();
this.collection.add (data[0]);
}.bind(this);

Issues searching backbone collection

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/

Ember: Model as content for CollectionView

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>

Backbone Fetching Process

I have Backbone Model that collect data from server:
Job.Models.Response = Backbone.Model.extend({
defaults: {
'authStatus': false,
'id': '1',
'name': 'name',
},
urlRoot: '/static/js/public/json/'
});
I have button with data-id = "id from /static/js/public/json/".
Job.Views.Response = Backbone.View.extend({
el: '.ra-response-button',
events: {
"click": "load"
},
load: function () {
var info = this.$el.data();
this.model.set({ id: info.id});
this.model.fetch();
if (this.model.attributes.authStatus === false) {
console.log('Register')
}
else {
console.log('Unregister')
}
}
});
If i console.log my model after fetch, its dont update, but data fetch success.
What kind of problem can be here?
Here i init our plugin:
var responseModel = new Job.Models.Response;
var response = new Job.Views.Response({ model: responseModel });
I resolve my problem. Finally View.
Job.Views.Response = Backbone.View.extend({
el: '.ra-response-button',
events: {
"click": "load"
},
load: function () {
var that = this;
var info = that.$el.data();
that.model.set({ id: info.id});
that.model.fetch({
success: function() {
if (that.model.attributes.authStatus === true) {
new Job.Views.ResponseForm({ model: that.model })
}
else {
new Job.Views.ResponseAuth({ model : that.model })
}
},
error: function() {
alert('Error, repeat please.')
}
});
}
});

Categories

Resources