Fiddle:
http://jsfiddle.net/lifeinafolder/mpcRr/
Essentially, I want to hide the current 'visible' item and make the next one 'visible' but toggleProperty doesn't seem to be working on the childView object. It just silently fails and throws no errors as well.
A CollectionView will show all items in the collection, which is not what you want. I would implement a standard view that houses the collection and displays the next button and a slide view within it that displays the selected slide when the container view sets the selected slide as content on the slide view.
A quite ugly solution, almost working. I kept the way of switching views.
The template is the same, the js looks like this:
App = Ember.Application.create();
App.slides = Ember.ArrayProxy.create({
content:[]
});
App.slidesCollectionView = Ember.CollectionView.extend({
contentBinding:"App.slides",
tagName:'div',
emptyView: Ember.View.extend({
template: Ember.Handlebars.compile("<div class=\"placeholder\">placeholder</div>")
}),
itemViewClass:"App.slideView",
classNames:["slideshow"],
click:function(){
var t = Ember.ArrayProxy.create({ content: this.get('childViews') });
var selected = t.findProperty('isVisible', true);
if(selected){
var nextSlide = t.objectAt(selected.get('contentIndex') + 1);
selected.set('isVisible', false);
if(nextSlide){
nextSlide.set('isVisible', true);
}else{
t.get('firstObject').set('isVisible', true);
}
}else{
t.get('firstObject').set('isVisible', true);
}
}
});
App.slideView = Ember.View.extend({
templateName: 'slide-item',
tagName:'div',
isVisible: false,
classNames:['slide'],
classNameBindings:['isVisible:selected']
});
setTimeout(function(){
App.slides.pushObjects([
Ember.Object.create({val:'a',index:0}),
Ember.Object.create({val:'b',index:1}),
Ember.Object.create({val:'c',index:2}),
Ember.Object.create({val:'d',index:3}),
Ember.Object.create({val:'e',index:4}),
Ember.Object.create({val:'f',index:5})
])},2000);
the fiddle:
http://jsfiddle.net/Sly7/dd6at/
Related
I'm trying add a delete button with an ember action from a controller. For some reason Ember.Handlebars.compile('<button {{action "deletePerson"}}>Delete</button> returns a function and not the compiled string.
Here's a jsbin
Here's the relevant portion of code:
App.ApplicationController = Ember.Controller.extend({
columns: function() {
...
buttonColumn = Ember.Table.ColumnDefinition.create({
columnWidth: 100,
headerCellName: 'Action',
getCellContent: function(row) {
var button = Ember.Handlebars.compile('<button {{action "deletePerson" this}}>Delete</button>');
return button; // returns 'function (context, options) { ...'
}
});
...
}.property()
...
After looking through the link from #fanta (http://addepar.github.io/#/ember-table/editable) and a lot of trial and error, I got it working.
Here's the working jsbin.
Here are some key points:
Instead of using getCellContent or contentPath in the ColumnDefinition, you need to use tableCellViewClass and to create a view that will handle your cell
Pass in this to the action on your button — and modify content off that. One gotcha is to edit content, you need to copy it using Ember.copy
Here's the relevant code:
App.ApplicationController = Ember.Controller.extend({
columns: function() {
...
buttonColumn = Ember.Table.ColumnDefinition.create({
columnWidth: 100,
headerCellName: 'Action',
tableCellViewClass: 'App.PersonActionCell'
});
...
}.property(),
onContentDidChange: function(){
alert('content changed!');
}.observes('content.#each'),
...
});
App.PersonActionCell = Ember.Table.TableCell.extend({
template: Ember.Handlebars.compile('<button {{action "deletePerson" this target="view"}}>Delete</button>'),
actions: {
deletePerson: function(controller){
// Will NOT work without Ember.copy
var people = Ember.copy(controller.get('content'));
var row = this.get('row');
// For some reason people.indexOf(row) always returned -1
var idx = row.get('target').indexOf(row);
people.splice(idx, 1);
controller.set('content', people);
}
}
});
I have a answers list like below:
each list item is a backbone model.
{
title: 'answer title...',
content: 'answer content...',
voteStatus: 'up'
}
When I click up-vote or down-vote, The model's voteStatus will be change, and this answer item be re-render.
If there have a picture in answer's content, the picture will be re-render too, But this is not what I want.
How could I just re-render the vote button when I just change voteStatus?
Have a subview inside your AnswerView that is only responsible for rendering the voting arrows, VotingArrowsView. You would initialize this subview inside the initialize function of AnswerView and then prepend the subview's el to the parent view's el when rendering the parent view:
var AnswerView = Backbone.View.extend({
initialize: function(options){
this.template = _.template($('#answerTmpl').html());
this.votingArrowsView = new VotingArrowsView({ model: this.model });
...
},
render: function(){
this.$el.html(this.template(this.model.toJSON()));
this.$el.prepend(this.votingArrowsView.render().el);
return this;
},
...
});
var VotingArrowsView = Backbone.View.extend({
initialize: function(options){
this.template = _.template($('#votingArrowsTmpl').html());
this.listenTo(this.model, 'change:voteStatus', this.render);
},
render: function(){
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
I am trying to create autocomplete behavior with emberjs. I am observing in the controller an input field (searchCity) and making ajax calls according to that field. I have a view which handles additional logic, and should display a list of clickable cities as suggestions. So far I have the following code:
var labelView = Ember.View.extend({
templateName: 'searchedCities',
geoData: null,
click:function(){
debugger;
}
}),
ModalView = Ember.View.extend({
layoutName: 'modal_layout',
cities: null,
didInsertElement: function() {
this.cities = Ember.CollectionView.create({
tagName: 'span',
itemViewClass: labelView
});
},
cityList: function(){
var callback,
cities = this.get('cities'),
searchCity = this.get('controller').get('searchCity'),
regExp = new RegExp(searchCity, 'i');
if (!searchCity){
return;
}
callback = function(data){
var aux = data.filter(function(geoObjects){
return geoObjects.city.match(regExp);
}).slice(0,9);
cities.clear();
aux.forEach(function(geoData){
cities.pushObject(labelView.create({geoData:geoData}));
});
};
Store.resolveGeoData(callback);
}.property('controller.searchCity')
});
In my template I have the following code:
{{view Ember.TextField valueBinding=searchCity placeholder="City"}}
{{#each view.cities}}
{{this.geoData.city}}
{{/each}}
{{#each view.cityList}}
{{this.city}}
{{/each}}
Even though in the callback I can check that the cities are populated with the views in my template nothing is shown if i remove the {{#each view.cityList}}{{/each}}, but it displays if I leave it there.
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,
I have a statemachine and I am using the new currentViewBinding to swap out parts of an overall containerView whenever a new state is entered using currentViewBinding:
index: Ember.State.create({
enter: function(manager) {
App.get('appController').set('feedView', Ember.View.create({
templateName: 'dashboard_feed',
contentBinding: 'App.feedController.content',
controller: App.get('App.feedController')
}));
}
})
At this moment in time, the rendering of these view is quite slow. Is there a way I could keep the view in memory and avoid the re-rendering every time I enter the state?
I actually provided a solution to this for another question on StackOverflow, but it's super relevant here too. Avoiding re-rendering of a flash object from scratch when view is reactivated
Here's the jsFiddle: http://jsfiddle.net/EE3B8/1
I extend ContainerView with a flag to stop it from destroying the currentView upon it's destruction. You'll want to stash the view instance somewhere that it won't be destroyed.
App.ImmortalContainerView = Ember.ContainerView.extend({
destroyCurrentView: true,
willDestroy: function() {
if (!this.destroyCurrentView) { this._currentViewWillChange(); }
this._super();
}
});
App.immortalView = Ember.View.create({
template: Ember.Handlebars.compile(
'I WILL LIVE FOREVER!'
)
});
You could extend Ember.ContainerView to show/hide its currentView view like so:
App.FastContainerView = Ember.ContainerView.extend({
toggleCurrentViewFast: true,
_currentViewWillChange: function() {
var childViews = this.get("childViews"),
currentView = this.get("currentView");
if (this.toggleCurrentViewFast && childViews.indexOf(currentView) >= 0) {
currentView.set("isVisible", false);
} else {
this._super();
}
},
_currentViewDidChange: function() {
var childViews = this.get("childViews"),
currentView = this.get("currentView");
if (this.toggleCurrentViewFast && childViews.indexOf(currentView) >= 0) {
currentView.set("isVisible", true);
} else {
this._super();
}
}
});