I have a basic Ember application. It has 2 controls: a select element and a text field. The controls are used to change what data is visible. Using the select element you can choose to see all numbers, even numbers only, or odd numbers only. Using the text field you can search for numbers that contain the digits you input.
I want the controls need to behave indepentently. By that I mean that if I type into the text field after choosing an option from the select element, the select element should be reset. Likewise if I choose an option from the select element, the text field should be cleared.
I find this difficult to implement because if I call this.set('query', '')
in select
's observer, I will trigger the query
's observer which is programmed to set content
to all the numbers when query === ''
. In other words, this approach makes it so that choosing even numbers will cause the text field to clear which will cause all the numbers to be visible which is wrong since even numbers were chosen!
The way I got it to work way by putting a check for the value of select
in the query_changed
method. This feels like a bad solution. I imagine that if there were many more than 2 controls, this approach would fill up observers with many if statements.
query_changed: function() {
var query = this.get('query');
if (query === '') {
if (this.get('select').value === '') { // <-- is there a better way?
this.set('content', this.get('numbers'));
}
} else {
var output = $.map(this.get('numbers'), function(number, i) {
if (_.string.include(number, query)) {
return number;
}
return null;
});
this.set('content', output);
}
}.observes('query')
I tried a few other ideas, but they were all basically like the above.
How are you supposed to handle this in Ember? I looked at Ember.StateManager
and it looks like it is meant to handle multiple views. Here I have only one view with stateful controls.
Demo: http://jsfiddle.net/DxbeB/1/
The code without anything to synchronize the controls:
App = Ember.Application.create({});
App.controller = Ember.Object.create({
numbers: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
contentBinding: Ember.Binding.oneWay('numbers'),
select: null,
query: null,
select_changed: function() {
if (this.get('select').value === 'all') {
this.set('content', this.get('numbers'));
}
if (this.get('select').value === 'even') {
var output = $.map(this.get('numbers'), function(number, i) {
if (number % 2 === 0) {
return number;
}
return null;
});
this.set('content', output);
}
if (this.get('select').value === 'odd') {
var output = $.map(this.get('numbers'), function(number, i) {
if (number % 2 !== 0) {
return number;
}
return null;
});
this.set('content', output);
}
}.observes('select'),
query_changed: function() {
var query = this.get('query');
if (query === '') {
this.set('content', this.get('numbers'));
} else {
var output = $.map(this.get('numbers'), function(number, i) {
if (_.string.include(number, query)) {
return number;
}
return null;
});
this.set('content', output);
}
}.observes('query')
});
App.Select = Ember.Select.extend({
content: [{label: '', value: ''}, {label: 'All numbers', value: 'all'}, {label: 'Even numbers', value: 'even'}, {label: 'Odd numbers', value: 'odd'}],
optionLabelPath: 'content.label',
optionValuePath: 'content.value'
});
App.MyView = Ember.View.extend({
controllerBinding: 'App.controller'
});
The template:
{{#view App.MyView}}
{{view App.Select selectionBinding="controller.select"}}
{{view Ember.TextField valueBinding="controller.query"}}
<p>{{controller.content}}</p>
{{/view}}
You could observe when the odd/even filter
value is set on the controller and then reset the query
value and vice versa, see http://jsfiddle.net/pangratz666/bWT4E/ :
Handlebars :
<script type="text/x-handlebars" >
{{#view App.MyView}}
{{view App.Select selectionBinding="select"}}
{{view Ember.TextField valueBinding="query"}}
<hr/>
{{#each controller.filtered}}
{{this}}
{{/each}}
{{/view}}
</script>
JavaScript :
App = Ember.Application.create({
oddFilter: function(value) {
return value % 2 === 1;
},
evenFilter: function(value) {
return value % 2 === 0;
},
queryFilter: function(query) {
return function(value) {
return (value + '').indexOf(query) !== -1;
};
}
});
App.Select = Ember.Select.extend({
prompt: 'define filter',
content: ['odd', 'even']
});
App.controller = Ember.ArrayProxy.create({
content: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
_queryChanged: function() {
var q = this.get('query');
if (!Ember.empty(q)) {
this.set('select', null);
}
}.observes('query'),
_selectChanged: function() {
var s = this.get('select');
if (!Ember.none(s)) {
this.set('query', null);
}
}.observes('select'),
filtered: function() {
var s = this.get('select');
var q = this.get('query');
var filter = Ember.K;
if (s === 'odd') {
filter = App.get('oddFilter');
} else if (s === 'even') {
filter = App.get('evenFilter');
} else if (!Ember.empty(q)) {
filter = App.get('queryFilter')(q);
}
return this.filter(filter, this);
}.property('select', 'query').cacheable()
});
App.MyView = Ember.View.extend({
controllerBinding: 'App.controller',
selectBinding: 'controller.select',
queryBinding: 'controller.query'
});
Regarding your question if (this.get('select').value === '') { // <-- is there a better way?
: Always access properties via set
and get
. This assures that the Binding stuff in Ember works as expected. Also, there are some utilities functions, so checking for an empty string can be done via Ember.empty(value)
. See a blog post about that: http://code418.com/blog/2012/03/08/useful-emberjs-functions/
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.