call an ember component action from within the component - ember.js

I am creating a component to wrap the select2 select box. The code is below:
App.FixedSelectComponent = Ember.Component.extend({
actions: {
change: function(value) {
this.set('selectedValue',value);
}
},
didInsertElement : function(){
this.$("#select1").select2().on("change", function(e) {
if ($.isArray(e.val)) {
$.each(e.val, function(index,value) {
console.log("multiple:",value.split('>')[2].split('<')[0]);
// send to change
});
} else {
console.log("single:",e.val.split('>')[2].split('<')[0]);
// send to change
}
});
},
willDestroyElement : function() {
this.$("#select1").select2('destroy');
},
});
however, what I am stuck at is how to send the data that I've got in the on("change") event to the action:change that I've defined , or if I can set the selectedValue property itself in the on("change") event
"this" isn't the component at the "// send to change" lines - how / where do I get the reference to the component itself at this point ?
basically what I am trying to achieve is to get the data passed to the "change" event of select2 into my selectedValue property
thanks

You can use Component.send('actionName').
I found it in Ember's documentation.

this context will not refer to FixedSelectComponent context in $.each, and also use send method which will call FixedSelectComponent change method..
refer : http://emberjs.com/api/classes/Ember.Component.html#method_send
didInsertElement : function(){
var _this = this;
this.$("#select1").select2().on("change", function(e) {
if ($.isArray(e.val)) {
$.each(e.val, function(index,value) {
console.log("multiple:",value.split('>')[2].split('<')[0]);
_this.send('change',value.split('>')[2].split('<')[0]); // send to change
});
} else {
console.log("single:",e.val.split('>')[2].split('<')[0]);
_this.send('change',e.val.split('>')[2].split('<')[0]); // send to change
}
});
}

this.get('actions').change.call(this, value);
Check http://emberjs.com/api/classes/Ember.Component.html#property_actions -- 'actions' is simply another property on your Component.

Try this:
App.FixedSelectComponent = Ember.Component.extend({
change: function(value) {
this.set('selectedValue',value);
}
didInsertElement : function(){
var self = this;
this.$("#select1").select2().on("change", function(e) {
if ($.isArray(e.val)) {
$.each(e.val, function(index,value) {
console.log("multiple:",value.split('>')[2].split('<')[0]);
// send to change
self.change(value); // substitute value by whatever you want to pass
});
} else {
console.log("single:",e.val.split('>')[2].split('<')[0]);
// send to change
self.change(value); // substitute value by whatever you want to pass
}
});
},
willDestroyElement : function() {
this.$("#select1").select2('destroy');
},
});

this._actions['change'].apply(this, value);

Related

Ember loading state not triggered on transitionTo

If I use a transitionTo on a route with a slow model hook, the loading.hbs state never gets triggered (I have loading.hbs files at all of the levels -- cluster, cluster.schedule and cluster.schedule.preview_grid). I tried renaming the one at cluster.schedule preview_grid-loading.hbs with no luck.
On the transitionTo, there is no model or model id passed in, just the route:
viewPreviewGrid: function() {
this.transitionTo('cluster.schedule.preview_grid');
},
I also have a loading action defined as follows:
loading(transition) {
var controller = this.controller;
if (!Ember.isNone(controller)) {
this.controller.reset();
}
transition.promise.finally(function() {
NProgress.done();
});
}
During the transitionTo call the page just stays on the previous route until the promises in the model hook resolve, and then it transitions to the other route. If I refresh the page, the loading state gets triggered just fine. Is this a known behaviour for transitionTo?
This is my model hook:
model: function (/*params*/) {
var socialProfile = this.modelFor('cluster.schedule').get('firstObject');
if (!socialProfile.get('isInstagram')){
throw new Error("Attempted to access preview with non-ig profile: " + socialProfile.get('id'));
}
var accessToken = socialProfile.get('token');
var self = this;
return Ember.RSVP.hash({
igPosts: new Ember.RSVP.Promise(function(resolve) {
self.getUsersRecentMedia(accessToken).then(function(response) {
var igPosts = Ember.A([]);
response.data.forEach(function(data) {
igPosts.pushObject(self.igPostFromResponse(data, socialProfile));
});
resolve(igPosts);
});
}),
posts: new Ember.RSVP.Promise(function(resolve) {
self.store.query('gram', { type: 'preview', social_profile_id: socialProfile.get('id'), limit: self.get('postLimit') }).then(function(grams) {
var filteredGrams = grams.filter(function(gram) {
return (gram.get('scheduledInFuture')) && (gram.belongsTo('socialProfile').id() === socialProfile.get('id')) && (gram.get('active'));
});
resolve(filteredGrams);
});
}),
igUser: new Ember.RSVP.Promise(function(resolve) {
self.getSelf(accessToken).then(function(response) {
resolve(self.igUserFromResponse(response.data, socialProfile));
});
})
});
},
You need to return true at the end of the loading() hook to tell Ember to go ahead and show the default loading route (loading.hbs).
loading(transition) {
var controller = this.controller;
if (!Ember.isNone(controller)) {
this.controller.reset();
}
transition.promise.finally(function() {
NProgress.done();
});
return true;
},

Access property inside Ember component

Need your help folks. How can I access property inside the component. Something like this:
export default Ember.Component.extend({
cMsg: Ember.computed('msg', function() {
return `${this.get('msg')} , ${this.get('msg')}`;
}),
selectedDomain: { msgPrefix: 'cMsg???' },
});
Here is the twiddle: https://ember-twiddle.com/9acda203a89dbd3892059170ab665d08?openFiles=components.hello-there.js%2C
Most of the time we miss the usage of custom helper and computed property. In this case you can write computed property,
selectedDomain: Ember.computed('cMsg', function() {
return { msgPrefix: this.get('cMsg') }
})

How to return a deferred promise and create a model with Ember.Deferred?

I'm trying to create a User.current() in my application, which pulls data from my server using $.getJSON('/users/current', function(data) { ... });. I am using the Singleton method that Discourse uses, which does the following:
Dashboard.Singleton = Ember.Mixin.create({
// See https://github.com/discourse/discourse/blob/master/app/assets/javascripts/discourse/mixins/singleton.js
current: function() {
if (!this._current) {
this._current = this.createCurrent();
}
return this._current;
},
createCurrent: function() {
return this.create({});
}
});
And in my User singleton model, I've rewritten createCurrent as follows:
Dashboard.User.reopenClass(Dashboard.Singleton, {
createCurrent: function() {
return Ember.Deferred.promise(function(p) {
return p.resolve($.getJSON('/users/current').then(function(data) {
return Dashboard.User.create(data);
}));
});
}
});
User is a normal Ember object model:
Dashboard.User = Ember.Object.extend({
});
This does request the data from the server, but the function is not setting User.current() correctly - when I inspect it, User.current() has none of the properties that should be set, such as name.
How can I return and set the current user using Ember's deferred and promises?
That's cause you're returning the promise in place of the user.
Why don't you create the user, then fill in the properties later.
Or use the Promise Proxy pattern that Ember Data uses (the promise can be used as the object once resolved)
DS.PromiseObject = Ember.ObjectProxy.extend(Ember.PromiseProxyMixin);
function promiseObject(promise) {
return DS.PromiseObject.create({ promise: promise });
}
Since $.getJSON('/users/current') returns a promise, might as well use that.
createCurrent: function() {
return $.getJSON('/users/current').then(function(data) {
return Dashboard.User.create(data);
});
}
Then you need to keep in mind that createCurrent returns a promise, not the object itself so you will need to:
current: function() {
if (!this._current) {
var that = this;
this.fetching = true;
this.createCurrent().then(function(val) {
that.fetching = false;
that._current = val;
});
}
return this._current;
},

How do I call an action method on Controller from the outside, with the same behavior by clicking {{action}}

Please look at this code...
```
App.BooksRoute = Ember.Route.extend({
model: return function () {
return this.store.find('books');
}
});
App.BooksController = Ember.ArrayController.extend({
actions: {
updateData: function () {
console.log("updateData is called!");
var books = this.filter(function () {
return true;
});
for(var i=0; i<books.length; i++) {
//doSomething…
}
}
}
});
```
I want to call the updateData action on BooksController from the outside.
I tried this code.
App.__container__.lookup("controller:books").send('updateData');
It works actually. But, in the updateData action, the this is different from the one in which updateData was called by clicking {{action 'updateData'}} on books template.
In the case of clicking {{action 'updateData'}}, the this.filter() method in updateData action will return books models.
But, In the case of calling App.__container__.lookup("controller:books").send('updateData');, the this.filter() method in updateData action will return nothing.
How do I call the updateData action on BooksController from the outside, with the same behavior by clicking {{action 'updateData'}}.
I would appreciate knowing about it.
(I'm using Ember.js 1.0.0)
You can use either bind or jQuery.proxy. bind is provided in JS since version 1.8.5, so it's pretty safe to use unless you need to support very old browsers. http://kangax.github.io/es5-compat-table/
Either way, you're basically manually scoping the this object.
So, if you have this IndexController, and you wanted to trigger raiseAlert from outside the app.
App.IndexController = Ember.ArrayController.extend({
testValue : "fooBar!",
actions : {
raiseAlert : function(source){
alert( source + " " + this.get('testValue') );
}
}
});
With bind :
function externalAlertBind(){
var controller = App.__container__.lookup("controller:index");
var boundSend = controller.send.bind(controller);
boundSend('raiseAlert','External Bind');
}
With jQuery.proxy
function externalAlertProxy(){
var controller = App.__container__.lookup("controller:index");
var proxySend = jQuery.proxy(controller.send,controller);
proxySend('raiseAlert','External Proxy');
}
Interestingly this seems to be OK without using either bind or proxy in this JSBin.
function externalAlert(){
var controller = App.__container__.lookup("controller:index");
controller.send('raiseAlert','External');
}
Here's a JSBin showing all of these: http://jsbin.com/ucanam/1080/edit
[UPDATE] : Another JSBin that calls filter in the action : http://jsbin.com/ucanam/1082/edit
[UPDATE 2] : I got things to work by looking up "controller:booksIndex" instead of "controller:books-index".
Here's a JSBin : http://jsbin.com/ICaMimo/1/edit
And the way to see it work (since the routes are weird) : http://jsbin.com/ICaMimo/1#/index
This solved my similar issue
Read more about action boubling here: http://emberjs.com/guides/templates/actions/#toc_action-bubbling
SpeedMind.ApplicationRoute = Ember.Route.extend({
actions: {
// This makes sure that all calls to the {{action 'goBack'}}
// in the end is run by the application-controllers implementation
// using the boubling action system. (controller->route->parentroutes)
goBack: function() {
this.controllerFor('application').send('goBack');
}
},
};
SpeedMind.ApplicationController = Ember.Controller.extend({
actions: {
goBack: function(){
console.log("This is the real goBack method definition!");
}
},
});
You could just have the ember action call your method rather than handling it inside of the action itself.
App.BooksController = Ember.ArrayController.extend({
actions: {
fireUpdateData: function(){
App.BooksController.updateData();
}
},
// This is outside of the action
updateData: function () {
console.log("updateData is called!");
var books = this.filter(function () {
return true;
});
for(var i=0; i<books.length; i++) {
//doSomething…
}
}
});
Now whenever you want to call updateData(), just use
App.BooksController.updateData();
Or in the case of a handlebars file
{{action "fireUpdateData"}}

Ember - how to create and bind a Checkbox controller?

This question is linked to the answer given here.
Having a checkbox in a view
App.RoleCheckbox = Em.Checkbox.extend({
userRolesBinding: 'parentView.user.roles', // Points to the roles of the user
checked: function () {
var userRoles = this.get('userRoles');
return userRoles.contains(this.get('content'));
}.property('content', 'userRoles.#each'),
click: function (evt) {
//do something
var controller = this.get("controller");
controller.clicked(evt);
}
});
I would like that the click function calls the clicked function from the RoleCheckboxController:
App.RoleCheckboxController = Em.Controller.extend({
clicked: function(evt){
//Really do the thing
}
});
But this does not work. How could I fix this ?
JSFiddle: http://jsfiddle.net/3fMpD/
You can instantiate and associate the controller to the view using the correct naming conventions.
For example, this would associate the controller to the view:
// Instead of App.RoleCheckBoxController
App.ApplicationController = Ember.Controller.extend( /* ... */ );
App.ApplicationView = Ember.View.extend( /* .. */ );
JSFiddle: http://jsfiddle.net/YL5rQ/
#c4p is definitely right and the problem there is that your controller is not being created, and furthermore App.RoleCheckbox has no way of knowing it should use App.RoleCheckboxController as its controller.
I am not quite sure if this is the most Ember-y way of doing this but you can set the controller in the init (constructor function) of the Checkbox view, and then just make sure you send to the controller all the properties it needs to work with:
App.RoleCheckbox = Em.Checkbox.extend({
init: function(){
this._super();
this.set('controller', new App.RoleController());
},
userRolesBinding: 'parentView.user.roles',
checked: function () {
var userRoles = this.get('userRoles');
return userRoles.contains(this.get('content'));
}.property('content', 'userRoles.#each'),
click: function (evt) {
this.get('controller').send('clicked',this.checked, this.content);
}
});
And the controller's code (just changing the parameters used in the function);
App.RoleCheckboxController = Em.ObjectController.extend({
clicked: function(checked,role){
var userRoles = App.User.roles;
console.log("userRoles = ", userRoles);
console.log("role = ", role);
console.log("will be: ", !checked ? "removed" : "added");
if (checked) {
userRoles.pushObject(role);
} else {
userRoles.removeObject(role);
}
console.log("updated userRoles = ", userRoles);
}
});
Working fiddle here: http://jsfiddle.net/cfSwq/3/
Hope this helps!
Your App.RoleCheckboxController is never created. The way you have things set up there will only be an instance of ApplicationController.
You can move the logic back into the view's click event to have everything work:
App.RoleCheckbox = Em.Checkbox.extend({
userRolesBinding: 'parentView.user.roles',
checked: function () {
var userRoles = this.get('userRoles');
return userRoles.contains(this.get('content'));
}.property('content', 'userRoles.#each'),
click: function (evt) {
console.log("event triggered:", evt);
//var controller = this.get("controller");
//controller.clicked(evt);
var isPresent = this.get('checked'),
userRoles = this.get('userRoles'),
role = this.get('content');
console.log("userRoles = ", userRoles);
console.log("role = ", role);
console.log("will be: ", isPresent ? "removed" : "added");
if (!isPresent) {
userRoles.pushObject(role);
} else {
userRoles.removeObject(role);
}
}
});
Updated JSFiddle