Ember do action on arrray push - ember.js

http://jsbin.com/qoyudape/4/edit
Every time you click update, item gets inserted. I would like to blink item red for one second after insertion, like on stackoverflow when linking to answer, it blinks orange for a second https://stackoverflow.com/a/22645880/1175593
How do I do that ?
Only CSS doesn't work, because I want the effect only on newly added items, and not on initial load

I don't know CSS, but this, Using CSS for fade-in effect on page load, seems to be just fine...
http://jsbin.com/qoyudape/6/edit
note, the render stuff I did isn't necessary, you could have just as easily done it without it...
http://jsbin.com/qoyudape/8/edit
or when the item is inserted into the page, you can do time sensitive animation
App.BlinkingFooView = Em.View.extend({
didInsertElement: function(){
if(this.get('controller.b') % 2 === 0){
this.$().addClass('mod')
}
}
});
http://jsbin.com/qoyudape/11/edit

Related

In an Ember Component where should you put reusable code that isn't an action?

I have a standard component defined something like:
export default Ember.Component.extend({
hideIfEmptyChange: function() {
var thingOfInterest = ...;
var otherThingOfInterest = ...;
...
// Perform some logic
...
// Perform some logic using thingOfInterest
// Perform exactly the same logic using otherThingOfInterest
}.observes('hideIfEmpty.#each')
});
I want to move the logic for the last two pieces into their own function to prevent writing out the same code twice just for two different variables.
I could write an action on my component and use this.send('myAction',...) - but would it be best practice to have this as an action if it isn't going to be used (or even usable) from the component's template? If you shouldn't do it this way then how should you?
My other thought was mixin's - but the code here will be completely specific to the component, so again this doesn't feel 100% right.
Edit
The component is used here to observe an array of widgets, which are displayed in a sidebar. To start with the sidebar is hidden away as there are no widgets. When a widget is added the sidebar then slides out using a css3 transition between a bootstrap class I added (col-md-0) and something like col-md-2, at the same time the main column shrinks from something like col-md-10 to col-md-8. Because of the nature of it coming out from zero width the widgets inside squish around during the animation and so need to be hidden during it. So the generic piece of code will be:
function(element, classes) {
var lowWidth = 50;
// Transition looks awful if columns start from small width, so hide inner content until css3 transition complete if starting from low width
if ($(element).width() < lowWidth) {
$('div', element).hide();
$(element).toggleClass(classes);
$(element).on('transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd', function(e) {
$('div', element).fadeIn(250);
$(this).off(e);
});
}
else {
$(element).toggleClass(classes);
}
}
My component here is in block form and actually has the sidebar within it in the template.
{{#hideable-cols hideIfEmpty=rightSidebarWidgets leftId="det-main" leftMinClass="col-md-8" leftMaxClass="col-md-10" rightId="det-sidebar-right" rightMinClass="col-md-0" rightMaxClass="col-md-2"}}
<div id="det-main" class="col-md-10 resizes">
{{outlet}}
</div>
<div id="det-sidebar-right" class="det-sidebar resizes col-md-0">
{{widget-area widgets=rightSidebarWidgets saveObjectFromWidget="saveObjectFromWidget" removeWidget="removeRightWidget"}}
</div>
{{/hideable-cols}}
The only other way to do what I wanted would have been if I could have set 'inner' components to only be able to react to changes in the array of widgets AFTER the parent block component is happy/done. I've used something similar in knockout js but couldn't see this kind of feature in Ember, so my workaround was to hide the div where the widgets are added, do the transition and show the div again afterward.

Having issues adding components within a ContainerView dynamically

I have a container view in application level and I wanted to add some components to this container view via some action. The way I do this is :
1) I push some objects to the application controller
2) ContainerView is within the context of application controller, so I observe the length of the array holding the objects mentioned in step 1
3) and then I create the components and append them to the container view
One weird thing I noticed was initially the observer defined in the container view was not triggering, but then I added initialize hook where I log the controller, and then the observer started working for me. This tells me maybe I am not doing something right. Here is my container view:
App.MyContainerView = Ember.ContainerView.extend({
initialize: function(){
//IF YOU WERE TO COMMENT THIS CONSOLE.LOG THEN THE OBSERVER NEVER GETS HIT WHEN YOU CLICK ON ADD BUTTON. MAYBE CONTROLLER IS NOT SET UP AND IT NEEDS SOME TIME???
console.log(this.get('controller').toString());
}.on('init'),
appendMyComponent: function(){
console.log("inside the observer");
this.get('controller.myComponents').forEach(function(comp){
console.log(this.toString());
this.pushOject(App.MyTestComponent.create({}));
}, this);
}.observes('controller.myComponents.length')
});
Do I have to use named outlet and then render the container view within the outlet? Not sure what I am doing wrong here.
Here is a jsbin: http://emberjs.jsbin.com/xamoxese/2/
Your feedback is much appreciated. Thanks good ppl of stackoverflow.
USING :
Ember : 1.5.0
Handlebars : 1.3.0
jQuery : 1.10.2
Allright some more updates, initially the solution provided by #bmeyers was not working for me although it was working in jsbin. So in my real app mu application.hbs looked like this:
{{outlet navbar}}
{{outlet}}
{{outlet modal}}
{{view App.MyContainerView}}
but then I re-ordered it as :
{{view App.MyContainerView}}
{{outlet navbar}}
{{outlet}}
{{outlet modal}}
and then the solution worked. Now only if I could get rid of that console.log thing :)
Here is updated jsbin that I have added on top of #bmeyers solution http://emberjs.jsbin.com/xamoxese/10/
I have fixed this for you and shown an example here: http://emberjs.jsbin.com/xamoxese/3/edit?html,css,js,output
I didn't fix your issue where you are adding the same components over and over again and never clearing the childView array, but maybe that was your intention.
Basically you do not push to the container view, you need to push to the childViews property.
Hope this helped!
UPDATE FIXED
http://emberjs.jsbin.com/xamoxese/6/edit
Ok so looks like you just needed a reference outside your foreach function and that for some reason allowed using it the way it is documented
UPDATE 2:: Change the containerView code to....
App.MyContainerView = Ember.ContainerView.extend({
init: function() {
this._super();
this.appendMyComponent();
},
appendMyComponent: function(){
this.get('controller.myComponents').forEach(function(comp){
this.pushObject(App.MyTestComponent.create({}));
}, this);
}.observes('controller.myComponents.length')
});
This just feels a bit cleaner than creating an empty view that you will never use. Picky I know but it was bugging me.
Finally figured out how to get rid of that console.log thing. So I actually have 2 solutions for this:
1) pass the controller to the container view
{{view 'myContainer' controller=this}}
2) set an empty view to the ContainerView
App.MyContainerView = Ember.ContainerView.extend({
childViews: [Ember.View.create()],
appendMyComponent: function(){
this.get('controller.myComponents').forEach(function(comp){
this.pushOject(App.MyTestComponent.create({}));
}, this);
}.observes('controller.myComponents.length')
});
Actually the best solution is as #bmeyers suggested, is to have init function for container view like this:
init: function() {
this._super();
/*
THIS IS THE OBSERVER METHOD THAT WE HAVE IN THE VIEW, THIS CALL DOES NOT EVEN
NEED TO ADD A CHILD VIEW, BUT STILL NEEDS TO BE CALLED - STRANGE I KNOW BUT WHY
I AM NOT SURE. ALSO FACED ISSUE DURING TEST FOR ABOVE 2 APPROACHES, THIS ONE WORKED WELL
*/
this.appendMyComponent();
}
Here is the working jsbin: http://jsbin.com/xamoxese/13/

Emberjs - Saving model with belongsTo from a modal

I'm trying to create a Todo record with a belongsTo relationship, Category, from a modal window and saving it.
Considering the following jsbin
http://jsbin.com/UlADutAj/1
In the modal window the todo has the category, you can see the title printed there, but once I save in the controller action, the category is not set.
It's done here with Fixtures, but I've got the same problem with ActiveModelSerializer.
If I use a normal route, not one with a modal, it works, so I'm guessing it's something wrong with my modal code, but can't figure it out.
You have a timing issue. You need to wait for your content to save (i.e. the promise to resolve) before closing the modal and performing a rollback.
You can check out this jsbin which changes the following:
saveTodo: function() {
var context = this;
var todo;
todo = this.get('model');
todo.save().then(function () {
// Now the save is complete and the isDirty flag is false.
context.send('closeModal');
});
}

Ember.js ToDoMVC - Chrome firing ObjectController action multiple times

I have been following the getting started guide for ember.
I have come to the parts that deal with editing and removing items and have come across a problem that seems to occur in chrome but not firefox.
The custom component "edit-todo" has 2 events that are linked to the action "acceptChanges"
{{#if isEditing}}
{{edit-todo class="edit" value=title focus-out="acceptChanges" insert-newline="acceptChanges"}}
{{else}}
The "acceptChanges" action fires the "removeTodo" action if the item's title is empty
acceptChanges: function () {
this.set('isEditing', false);
if (Ember.isEmpty(this.get('model.title'))) {
this.send('removeTodo');
} else {
this.get('model').save();
}
},
removeTodo: function () {
var todo = this.get('model');
todo.deleteRecord();
todo.save();
}
If you edit an item, delete the text and press enter or switch focus, the item gets deleted. This works perfectly for me in Firefox.
In Chrome however, the acceptChanges action is firing twice if you delete the title and press enter. When the DOM updates to remove the item, the acceptChanges action is fired again, presumably because it loses focus.
This jsbin shows the problem, it is essentially just the code from the guide with some console logs. If you edit the item "BBB", delete the text and press enter, the data for "CCC" is deleted also. It remains in the DOM but you can no longer interact with the item and the items remaining count is wrong. If you open ember inspector in the developer tools you can see that the only data left is for the item "AAA".
I am wondering if this is a bug with ember, with ember-data, with chrome, or if it is expected behaviour that I should be checking for in my js?
I am using Chrome Version 26.0.1410.63

Mitigating a flickering UI when refreshing content in Ember

The problem
I have a view which displays a list of workers. This view has a button which calls a controller method named refresh. Originally I was setting the content to this list, but noticed that the UI would flicker, which would be especially annoying once this is run on a timer.
I managed to make everything work without flickering using the following:
App.WorkersIndexController = Ember.ArrayController.extend
refresh: ->
console.log 'refreshing workers'
# set a property to a new collection
#set 'refreshing', App.Worker.find()
# observer that watches the new collection load
update: (->
refreshing = #get 'refreshing'
return unless refreshing.get('isLoaded')
# what this question is about...
Ember.run =>
#set 'content', refreshing
).observes('refreshing.isLoaded')
As you can see I'm setting the refreshed content to a temporary variable, and observing the loading of the content and then updating the content. The content flickered less until I added a call to Ember.run which removed the flicker entirely.
I understand why the flicker is occurring, and I understand why Ember.run makes it go away but my question is what is the best way to solve this without resorting to Ember.run? I feel like calling Ember.run is kind of a last resort... Any help would be greatly appreciated!
Edit: Also I'm not using ember data. The find method looks like this:
App.Worker.reopenClass
find: ->
console.log 'loading workers'
workers = Em.A []
request = $.getJSON '/api/admin/workers', (data) =>
console.log ' -> loaded workers'
data.forEach (d) =>
worker = App.Worker.create(d)
worker.set 'isLoaded', true
workers.pushObject(worker)
request.error =>
console.log ' -> failed to load workers'
workers.set 'isFailed', true
request.complete =>
workers.set 'isLoaded', true
workers
I have avoided flicker in similar situations by having a fixed list who's objects I just change.
In the simplest case, lets' say you were displaying names, and knew that you knew you'd never have more than 3 elements you could make
App.names = [
Ember.Object.create({name:"", active:false}),
Ember.Object.create({name:"", active:false}),
Ember.Object.create({name:"", active:false})
]
and have your template be
{{#each App.names}}
{{#if active}}
<li> {{name}} </li>
{{/if}}
{{/each}}
you could then add names by running
App.names[0].set("name", "Frank");
App.names[0].set("active", true);
and update the list without any flicker. Only a single element in the list is changed at a time, so you don't have the flicker from the whole list being redrawn.
In practical terms you might need App.names to be an ArrayController so that you can handle expanding the list gracefully, but the gist of that will work.