Run jquery at the end of Ember.CollectionView rendering - ember.js

I have a ContainerView which contains a CollectionView. After this CollectionView renders on the screen I need to perform a jquery function which essentially looks through the content of the rendered template and performs some display modifications.
If I perform that jquery within the didInsertElement of CollectionView it works but it gets executed for every single element in the CollectionView where as I really just need it to be done once at the end. How do I specify that?
http://jsfiddle.net/JFqNr/ (note doesn't render on jsfiddle or some reason but just to show you structure)
App = Ember.Application.create();
App.FooContainerView = Ember.ContainerView.extend({
childViews: ['elementList'],
elementList: Ember.CollectionView.extend({
content: function() {
return [
{ Title: "Dashboard", ID: "dashboard" },
{ Title: "Invoices", ID: "invoices" },
{ Title: "Expenses", ID: "expenses" },
{ Title: "People", ID: "people" },
{ Title: "Reports", ID: "reports" },
{ Title: "Settings", ID: "settings" }
];
}.property(),
template: Ember.Handlebars.compile( '{{view.content.title}}' ),
didInsertElement: function() {
// perform jquery function
}
}),
didInsertElement: function() {
// does not work if perforemed here
}
});
App.initialize();
​

The functionality to do this has only very recently been added to the master branch, so you will need to be compile your own version of Ember.
You can now schedule into an afterRender queue to run after all the individual views have been rendered.
App.FooContainerView = Ember.ContainerView.extend({
// Existing code
didInsertElement: function() {
Ember.run.scheduleOnce('afterRender', this, function(){
// perform jQuery function here;
});
}
See https://github.com/emberjs/ember.js/pull/1528 for code details.

Related

carousel for each product(route) ember js

Hi I have a categories route and then product route. Each product has many images.
I want to show this images in carousel. the first product i click i get carousel with images but when i click on second product no carousel is displayed
MyApp.ShowCarouselComponent = Ember.Component.extend({
content: [],
templateName: 'show-carousel',
classNames: ['carousel', 'slide'],
init: function () {
this._super.apply(this, arguments);
// disable the data api from boostrap
$(document).off('.carousel.data-api');
// at least one item must have the active class, so we set the first here, and the class will be added by class binding
//var cdata = this.get('controller').get('carouselData');
var obj = this.get('content').get('firstObject');
Ember.set(obj, 'isActive', true);
console.log('this is what obj is ');
console.log(obj);
},
previousSlide: function () {
this.$().carousel('prev');
},
nextSlide: function () {
this.$().carousel('next');
},
didInsertElement: function () {
this.$().carousel();
},
willDestroyElement: function () {
this.$('.carousel').remove();
this._super();
},
indicatorsView: Ember.CollectionView.extend({
tagName: 'ol',
classNames: ['carousel-indicators'],
contentBinding: 'parentView.content',
itemViewClass: Ember.View.extend({
click: function () {
var $elem = this.get("parentView.parentView").$();
$elem.carousel(this.get("contentIndex"));
},
template: Ember.Handlebars.compile(''),
classNameBindings: ['content.isActive:active']
})
}),
itemsView: Ember.CollectionView.extend({
classNames: ['carousel-inner'],
contentBinding: 'parentView.content',
itemViewClass: Ember.View.extend({
classNames: ['item'],
classNameBindings: ['content.isActive:active'],
template: Ember.Handlebars.compile('\
{{view.content}}\
<img {{bind-attr src="view.content.product_url"}} alt="dfdds"/>\
<div class="carousel-caption">\
<h4>{{view.content}}</h4>\
<p>{{view.content.image_description}}</p>\
</div>')
})
})
});
show-carousel component
{{view view.indicatorsView}}
{{view view.itemsView}}
<a class="left carousel-control" {{action previousSlide target="view"}}>‹</a>
<a class="right carousel-control" {{action nextSlide target="view"}}>›</a>
router.js
this.resource('categories', {
path: '/'
}, function () {
this.resource('category', {
path: '/:category_id'
}, function () {
this.resource('product', {
path: '/:product_id'
});
});
});
In case of Ember views and Ember Components case accessing dom in init() method is a bad idea because it might happen that element that you are trying to access is not yet inserted into dom. so putting code from init method into didInsertElement() might solve your problem.

Collection is empty when a route calls it, but if I go away and come back, the collection holds what it is supposed to

The code works every-time EXCEPT for when the router is first called with page load. The, the collection is created, but it's not populated with the notes.fetch();. I've been looking all over, and I can't see why this is happening.
For example, when I go to the URL to load this page, everything but what is in the collection loads. When I go to #blank/' and then go "Back" to thelist` view, the collection AND the models load as they are supposed to. SO how do I get the collection to load once the page loads?
Here's the code:
$(function() {
// Note: The model and collection are extended from TastypieModel and TastypieCollection
// to handle the parsing and URLs
window.Note = TastypieModel.extend({});
window.Notes = TastypieCollection.extend({
model: Note,
url: NOTES_API_URL
});
window.notes = new Notes();
window.NoteView = Backbone.View.extend({
className: "panel panel-default note",
template: _.template($('#notes-item').html()),
events: {
'click .note .edit': 'editNoteToggle',
'click .note .edit-note': 'doEdit'
},
initialize: function () {
_.bindAll(this, 'render');
this.model.bind('change', this.render);
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
editNoteToggle: function() {
console.log("I've toggled the edit box");
},
doEdit: function() {
console.log('This will edit the note');
}
});
window.NoteListView = Backbone.View.extend({
el: "#app",
template: _.template($('#notes-item-list').html()),
events: {
'click #add-note': 'addNote'
},
initialize: function () {
_.bindAll(this, 'render', 'addNote');
this.collection.bind('change', this.render);
},
render: function() {
this.$el.html(this.template());
console.log('before the loop');
console.log(this.collection.toJSON()); //<- Empty when it first loads, then contains the models when I visit another page and come "back" to this page
this.collection.each(function(note){
var view = new NoteView({ model: note });
$('#notes-list').append(view.render().el);
});
console.log('done');
$('#add-note-toggle').on('click', this, function() {
$('#note-add-form').toggle();
});
return this;
},
addNote: function() {
console.log('The add note was clicked');
}
});
window.NotesRouter = Backbone.Router.extend({
routes: {
'': 'list',
'blank/': 'blank'
},
initialize: function() {
// starts by assigning the collection to a variable so that it can load the collection
this.notesView = new NoteListView({
collection: notes
});
notes.fetch();
},
list: function () {
$('#app').empty();
$('#app').append(this.notesView.render().el);
},
blank: function() {
$('#app').empty();
$('#app').text('Another view');
}
});
window.notesRouter = new NotesRouter();
Backbone.history.start();
})
It looks like your listening to the wrong collection event. Try using
window.NoteListView = Backbone.View.extend({
// ...
initialize: function () {
this.collection.bind('reset', this.render);
}
// ...
});
window.NotesRouter = Backbone.Router.extend({
// ...
initialize: function() {
notes.fetch({reset: true});
}
// ...
});
Right now you are firing off an async collection.fetch(), which will eventually load the data. Problem is, once that data is loaded into the collection, it won't fire a change event, just a bunch of adds. When you go to the blank page and back, you're data has arrived by the second render call and displays properly.
If you modify your fetch() to throw a reset event and then listen for that, you'll have a freshly rendered NoteList once the data comes in without the need to go to the blank page.

Ember.js bind class change on click

How do i change an elements class on click via ember.js, AKA:
<div class="row" {{bindAttr class="isEnabled:enabled:disabled"}}>
View:
SearchDropdown.SearchResultV = Ember.View.extend(Ember.Metamorph, {
isEnabled: false,
click: function(){
window.alert(true);
this.isEnabled = true;
}
});
The click event works as window alert happens, I just cant get the binding to.
The class is bound correctly, but the isEnabled property should be modified only with a .set call such as this.set('isEnabled', true) and accessed only with this.get('isEnabled'). This is an Ember convention in support of first-class bindings and computed properties.
In your view you will bind to a className. I have the following view in my app:
EurekaJ.TabItemView = Ember.View.extend(Ember.TargetActionSupport, {
content: null,
tagName: 'li',
classNameBindings: "isSelected",
isSelected: function() {
return this.get('controller').get('selectedTab').get('tabId') == this.get('tab').get('tabId');
}.property('controller.selectedTab'),
click: function() {
this.get('controller').set('selectedTab', this.get('tab'));
if (this.get('tab').get('tabState')) {
EurekaJ.router.transitionTo(this.get('tab').get('tabState'));
}
},
template: Ember.Handlebars.compile('<div class="featureTabTop"></div>{{tab.tabName}}')
});
Here, you have bound your className to whatever the "isSelected" property returns. This is only true if the views' controller's selected tab ID is the same as this views' tab ID.
The code will append a CSS class name of "is-selected" when the view is selected.
If you want to see the code in context, the code is on GitHub: https://github.com/joachimhs/EurekaJ/blob/netty-ember/EurekaJ.View/src/main/webapp/js/app/views.js#L100
Good answers, however I went down a different route:
SearchDropdown.SearchResultV = Ember.View.extend(Ember.Metamorph, {
classNameBindings: ['isSelected'],
click: function(){
var content = this.get('content');
SearchDropdown.SelectedSearchController.set('content', content);
var loadcontent = this.get('content');
loadcontent.set("searchRadius", $("select[name=radius]").val());
SearchDropdown.LoadMap.load(content);
},
isSelected: function () {
var selectedItem = SearchDropdown.SelectedSearchController.get('content'),
content = this.get('content');
if (content === selectedItem) {
return true;
}
}.property('SearchDropdown.SelectedSearchController.content')
});
Controller:
SearchDropdown.SelectedSearchController = Ember.Object.create({
content: null,
});
Basically stores the data of the selected view in a controller,

How to manipulate a list item from it's view in emberjs

I have an Ember application with both a view and a controller:
http://jsfiddle.net/gavriguy/EDr4G/
I want to mark the current item the user clicks as read - by changing it's related model.
I'm currently able to do that by figuring the item's index of the view - but the problem is that i can't be sure that the index on the view is the same as the index on its controller.
Any thoughts?
JavaScript:
App.tempController = Em.ArrayController.create({
content: [
{
title: 'A',
unread: true},
{
title: 'B',
unread: true},
{
title: 'C',
unread: false}
]
});
App.someItemsView = Ember.CollectionView.create({
contentBinding: 'App.tempController.content',
itemViewClass: Ember.View.extend({
template: Ember.Handlebars.compile('<div>{{content.title}} unread: {{content.unread}}</div>'),
click: function(event) {
//How to mark current clicked item as read?
console.log(this.content);
console.log(event);
this.set('content.unread', false);
}
})
});​
Inside your click handler you can get the reference to the array item for which the view is rendered via this.get('content'). So you can set the flag via this.setPath('content.unread', false), see http://jsfiddle.net/pangratz666/t6Nst/:
itemViewClass: Ember.View.extend({
template: Ember.Handlebars.compile('<div>{{content.title}} unread: {{content.unread}}</div>'),
click: function(event) {
// this.content is the item in the array on which this click event occures.
this.setPath('content.unread', false);
}
})

List pushed in navigation view is not rendered

I am using sencha touch 2.
My App.js file (summed up)
Ext.application({
launch: function() {
// ...
var list = Ext.create('Ext.List', {
itemTpl : '<img src="{icon}"/>{title}<br/>{description}',
store: store,
listeners: {
select: function(view, record) {
var customView = Ext.create(record.get('view'));
navView.push(customView);
view.deselectAll();
}
}
});
//----------------------------------------------------------------------
var navView = Ext.create('Ext.NavigationView', {
navigationBar:{
items: [{
text:'refresh',
align: 'right'
}]
},
items: [list]
});
//----------------------------------------------------------------------
Ext.Viewport.add(navView);
}
});
When i am loading a view within my navigation view, everything is ok appart when it conains a list.
There is a view with a list in it.
The subpanel is rendered, but not the list view (the list view has been tested and is of course rendering in a different context)
Ext.define('ts.views.jobs', {
extend: 'Ext.Panel',
layout:'fit',
config:{
title:'Jobs'
},
initialize: function() {
this.callParent();
var jobsStore = Ext.create('Ext.data.Store', {
model: 'ts.model.job',
data: [{
key2: 'key1'
}, {
key2:'key2'
},
{
key2:'key3'
}
]
});
var jobsList = Ext.create('Ext.List', {
xtype: 'jobsList',
ui: 'round',
itemTpl : 'ok{key}',
store: jobsStore
});
var panel = Ext.create('Ext.Panel', {
html: 'Testing'
});
this.add([jobsList,panel]);
}
});
What am i doing wrong ?
* is it a navigationview bug ?
* am i not initializing properly in my subview ?
Thx for your help.
This was cross posted to the sencha touch forums: http://www.sencha.com/forum/showthread.php?184492-List-pushed-in-navigation-view-is-not-rendered and the answer accepted was:
layout config needs to be within the config object.