I have a view with disabled selector that should be set to the value from ember-data as soon as the model is loaded.
LocationSelectView: Ember.Select.extend({
prompt: "Choose location",
contentBinding: 'controller.locations',
optionValuePath: 'content.id',
optionLabelPath: 'content.title',
valueBinding: 'controller.content.location_id'
})
and the view is disabled in template
{{view view.LocationSelectView disabled="true"}}
everything is working as expected as long as the locations were already loaded to the store,
if they are not loaded, the content is binding as expected (I can verify that by enabling the selector), but the selected value stays at the "prompt".
I worked around this by pre-loading the locations data in the controller's init, but I really don't like this solution.
how can I fix this ? is it a bug ?
This issue is mentioned here: https://github.com/emberjs/ember.js/issues/1333
My solution was to add an observer to the view like this:
preselect: function () {
var item = this.get('items.firstObject');
this.set('currentItem', item);
}.observes('items.#each')
That presumes using selectionBinding (which I prefer) instead of valueBinding
I developed a workaround for this problem. Look at the following code:
App.Select = Ember.Select.extend({
placeholder: '',
allowClear: false,
contentChanged: function() {
if (this.get('value') === undefined && this.get('_iv') != undefined) {
var v = this.get('_iv');
var o = this.get('content').findProperty('id', v);
if (o) {
this.set('value', v);
}
}
}.observes('content.#each'),
init: function() {
this._super();
this.set('_iv', this.get('value'));
},
...
I hope it helps
Related
I've written an array controller with pagination function.
When I switch to another pages for the first time, there's no problem.
But if I reviist the page I visited before, and observer for an attribute that is used is template is triggered.
(in this case, published)
When I remove {{#unless published}}...{{/unless}} from template, the observer isn't triggered anymore when I revisit the page where I've already visited.
I don't think I've done weird thing on my controllers....
(When pagination button is clicked, it simply changes controllers's page)
(I've written observer for title in model class to test whether this issue is limited to published property, and observer for title also behaves like observer for published. So this issue doesn't seem to limited to published property )
I'm using
Ember : 1.7.1+pre.f095a455
Ember Data : 1.0.0-beta.9
Handlebars : 1.3.0
jQuery : 1.11.1
and I tried beta and canary version of ember, but this issue remains same.
Here is my Route
MuteAdmin.IndexRoute = Ember.Route.extend({
model: function(params, transition, queryParams) {
var search = params.search || '';
var page = params.page || 1;
return this.store.find(this.get('articleModelClassName'), {
search: search,
page: page
});
},
setupController: function(controller, model) {
controller.set('model', model);
var will_paginate_meta = model.get("meta.will_paginate");
controller.set('totalPages', will_paginate_meta.total_pages);
controller.set('previousPage', will_paginate_meta.previous_page);
controller.set('nextPage', will_paginate_meta.next_page);
}
});
and here is my controller
MuteAdmin.IndexController = Ember.ArrayController.extend(MuteAdmin.Modelable, {
queryParams: ['page', 'search'],
page: 1,
totalPages: null,
pageChanged: function() {
this.store.find(this.get('articleModelClassName'), {
search: this.get('search'),
page: this.get('page')
}).then(function(model) {
this.set('model', model);
var will_paginate_meta = model.get("meta.will_paginate");
this.set('totalPages', will_paginate_meta.total_pages);
this.set('previousPage', will_paginate_meta.previous_page);
this.set('nextPage', will_paginate_meta.next_page);
}.bind(this));
}.observes('page'),
actions: {
doSearch: function() {
this.store.find(this.get('articleModelClassName'), {
search: this.get('search'),
page: 1
}).then(function(model) {
this.set('model', model);
var will_paginate_meta = model.get("meta.will_paginate");
this.set('totalPages', will_paginate_meta.total_pages);
this.set('previousPage', will_paginate_meta.previous_page);
this.set('nextPage', will_paginate_meta.next_page);
this.set('page', will_paginate_meta.current_page);
}.bind(this));
}
}
});
and here is my template
{{#each controller}}
<tr>
<td>{{link-to title "edit" this}} {{#unless published}}<small class="text-muted">비공개</small>{{/unless}}</td>
<td>{{author.name}}</td>
<td>{{category.title}}</td>
<td>시간 지정</td>
<td>{{viewCount}}</td>
</tr>
{{/each}}
and here is my model which has observers
MuteAdmin.Article = DS.Model.extend({
title: DS.attr( 'string' ),
body: DS.attr( 'string' ),
category: DS.belongsTo('category'),
author: DS.belongsTo('user'),
viewCount: DS.attr('number'),
published: DS.attr('boolean', { defaultValue: true }),
publishScheduled: DS.attr('boolean', { defaultValue: false }),
publishScheduleTime: DS.attr('date'),
publishedChanged: function() {
if (this.get('published') == true) {
this.set('publishScheduled', false);
}
console.log('published changed! ' + this.toString());
}.observes('published'),
});
Never mind, I know what it is. Your making a call to the server for the records that already exist. The results are merging into the pre-existing records in the store causing the model to invalidate and observer to fire.
http://emberjs.jsbin.com/OxIDiVU/1043/edit
I'm trying to add permissions to groups, and I have a drag and drop set up so that a user can pull the unselected permissions over to selected, or vice versa. Unselected permissions are computed via removing the selected permissions from all permissions. This code is all functioning properly. The first time a user brings up the page, only those permissions that are unselected appear in the unselected side, and the same for selected.
However, when the user chooses another group to look at, the selected side is correct, while the unselected side shows what was displayed for the last group. Here is the route and controller:
App.GroupsEditRoute = Ember.Route.extend({
setupController: function(controller, model) {
this._super(controller, model);
controller.set('allPermissions', this.store.find('permission'));
},
actions: {
'update': function(group){
var route = this;
group.save().then(function(){
route.transitionTo('groups');
});
},
'cancel': function(group){
group.rollback();
this.transitionTo('groups');
},
'delete': function(group){
group.destroyRecord();
this.transitionTo('groups');
}
}
});
App.GroupsEditController = Ember.ObjectController.extend({
unselectedPermissions: function() {
console.log('UNSELECTED');
var allPermissions=this.get('allPermissions');
var permissions=this.get('permissions');
var self=this;
allPermissions.then( function() {
permissions.then( function() {
var unselected=allPermissions.filter(function(permission) {
return !permissions.contains(permission);
});
unselected=Ember.ArrayProxy.createWithMixins(Ember.SortableMixin, {
sortProperties: ['name'],
content: unselected
});
self.set('unselectedPermissions',unselected);
});
});
}.property('model.unselectedPermissions'),
selectedPermissions: function() {
console.log('SELECTED');
return Ember.ArrayProxy.createWithMixins(Ember.SortableMixin, {
sortProperties: ['name'],
content: this.get('permissions')
});
}.property('model.selectedPermissions')
});
When I use unselectedPermissions in my view via {{#each}}, it only fires once. I never see UNSELECTED in my log after that. However, the SELECTED, which is used in the same fashion, fires every time. Of course, the data displayed on the page is not updated, either, unless I refresh.
The setupController is being called each time a page is displayed, as it should.
I'm not sure what I'm doing wrong.
Any ideas?
in general computed properties shouldn't be set. When you set them you destroy the computed property portion of the code. There are a couple of different ways to handle this, the easiest is using an observer instead of computed property and setting the property.
unselectedPermissionList: [],
unselectedWatcher: function() {
console.log('UNSELECTED');
var allPermissions=this.get('allPermissions');
var permissions=this.get('permissions');
var self=this;
allPermissions.then( function() {
permissions.then( function() {
var unselected=allPermissions.filter(function(permission) {
return !permissions.contains(permission);
});
unselected=Ember.ArrayProxy.createWithMixins(Ember.SortableMixin, {
sortProperties: ['name'],
content: unselected
});
self.set('unselectedPermissionList',unselected);
});
});
}.observes('selectedPermissions')
The other way is to return an array reference, then push objects into that array after the fact.
unselectedWatcher: function() {
console.log('UNSELECTED');
var allPermissions=this.get('allPermissions'),
permissions=this.get('permissions'),
self=this,
ret = [];
allPermissions.then( function() {
permissions.then( function() {
var unselected=allPermissions.filter(function(permission) {
return !permissions.contains(permission);
});
unselected=Ember.ArrayProxy.createWithMixins(Ember.SortableMixin, {
sortProperties: ['name'],
content: unselected
});
unselected.forEach(function(item){
ret.pushObject(item);
});
});
});
return ret;
}.property('selectedPermissions')
Additionally your two properties claim to be dependent on each other, which should fire an infinite loop of property updating (a changes, b is dirty, b updates, a is dirty etc).
I'm not sure why selectedPermissions is a computed property, it seems like it would just be a list that's added to or removed from, and unselectedPermissions would just be allPermisions not selectedPermissions
I have the following drop-downs:
{{view SettingsApp.Select2SelectView
id="country-id"
contentBinding=currentCountries
optionValuePath="content.code"
optionLabelPath="content.withFlag"
selectionBinding=selectedCountry
prompt="Select a country ..."}}
...
{{view SettingsApp.Select2SelectView
id="city-id"
contentBinding=currentCities
selectionBinding=selectedCity
prompt="Select a city ..."}}
The bound properties are defined in a controller:
SettingsApp.ServicesEditController = SettingsApp.BaseEditController.extend(SettingsApp.ServicesMixin, {
needs : ['servicesIndex'],
selectedCountry : null,
selectedCity : null,
currentCountries : null,
currentCities : null,
init : function () {
this._super();
},
setupController : function (entry) {
this._super(entry);
var locator = SettingsApp.locators.getLocator(this.get('content.properties.locator'));
var countryCode = locator.get('country'), city = locator.get('city');
this.set('currentCountries', SettingsApp.countries.getCountries());
this.set('currentCities', SettingsApp.locators.getCities(countryCode));
this.set('selectedCountry', SettingsApp.countries.getCountry(countryCode));
this.set('selectedCity', city);
// Add observers now, once everything is setup
this.addObserver('selectedCountry', this.selectedCountryChanged);
},
selectedCountryChanged: function () {
var countryCode = this.get('selectedCountry.code');
var currentCities = SettingsApp.locators.getCities(countryCode);
var selectedCity = currentCities[0];
this.set('currentCities', currentCities);
this.set('selectedCity', selectedCity);
},
...
});
Initial setup is working fine, but changing the country selection does not update the city selection in the drop-down, even though the observer (selectedCountryChanged) is called and the this.set('selectedCity', selectedCity); is working as expected (as seen in console logging). The currentCities are properly set after the observer runs, but the active (selected) value is not correct (remains unchanged).
Are there any known issues with the programmatic update of bound properties, in this case selectionBinding?
My Select2SelectView is:
SettingsApp.Select2SelectView = Ember.Select.extend({
prompt: 'Please select...',
classNames: ['input-xlarge'],
didInsertElement: function() {
Ember.run.scheduleOnce('afterRender', this, 'processChildElements');
},
processChildElements: function() {
this.$().select2({
// do here any configuration of the
// select2 component
escapeMarkup: function (m) { return m; } // we do not want to escape markup since we are displaying html in results
});
},
willDestroyElement: function () {
this.$().select2('destroy');
}
});
Check whether selected city is getting displayed by removing select2(replacing with normal select). If that's the case, selectionbinding has to be propagated to select2.
My controller has a computed property:
App.IndexController = Ember.ArrayController.extend({
grandTotal: function () {
return this.getEach('total').reduce(function(accum, item) {
return accum + item;
}, 0);
}.property('#each.total'),
});
but I'm having trouble accessing it with my view. Here's my view:
App.SummaryView = Ember.View.extend({
templateName: 'summary',
companiesChanged: function() {
Ember.run.once(this, 'logCompanies');
}.observes('controller.#each'),
logCompanies: function() {
console.log(this.get('controller').get('model').get('length'));
console.log(this.get('controller').get('grandTotal'));
}
});
.get('length') returns correctly, so I know when this is called the models are loaded. But grandTotal is coming back as NaN, even though I know it's coded correctly since it's being rendered in the template. I need to access it within my view for additional reasons.
Any ideas?
Even though the controller's computed property changes with #each.total, the view only cares about the controller's property. Thus, the view was wrongly observing #each model, when it should have just been observing controller.grandTotal:
App.SummaryView = Ember.View.extend({
templateName: 'summary',
companiesChanged: function() {
Ember.run.once(this, 'logCompanies');
}.observes('controller.grandTotal'),
logCompanies: function() {
console.log(this.get('controller').get('model').get('length'));
console.log(this.get('controller').get('grandTotal'));
}
});
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,