calling method from action of controller in emberjs - ember.js

for example i have a controller like this :
App.theController = Ember.ArrayController.extend({
methodA:funtcion() {},
actions: {
methodB:function(){},
methodC:function(){}
}
});
my questions is :
How can methodB call methodC
How can methodA call methodB

You have to use this.send([methodName]) in order to get your methods called correctly:
var App = Ember.Application.create({
ready: function() {
console.log('App ready');
var theController = App.theController.create();
theController.send('methodC');
}
});
App.theController = Ember.ArrayController.extend({
methodA:function(){
//How can methodA calling methodB
this.send('methodB');
console.log('methodA called');
},
actions:{
methodB:function(){
//How can methodB calling methodC
this.send('methodC');
console.log('methodB called');
},
methodC:function(){
console.log('methodC called');
}
}
});
Here a simple jsbin as a playground.
Hope it helps.

Related

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.

How to test logic in joined promises/then

I am trying to write a test for an action handler on one of my components. I am stubbing out the save method on one of my models to return a resolved promise using Em.RSVP.Promise.resolve()
in my component, i chain on that promise using then:
return target
.save()
.then(function(){
selected.rollback();
this.sendAction('quicklinkChanged', target);
}.bind(this),this.notify_user_of_persistence_error.bind(this, 'Save As'));
this is a pattern that i use a lot server-side where we use when for our promise library. however, when i do this client-side, i never end up inside the function in the then block so i cannot assert any of the functionality there in my unit tests.
can anyone provide any insight on the best way to do this?
We moved our callbacks out of the method so we could call them separately and verify functionality, or replace them and verify they were called.
Controller Example:
App.IndexController = Em.Controller.extend({
randomProperty: 1,
async: function(fail){
return new Em.RSVP.Promise(function(resolve, reject){
if(fail){
reject('fdas');
}else{
resolve('foo');
}
});
},
doAsyncThing: function(fail){
return this.async(fail).then(this.success.bind(this), this.failure.bind(this));
},
success: function(){
this.set('randomProperty', 2);
},
failure: function(){
this.set('randomProperty', -2);
}
});
Tests
test("async success", function(){
var ic = App.IndexController.createWithMixins();
stop();
ic.doAsyncThing(false).then(function(){
start();
equal(ic.get('randomProperty'), 2);
});
});
test("async fail", function(){
var ic = App.IndexController.createWithMixins();
stop();
ic.doAsyncThing(true).then(function(){
start();
equal(ic.get('randomProperty'), -2);
});
});
test("async success is called", function(){
expect(1);
var ic = App.IndexController.createWithMixins();
ic.success = function(){
ok(true);
};
stop();
ic.doAsyncThing(false).then(function(){
start();
});
});
test("async failure is called", function(){
expect(1);
var ic = App.IndexController.createWithMixins();
ic.failure = function(){
ok(true);
};
stop();
ic.doAsyncThing(true).then(function(){
start();
});
});
test("doAsyncThing returns a promise", function(){
expect(1);
var ic = App.IndexController.createWithMixins();
ok(ic.doAsyncThing(true).then);
});
Example: http://emberjs.jsbin.com/wipo/37/edit

Ember App Kit and testing model hook

In Ember App Kit, there are a number of testing examples that ship with the initial repo. One of those is a basic Route Unit test. This test is trivial, if the data is hard-coded in the model hook, like this:
test("#model", function(){
deepEqual(route.model(), ['red', 'yellow', 'blue']);
});
How do you use the isolated container to test the model hook if it returns a promise from ember-data?
Here's the test:
import Activities from 'appkit/routes/activities';
var route;
module("Unit - ActivitiesRoute", {
setup: function(){
var container = isolatedContainer([
'route:activities'
]);
route = container.lookup('route:activities');
}
});
test("#model", function(){
deepEqual(route.model(), ['activity', 'activity2', 'activity3']);
});
And the actual Route's model hook:
export default Ember.Route.extend({
model: function() {
return this.get('store').find('activity');
}
});
UPDATE:
After implementing the different approaches below from kingpin2k. Here is a summary of the outcomes.
First approach: works great ... yet no promise.
Second approach: returns the promise object (appears to be resolved), but the array, and correct values are assigned to _detail property.
test("#model", function(){
deepEqual(route.model()['_detail'], ['activity', 'activity2', 'activity3']); //passes
});
I'd like for store creation to be taken care of within the module setup().
...
module("Unit - ActivitiesRoute", {
setup: function(){
var container = isolatedContainer([
'route:activities'
]);
route = container.lookup('route:activities');
var store = {
find: function(type){
return new Em.RSVP.Promise(function(resolve){
resolve(['activity', 'activity2', 'activity3']); // or made up model(s) here
});
}
};
route.set('store', store);
}
});
And the test:
test("#model", function(){
deepEqual(route.model(), ['activity', 'activity2', 'activity3']); // ???
});
Third approach:
...
module('Unit - ActivitiesRoute', {
setup: function() {
var container = isolatedContainer([
'route:activities'
]);
route = container.lookup('route:activities');
var store = {
find: function() {
var promise = new Ember.RSVP.Promise(function(resolve) {
Em.run.later(function() {
resolve(Activity.FIXTURES);
}, 10);
});
return Ember.ArrayProxy.extend(Ember.PromiseProxyMixin).create({
promise: promise
});
}
};
route.set('store', store);
}
});
And in the test, calling route.model() returns an empty object {} :
test("#model", function(){
deepEqual(route.model(), Activity.FIXTURES); // returns {}
});
UPDATE #2
It was also necessary to add asyncTest() instead of test() and to also call start() to prevent the test runner from hanging.
asyncTest('#model', function(){
Em.run(function(){
route.model().then(function(result){
ok(result);
equal(result, Activity.FIXTURES);
start();
});
});
});
Simple approach, it's a unit test, so really you aren't testing the store, so setup a mock store and result.
route = container.lookup('route:activities');
var store = {
find: function(type){
equal(type, 'activity', 'type is activity');
return ['activity', 'activity2', 'activity3'];
}
}
route.set('store', store);
Even better you can also replicate the promise
route = container.lookup('route:activities');
var store = {
find: function(type){
equal(type, 'activity', 'type is activity');
return new Em.RSVP.Promise(function(resolve){
resolve(['activity', 'activity2', 'activity3']); // or made up model(s) here
});
}
}
route.set('store', store);
If you want to more closely replicate Ember Data you might use an ArrayProxy implementing the PromiseProxyMixin...
route = container.lookup('route:activities');
var store = {
find: function(type){
equal(type, 'activity', 'type is activity');
var promise = new Ember.RSVP.Promise(function(resolve){
Em.run.later(function(){
resolve(['activity', 'activity2', 'activity3']);
}, 10);
});
return Ember.ArrayProxy.extend(Ember.PromiseProxyMixin).create({
promise: promise
});
}
}
route.set('store', store);
Update
Using your last approach you should implement it like this
test("#model", function(){
route.model().then(function(result){
deepEqual(result, Activity.FIXTURES); // returns {}
});
});
But, there is something tricky here, since it has an async response you'll want to wrap it in an Ember run loop
test("#model", function(){
Em.run(function(){
route.model().then(function(result){
deepEqual(result, Activity.FIXTURES); // returns {}
});
});
});

how to call variables scoped in describe constructor in jasmine

I am using jasmine runner to test angular code.
describe('des1', function() {
var des1Var = function(){};
beforeEach() {
//....
}
describe('test1', function() {
var scope4Compile = $rootScope.$new();
var des2Var = des1Var(scope4Compile); // returns undefined.
beforeEach(function() {
des2Var = des1Var(scope4Compile); // returns des1Var() fine;
})
it('should do ', function(){
//should do...
})
it('should also do', function(){
//should also do...
})
})
})
I need to instantiate something once before the it statements, if run multiple times result is pretty bad. How can I get it done properly?
I believe it you call it once in the first beforeEach it will be run one time for each describe that is below it.
In the code below, des2Var will be set once for the whole test1 describe.
describe('des1', function() {
var des1Var = function () { };
beforeEach(function () {
var des2Var = des1Var();
});
describe('test1', function() {
it('should do ', function(){
//should do...
});
it('should also do', function(){
//should also do...
});
});
});