How to call ember actions synchronously - ember.js

I want execute couple of ember actions synchronously, what is the right way to do this. For example
this.send('closeModal');
this.send('openModal','login'); // run when `closeModal` is totally executed
or how to call action in route without using send
export default Ember.Route.extend({
actions:{
openModal: function () {
//how to execute like this.closeModal(); without .send
... logic...
});
closeModal: function () {
... logic...
});
}
});

Perhaps you could move the action logic to other functions on the route i.e.
export default Ember.Route.extend({
openModal: function() {
...logic...
},
closeModal: function() {
...logic...
},
actions:{
openModal: function () {
this.closeModal(); // Ensure other modals are closed.
this.openModal();
});
closeModal: function () {
this.closeModal();
});
}
});

Related

Ember 2.0 get another router from router/controller

Is there any way to call route action from another router/controller? Let's say I have two routes:
App.RouteOne = Ember.Object.extend({
actions: {
someCommonFunctionality: function() {
// ...
}
}
});
App.RouteTwo = Ember.Object.extend({
actions: {
// Here I want to call someCommonFunctionality function from RouteOne
}
});
Is this somehow possible? I have an AJAX get method that I do not want to repeat in RouteTwo as I have it already in RouteOne

How to run action in Ember Controller afterRender

I am new to ember framework. I just want to execute a function that is defined inside the actions hook after the rendering completes.
var Controller = Ember.Controller.extend({
actions: {
foo: function() {
console.log("foo");
}
}
});
Ember.run.schedule("afterRender",this,function() {
this.send("foo");
}
But the above code is not working.
I just want to know, is it possible to run foo() afterRender?
You could use init:
App.Controller = Ember.Controller.extend({
init: function () {
this._super();
Ember.run.schedule("afterRender",this,function() {
this.send("foo");
});
},
actions: {
foo: function() {
console.log("foo");
}
}
});

Ember, mixin to detect click outside of view/component

I'm writing a Mixin to handle when user clicks outside of a view/component.
This is the mixin:
App.ClickElsewhereMixin = Ember.Mixin.create({
onClickElsewhere: Ember.K,
didRender: function() {
this._super.apply(this, arguments);
return $(document).on('click', this.get('onClickElsewhere'));
},
willDestroyElement: function() {
this._super.apply(this, arguments);
$(document).off('click', this.get('onClickElsewhere'));
},
});
I use it in my component:
onClickElsewhere: function() {
this.send('exitEditMode');
},
But when I run it, I get:
TypeError: this.send is not a function
How can I keep the this context?
Solution:
just to make it easier for the reader, here the working Mixin:
App.ClickElsewhereMixin = Ember.Mixin.create({
onClickElsewhere: Ember.K,
setupListener: Ember.on('didRender', function() {
// Set an event that will be fired when user clicks outside of the component/view
return $(document).on('click', $.proxy(this.get('onClickElsewhere'), this));
}),
removeListener: Ember.on('willDestroyElement', function() {
// Clean the previously defined event to keep events stack clean
return $(document).off('click', $.proxy(this.get('onClickElsewhere'), this));
}),
});
The current answer doesn't check whether the click was actually outside of the element – a click on the component will also trigger the callback.
Here's an updated version:
export default Ember.Mixin.create({
onOutsideClick: Ember.K,
handleOutsideClick: function(event) {
let $element = this.$();
let $target = $(event.target);
if (!$target.closest($element).length) {
this.onOutsideClick();
}
},
setupOutsideClickListener: Ember.on('didInsertElement', function() {
let clickHandler = this.get('handleOutsideClick').bind(this);
return Ember.$(document).on('click', clickHandler);
}),
removeOutsideClickListener: Ember.on('willDestroyElement', function() {
let clickHandler = this.get('handleOutsideClick').bind(this);
return Ember.$(document).off('click', clickHandler);
})
});
Greg answer have a mistake, that makes removing the clickHandler event not working. Which means that your clickevent will fire even if you destroy the component.
Here is proper version
import Ember from 'ember';
export default Ember.Mixin.create({
onOutsideClick: Ember.K,
handleOutsideClick: function(event) {
let $element = this.$();
let $target = $(event.target);
if (!$target.closest($element).length) {
this.onOutsideClick();
}
},
setupOutsideClickListener: Ember.on('didInsertElement', function() {
let clickHandler = this.get('handleOutsideClick').bind(this);
return Ember.$(document).on('click', clickHandler);
}),
removeOutsideClickListener: Ember.on('willDestroyElement', function() {
let clickHandler = this.get('handleOutsideClick').bind(this);
return Ember.$(document).off('click', Ember.run.cancel(this, clickHandler));
})
});
The ember way of doing it is Ember.run.bind. This takes care of binding and the run loop.
App.ClickElsewhereMixin = Ember.Mixin.create({
onClickElsewhere: Ember.K,
setupListener: Ember.on('didRender', function() {
this.set('clickHandler', Ember.run.bind(this, this.onClickElsewhere));
Ember.$(document).click(this.get('clickHandler'));
}),
removeListener: Ember.on('willDestroyElement', function() {
Ember.$(document).off('click', this.get('clickHandler'));
}),
});
You have two options:
Use a closure
Use bind
Closure
App.ClickElsewhereMixin = Ember.Mixin.create({
onClickElsewhere: Ember.K,
didRender: function() {
this._super.apply(this, arguments);
return $(document).on('click', function(this){ return this.get('onClickElsewhere'); }(this));
},
willDestroyElement: function() {
this._super.apply(this, arguments);
$(document).off('click', function(this){ return this.get('onClickElsewhere'); }(this));
},
});
Bind
App.ClickElsewhereMixin = Ember.Mixin.create({
onClickElsewhere: Ember.K,
didRender: function() {
this._super.apply(this, arguments);
return $(document).on('click', this.get('onClickElsewhere').bind(this));
},
willDestroyElement: function() {
this._super.apply(this, arguments);
$(document).off('click', this.get('onClickElsewhere').bind(this));
},
});
However, not all browsers support bind yet.
Also, I think you need to use sendAction instead of send in the component (http://guides.emberjs.com/v1.10.0/components/sending-actions-from-components-to-your-application/)
Edit:
jQuery.proxy uses call/apply underneath the covers. See this post for a discussion of call/apply vs bind.
You can use the lib ember-click-outside. Worked for me.

In Ember pre4's router, how do you trigger a route event from within another route?

I have code such as that shown below, and I'm just wondering how I can trigger another event within the route's events. Thoughts?
App.MyRoute = Ember.Route.extend({
events: {
eventOne: function() {
// do something
},
eventTwo: function() {
// how do I call eventOne() here?
},
}
});
You can just call events.eventOne() using this as the context:
App.IndexRoute = Ember.Route.extend({
events: {
eventOne: function() {
console.log('You got me!');
},
eventTwo: function() {
this.events.eventOne();
},
}
});

Same Ember.JS template for display/edit and creation

I am writing a CRUD application using Ember.JS:
A list of “actions” is displayed;
The user can click on one action to display it, or click on a button to create a new action.
I would like to use the same template for displaying/editing an existing model object and creating a new one.
Here is the router code I use.
App = Ember.Application.create();
App.Router.map(function() {
this.resource('actions', {path: "/actions"}, function() {
this.resource('action', {path: '/:action_id'});
this.route('new', {path: "/new"});
});
});
App.IndexRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('actions');
}
});
App.ActionsIndexRoute = Ember.Route.extend({
model: function () {
return App.Action.find();
}
});
App.ActionRoute = Ember.Route.extend({
events: {
submitSave: function () {
this.get("store").commit();
}
}
});
App.ActionsNewRoute = Ember.Route.extend({
renderTemplate: function () {
this.render('action');
},
model: function() {
var action = this.get('store').createRecord(App.Action);
return action;
},
events: {
submitSave: function () {
this.get("store").commit();
}
}
});
The problem is that when I first display an action, and then come back to create a new one, it looks like the template is not using the newly created record, but use instead the one displayed previously.
My interpretation is that the controller and the template are not in sync.
How would you do that?
Maybe there is a simpler way to achieve this?
Here is a JSBin with the code: http://jsbin.com/owiwak/10/edit
By saying this.render('action'), you are not just telling it to use the action template, but also the ActionController, when in fact you want the action template, but with the ActionNewController.
You need to override that:
this.render('action', {
controller: 'actions.new'
});
Updated JS Bin.