before i ask my question i have problem with with attribute binding and solve the problem with this link know my component is like this :
OlapApp.ItemRowsComponent = Ember.Component.extend({
tagName: 'li',
classNameBindings: ['currentItem.isMeasure:d_measure:d_dimension'],
attributeBindings:['data-id'],
'data-id':Ember.computed.oneWay('currentItem.id'),
actions: {
removeItem: function(item) {
item.deleteRecord();
item.save();
},
didClick: function(item) {
if (item.get('isMeasure')) {
item.deleteRecord();
item.save();
}
}
}
});
ok know i want to add another attribute to this that bind with currentItem.isMeasure.before this i use currentItem.isMeasure for class binding in this component and work correctly but when im using this code :
attributeBindings:['data-id','data-isMeasure'],
'data-id':Ember.computed.oneWay('currentItem.id'),
'data-isMeasure':Ember.computed.oneWay('currentItem.isMeasure'),
and ember create a li element like this :
<li id="ember745" class="ember-view d_measure" data-id="03lp9" data-ismeasure="data-isMeasure">
data-ismeausre must be true of false not data-isMeasure . so im using another way:
attributeBindings:['data-id','io:data-isMeasure'],
'data-id':Ember.computed.oneWay('currentItem.id'),
io:function(){
console.log(this.get('currentItem').get('isMeasure')); //its return true
return this.get('currentItem').get('isMeasure');
}.property(),
but the returned value still
but when im console.log it return true but insert data-isMeasure instead of true in element.
i solved my problem with a trick . in my application currentItem.isMeasure is true and the true is boolean so ember insert the name of attribute in element. so i try this code :
attributeBindings:['data-id','io:data-isMeasure'],
'data-id':Ember.computed.oneWay('currentItem.id'),
io:function(){
var val = this.get('currentItem.isMeasure');
if(val==true)return "true";
return "false";
}.property('currentItem.isMeasure'),
now every thing work correctly and my element is :
<li id="ember745" class="ember-view d_measure" data-id="acafd" data-ismeasure="true">
and know i can think about cleaning of this trick with better code.
Related
I want to define a reactive array property in a component so that every time the array is updated, it auto-updates necessary HTML content.
I thought this was going to be straightforward, but it didn't work as I expected. Every time I retrieve the property via this.get('array'), it returns undefined.
// components/test-component.js
export default Ember.Component.extend({
_array: [],
array: Ember.computed('_array',{
get(key) { return this.get('_array'); },
set(key,value) { this.set('_array', value); }
}),
isEmpty: Ember.computed('array', function() {
// Here, this.get('array') returns undefined. Why?
return this.get('array').length;
}),
actions: {
addNew() {
this.get('array').push(Date.now());
}
},
init() {
this._super(...arguments);
this.set('array', [1,2,3]);
},
});
I also noticed that in the init method, if I retrieve the array property right after setting it, it also returns undefined. Why is this happening?
Here is a twiddle. It is supposed to iterate the array, and show a list of all items, but it is currently crashing because it returns undefined.
The problem you are currently seeing is that you need to add a return in the set method. Also you should use the Ember.computed('array.[]') syntax where you listen for changes to the array itself.
But you would be better off using an Ember array so that you don't need a second array:
import Ember from 'ember';
export default Ember.Component.extend({
array: undefined,
init() {
this._super(...arguments);
this.set('array', Ember.A());
},
actions: {
addNew() {
this.get('array').addObject(Date.now());
}
}
});
and
<ul>
{{#each array as |item|}}
<li>{{item}}</li>
{{else}}
<li>No items</li>
{{/each}}
</ul>
<button onclick={{action 'addNew'}}>Add New</button>
Hy and thanks for reading :
I have an issue with Ember.ArrayController and arrangedContent. The senario is as follow :
Items inside my arrayController can be modified by some actions.
My arrangedContent is filtred on some of those items properties.
Thus if an observed item property change the arrangedContent property should be refreshed
I achieve this by setting the property() of my arrangedContent with "content.#each.myproperty"
All works fine, except if i try to refresh the model from the route i then get an error message TypeError: Cannot read property 'destroy' of undefined
at ContainerView.extend.arrayWillChange
In some case it would work but duplicate the content every time the refresh() is triggered
with some minimal code it may become more clear ...
App.IndexRoute = Ember.Route.extend({
model : function(){
return [App.Cars.create({color : "red", model : "march"}),
App.Cars.create({color : "yellow", model : "alto"}),
App.Cars.create({color : "blue", model : "gundam"}) ];
},
actions : {
reload : function(){
this.refresh();
}
}
});
App.IndexController = Ember.ArrayController.extend({
arrangedContent : function(){
var data= this.get("content");
data=data.filter(function(elem){
return elem.get("color").match(new RegExp("el","gi"))!==null;
});
return data;
}.property("lenght","content.#each.color"),
actions : {
allYell :function(){
this.get("content").forEach(function(elem){
elem.set("color","yellow");
});
},
erase : function(){
if(this.get("length")>0){
this.get("content").removeAt(0);
}
}
}
});
a JSBin can be found here http://jsbin.com/yebopobetu/3/edit?html,js,console,output
I've never seen anyone recommend overriding arrangedContent. I honestly wouldn't recommend it.
App.IndexController = Ember.ArrayController.extend({
foos : function(){
var data= this.get("model");
return data.filter(function(elem){
return elem.get("color").match(new RegExp("el","gi"))!==null;
});
}.property("#each.color"),
actions : {
allYell :function(){
this.forEach(function(elem){
elem.set("color","yellow");
});
},
erase : function(){
if(this.get("length")>0){
this.removeAt(0);
}
}
}
});
http://jsbin.com/sivugecunu/1/edit
I am probably going to use an ArrayController + itemController setup to solve this, but maybe this is better off inside the model layer.
I want to override the property of an object to return another value if the property is empty. I think this i best described in code (jsfiddle: http://jsfiddle.net/ahx_/Tqw4C/2/).
App = Ember.Application.create()
App.Teacher = Ember.Object.extend()
App.Pupil = Ember.Object.extend({
// TODO Add a property ('answer') that returns teacher.answer unless this.answer is defined
// Pseudo-code:
// answer: function(key, value) {
// if(Ember.isEmpty(this.answer)) {
// return this.get('teacher.answer')
// } else {
// return this.answer
// }
// }.property('answer')
})
App.IndexController = Ember.Controller.extend({
init: function() {
this._super()
teacher = App.Teacher.create({answer: 'correct'})
this.set('pupil1', App.Pupil.create({ teacher: teacher, answer: 'incorrect' }))
this.set('pupil2', App.Pupil.create({ teacher: teacher }))
}
})
You need to add another property as .property() cannot refer to itself.
Object:
App.Pupil = Ember.Object.extend({
answerToShow: function(){
return this.get('answer') ? this.get('answer') : this.get('teacher.answer')
}.property('answer')
})
Template:
<script type="text/x-handlebars" data-template-name="index">
Pupil1 ('incorrect'): {{pupil1.answerToShow}}
<br>
Pupil2 ('correct'): {{pupil2.answerToShow}}
</script>
Demo: http://jsfiddle.net/Tqw4C/5/
I have this test application which should print "filtered" and "changed" each time the applications view is clicked, because a computed property is called. However the property binding is only triggered when updating the property with an empty array:
window.App = Ember.Application.create({
Settings: Ember.Object.create({
filter: []
}),
ApplicationView: Ember.View.extend({
click: function(event) {
filter = App.get('Settings.filter');
console.dir(filter);
if (App.get('Room.filtered')) {
filter = filter.filter(function(item) {
return item != App.get('Room.name');
});
} else {
filter.push(App.get('Room.name'));
}
App.set('Settings.filter', filter);
}
})
});
Room = Ember.Object.extend({
name: "test",
_filterer: function() {
console.dir("changed");
}.observes('filtered'),
filtered: function() {
console.dir("filtered");
filter = App.get('Settings.filter');
for (var i = 0; i < filter.length; i++) {
if (filter[i] == this.get('name')) return true;
}
return false;
}.property('App.Settings.filter', 'name').volatile()
});
App.Room = Room.create();
setTimeout(function() {App.set('Settings.filter', ['test']); }, 3000);
Here is the jsbin: http://jsbin.com/edolol/2/edit
Why is the property binding only triggered when setting the Setting to an empty array? And why does it trigger when doing it manually in the timeout?
Update
Here is a working jsbin: http://jsbin.com/edolol/11/edit
When you're going to add/remove items from an array, and not change the entire array, you need to inform the computed property to observe the items inside the array, not only the array itself:
The syntax is:
function() {
}.property('App.settings.filter.#each')
The reason it was working with setTimeout is because you were replacing the entire array instead of the items inside it.
I fixed your jsbin: http://jsbin.com/edolol/10/edit
I fixed some minor other stuff such as filter.push is now filter.pushObject (Using Ember's MutableArray).
And after changing the filter array (filter = filter.filter()) you need to set the new filter variable as the property: App.set('Settings.filter', filter);
The Problem is that I have used .push() to add to App.Settings.filter and .filter() to remove from it. The first approach does not create a new array, the latter does. Thats why removing from that array has worked, but not adding.
I assume that using Ember.ArrayProxy and an observer for .#each would have worked. But thats out of my knowledge. This little problem is solved by just creating a new array though.
filter = App.get('Settings.filter').slice(0);
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