Currently, I am working with Ember.js using ember-cli and ember-data. The development process has been running smoothly so far, but recently I encountered an issue related to css transitions that I am struggling to resolve on my own.
The problem arises with a list in which each element contains subelements that are also rendered as a list. I am fetching data from a REST API using ember-data. Upon receiving the data, I intend to apply a fade-in effect (via css opacity) to the list. However, the transition does not always work as expected. It seems to be a timing issue. To address this, I incorporated Ember.run.next into my code, but it did not provide the desired solution. Surprisingly, adding a setTimeout with just 1ms within Ember.run.next seemed to resolve the issue, at least on my end. This situation feels quite peculiar. Below is the code snippet I have thus far. Any input or feedback would be greatly appreciated.
controller.js:
export default Ember.Controller.extend({
//...
objects: [],
//...
_pushToMatchings: function (response) {
var tempArray = [];
var pushed = false;
for (var i = 0, length = this.get('objects.length'); i < length; i++) {
pushed = false;
var match = this.get('objects').objectAt(i);
if (match.get('meta.items').objectAt(0) === response.get('meta.items').objectAt(0)) {
tempArray.pushObject(response);
pushed = true;
} else {
tempArray.pushObject(match);
}
}
if (!pushed) {
tempArray.pushObject(response);
}
this.set('objects', tempArray);
},
fetch: function() {
var self = this;
// find parent item
this.get('store').find('item', id).then(function (item) {
self._pushToMatchings(Ember.Object.create({
meta: {
items: [id],
isLoading: true,
label: item.get('label')
},
content: []
}));
self.set('isOpen', true);
// child object
self.get('store').find('child', searchParams).then(function (result) {
(function (resultPtr) {
Ember.run.next(function () {
setTimeout(function () { // @todo why do we need timeout here? whitout there is no fade out with opacity in css possible
resultPtr.set('meta.isLoaded', true);
}, 1); // 1 is enough but give spinner some time otherwise it looks ugly
});
}(result));
result.set('meta.label', item.get('label'));
self._pushToMatchings(result);
}, function (error) { /* ... */ });
}, function (error) { /* ... */ });
}
}
controller.hbs:
<div>
{{item-list close="close" elements=objects }}
</div>
item-list.js
export default Ember.Component.extend({
elements: [],
actions: {
close: function () {
this.sendAction('close');
}
}
});
item-list.hbs
<div class="items-list__buttons">
<i class="icon-close_32" {{action "close" }}></i>
</div>
<div class="items-list__content">
{{#each matching in elements}}
<div class="items-list__item">
<h2>{{t "items.offers" }} {{matching.meta.label}}</h2>
{{spinner-element hideEvent=matching.meta.isLoaded }}
<div {{bind-attr class=":items-list__box matching.meta.isLoaded:items--fadeIn" }}>
{{#each item in matching.content}}
<div>
<!-- Render details of item -->
</div>
{{/each}}
</div>
</div>
{{/each}}
</div>
CSS:
.items-list__box {
opacity: 0;
transition: opacity 150ms ease 100ms;
}
.items--fadeIn {
opacity: 1;
}