how to return values from actions in emberjs - ember.js

how to return some value from actions??
I tried this:
var t = this.send("someAction", params);
...
actions:{
someAction: function(){
return "someValue";
}
}

actions don't return values, only true/false/undefined to allow bubbling. define a function.
Ember code:
send: function(actionName) {
var args = [].slice.call(arguments, 1), target;
if (this._actions && this._actions[actionName]) {
if (this._actions[actionName].apply(this, args) === true) {
// handler returned true, so this action will bubble
} else {
return;
}
} else if (this.deprecatedSend && this.deprecatedSendHandles && this.deprecatedSendHandles(actionName)) {
if (this.deprecatedSend.apply(this, [].slice.call(arguments)) === true) {
// handler return true, so this action will bubble
} else {
return;
}
}
if (target = get(this, 'target')) {
Ember.assert("The `target` for " + this + " (" + target + ") does not have a `send` method", typeof target.send === 'function');
target.send.apply(target, arguments);
}
}

I had the same question. My first solution was to have the action put the return value in a certain property, and then get the property value from the calling function.
Now, when I need a return value from an action, I define the function that should be able to return a value seperately, and use it in an action if needed.
App.Controller = Ember.Controller.extend({
functionToReturnValue: function(param1, param2) {
// do some calculation
return value;
},
});
If you need the value from the same controller:
var value = this.get("functionToReturnValue").call(this, param1, param2);
From another controller:
var controller = this.get("controller"); // from view, [needs] or whatever
var value = controller.get("functionToReturnValue").call(controller, param1, param2); // from other controller
The first argument of the call() method needs to be the same object that you are running the return function of; it sets the context for the this reference. Otherwise the function will be retrieved from the object and ran from the current this context. By defining value-returning functions like so, you can make models do nice stuff.
Update I just found this function in the API that seems to do exactly this: http://emberjs.com/api/#method_tryInvoke

Look this example:
let t = this.actions.someAction.call(this, params);

Try
var t = this.send("someAction", params);
instead of
vat r = this.send("someAction", params);

Just use #set for set value which you want to return
actions:{
someAction: function(){
// return "someValue";
this.set('var', someValue);
}
}

Related

Ember: Setting a component object value within a promise

I have component with a couple of properties, using a promise in the willRender hook to try and create a (pagination) object:
export default Ember.Component.extend({
pagination:null,
testing:null, // to check if this.set is ok within the promise!
willRender() {
let page = {};
let model = this.get('data');
model.get('products').then(relatedItems => {
let maxRecords = relatedItems.get('length');
relatedItems.forEach(function(item,index) {
if (item.get('slug') === itemModel.get('id')) {
if (index === 0) {
page.Prev = null;
page.Next = relatedItems.objectAt(index+1).get('slug');
}
else if (index+1 === maxRecords) {
page.Prev = relatedItems.objectAt(index-1).get('slug');
page.Next = null;
}
else {
page.Prev = relatedItems.objectAt(index-1).get('slug');
page.Next = relatedItems.objectAt(index+1).get('slug');
}
}
});
this.set('testing','hello world');
console.log(this.get('testing')); // hello world
this.set('pagination',page);
console.log(this.get('pagination')); // Object {Prev: "product-1", Next: "product-2"}
},reject => {
console.log('error '+reject);
});
}
})
In my template
{{testing}} // prints hello world
However, if I try and access {{pagination}} eg {{log pagination}}, the browser crashes with a loop printing out the object to the console.
I don't know where I'm going wrong here - any help much appreciated!
It's likely you are triggering the template to rerender causing willRender to fire over and over which causes an infinite loop in your code.
willRender is a non-standard place to do this code, init would be more standard since it only fires on initialization of the component. Even better would be to use
myInit: Ember.on('init', function(){
....
})`
instead of overriding willRender on the object.
try to check whether Object is present at specific position. i think its going undefined during iteration of for loop. try to ensure that ObjectAt is not returning undefined or null value during running of for loop.
relatedItems.forEach(function(item,index) {
if (item.get('slug') === itemModel.get('id')) {
if (index === 0) {
page.Prev = null;
if(relatedItems.objectAt(index+1) ! = undefined) {
page.Next = relatedItems.objectAt(index+1).get('slug');
}else{
page.Next == null;
}
}
else if (index+1 === maxRecords) {
if(relatedItems.objectAt(index-1) ! = undefined) {
page.Prev = relatedItems.objectAt(index-1).get('slug');
}else{
page.Prev = null;
}
page.Next = null;
}
else {
if(relatedItems.objectAt(index-1) ! = undefined) {
page.Prev = relatedItems.objectAt(index-1).get('slug');
}else{
page.Prev = null;
}
if(relatedItems.objectAt(index+1) ! = undefined) {
page.Next = relatedItems.objectAt(index+1).get('slug');
}else{
page.Next = null;
}
}
}
Please ensure that Object at is returning object.
There seems to be a few problems here, would be interested to know what your console errors are.
You don't seem to have defined itemModel so don't know how you're referencing that.
Also you can't access this from within a .then. You need to do something like this to set a component variable.
var _this = this;
promise().then(function(results) {
_this.set('testing', 'hello world');
});
you are not using , after testing:null
there should be , after testing property like that
pagination:null,
testing:null, // i use ',' after testing: null property
try to use your pagination code under init hook rather than willrender hook
init() {
//you code
},

How do I return the result of Ember.RSVP.all from a function?

The function below needs to return a true or false. Inside the then call at the bottom of the function, I get the value I need from result.isAny('hasOverride') but I don't know how to return this value from the hasRoomUpcharge function. How do I do this? I thought adding return in front of Ember.RSVP.all would do it, but it doesn't work. Please help!
hasRoomUpcharge: function(roomRates, defaultRoomTypeCode){
var defaultRoomRates = roomRates.findBy('roomTypeCode', defaultRoomTypeCode);
var nonDefaultRoomRates = roomRates.rejectBy('roomTypeCode', defaultRoomTypeCode);
var nonDefaultRoomMasters = this.modelFor('propertyPricing').roomTypeMasters.rejectBy('isDefault');
var promises = [];
var promise;
nonDefaultRoomMasters.forEach(function(roomMaster){
var roomTypeCode = roomMaster.get('roomTypeCode');
promise = roomMaster.get('roomRateUpcharge').then(function(roomRateUpcharge){
var currentRoomMasterOnePersonPrice = nonDefaultRoomRates.findBy('roomTypeCode', roomTypeCode).onePersonPrice;
var defaultRoomMasterOnePersonPrice = defaultRoomRates.onePersonPrice;
var roomUpcharge = roomRateUpcharge.get('onePersonRateUpcharge');
if(currentRoomMasterOnePersonPrice != defaultRoomMasterOnePersonPrice + roomUpcharge){
return { hasOverride: true };
}
else {
return { hasOverride: false };
}
});
promises.push(promise);
});
return Ember.RSVP.all(promises).then(function(result){
return result.isAny('hasOverride');
});
},
You cannot return the result from the hasRoomUpcharge function, as it returns before promises resolve (i.e. before the result is even known).
You are in fact doing it the right way - returning a promise from the hasRoomUpcharge function. When invoking this function, you should consume the returned promise, e.g.
this.hasRoomUpcharge(1, 3).then(function(result) {
console.log(result); //do something with the result
}

Return from a beforeRemote

I have two questions regarding "beforeRemote" method of loopback-
How do I get hold of model methods inside beforeRemote method? I mean inside beforeRemote I want to invoke (lets say) "upsert" method of the model.
How do I return invocation from beforeRemote? By return I mean instead of hitting the target invoked method the execution will return from beforeRemote method.
My code -
Installation.beforeRemote("create", function (context, result, next) {
var data = context.req.body;
console.log("ImEI" + data.imei);
data.vendor = "jahid";
var self = this;
var filter = {where: {imei: data.imei}};
//the self here point to global object. but i want self to point to model
self.findOne(filter, function (err, result) {
if (result) {
data.id = result.id;
self.upsert(data, function(err, result){
if(err){
next(err);
} else if(result) {
//here i want to send a valid response back to client with 200 and body as my model.
next(data);
}
});
return;
}
next();
});
});
You have access to the Installation model from the module.exports declaration:
module.exports = function(Installation) {
...
Installation.upsert...
...
}
You have access to the response object from the context object. So you could just respond with something like context.res.send('hello world') and not call next().

How to properly setup a store that acts as a single pointer across your web app

I have a home grown store that has a simple identityMap. When I return an array of models from this and bind it to a controllers "model" it reflects what you'd expect
the first time you hit a route it reflects this in the template as
you'd expect
But later if I get this same store instance (it's a singleton) and push an object into the identityMap it doesn't automatically update the previous template
The store itself is super basic (no relationships/ just push objects and get by id)
function buildRecord(type, data, store) {
var containerKey = 'model:' + type;
var factory = store.container.lookupFactory(containerKey);
var record = factory.create(data);
var id = data.id;
identityMapForType(type, store)[id] = record;
return record;
}
function identityMapForType(type, store) {
var typeIdentityMap = store.get('identityMap');
var idIdentityMap = typeIdentityMap[type] || {};
typeIdentityMap[type] = idIdentityMap;
return idIdentityMap;
}
var Store = Ember.Object.extend({
init: function() {
this.set('identityMap', {});
},
push: function(type, data) {
var record = this.getById(type, data.id);
if (record) {
record.setProperties(data);
} else {
record = buildRecord(type, data, this);
}
return record;
},
getById: function(type, id) {
var identityMap = identityMapForType(type, this);
return identityMap[id] || null;
}
getEverything: function(type) {
var identityMap = identityMapForType(type, this);
var keys = Object.keys(identityMap);
var values = [];
for (var i = 0; i < keys.length; i++)
{
var val = identityMap[keys[i]];
values.push(val);
}
return values;
}
});
Ember.onLoad('Ember.Application', function(Application) {
Application.initializer({
name: "store",
initialize: function(container, application) {
application.register('store:main', Store);
application.inject('controller', 'store', 'store:main');
application.inject('route', 'store', 'store:main');
}
});
});
In my model hook (in the find all route lets say) I simply query for each item and push them into the store
//inside my model find method lets say ...
find: function(store) {
var url = "/api/foo";
$.getJSON(url, function(response) {
response.forEach(function(data) {
var model = store.push("foo", data);
}
}
return store.getEverything("foo");
}
So I assumed my controllers' model was this bound array (using a single pointer in memory for this array of models)
Yet when I do this inside a controller submit action it won't re-render that prev view (to show the new item that was added to that store's array)
actions: {
submit: function() {
var foo = {}; // assume this is a real json response or js object
var store = this.get("store");
store.push("foo", foo);
}
}
Because of this today, I'm forced to get the parent controller and "set" / "push" this new object to it's content/model property :(
Anyone know what I'm doing wrong here?
I like homegrown solutions, they generally are easier to work with and meld around what you're working on.
So I'm actually surprised this part is working:
//inside my model find method lets say ...
find: function(store) {
var url = "/api/foo";
$.getJSON(url, function(response) {
response.forEach(function(data) {
var model = store.push("foo", data);
}
}
return store.getEverything("foo");
}
If I read through it I see you make an ajax call, and then return store.getEverything immediately after (without a guarantee that the ajax call has completed). Then inside of getEverything you create a new array called values then iterate the identity map linking up all of the currently available records and return that. At this point your store is unaware of this array going forward. So any changes to your store wouldn't get pushed out to the array, they might make it into the identity map, but it isn't feeding the getEverything array.
There are a couple of solutions, one would be to keep track of your everything array. That collection would be super cheap to build, more expensive to search, so keeping the identity map as well would be super beneficial. You could follow your same pattern, but one collection would be the map, whereas the other would be an array of everything.
Modified Build Record
function buildRecord(type, data, store) {
var containerKey = 'model:' + type;
var factory = store.container.lookupFactory(containerKey);
var record = factory.create(data);
var id = data.id;
identityMapForType(type, store)[id] = record;
everythingArrayForType(type, this).pushObject(record);
return record;
}
Copy paste, possibly could be refactored
function everythingArrayForType(type, store) {
var everythingArrays = store.get('everythingArrays');
var arr = everythingArrays[type] || [];
everythingArrays[type] = arr;
return arr;
}
Slightly modified Store
var Store = Ember.Object.extend({
init: function() {
this.set('identityMap', {});
this.set('everythingArrays', {});
},
push: function(type, data) {
var record = this.getById(type, data.id);
if (record) {
record.setProperties(data);
} else {
record = buildRecord(type, data, this);
}
return record;
},
getById: function(type, id) {
var identityMap = identityMapForType(type, this);
return identityMap[id] || null;
}
getEverything: function(type) {
return everythingArrayForType(type, this);
}
});

bound {{if}} statement in ember not reevaluated

The following bound {{if}} statement is not reevaluated when the object 'model' it is bound to changes. This is contrary to my expectation and what I thought was the purpose of binding an if statement to an object. Am I doing something wrong?
if-data.js:
Handlebars.registerHelper("ifData", function(property, fn)
{
var context = (fn.contexts && fn.contexts[0]) || this;
var args = [property];
var canAction = function(can_args)
{
alert('I was called for '+can_args[0].get('id'));
return true;
};
// Resolve actual values for all params to pass to the conditional callback
var normalizer = function() {
return Ember.Handlebars.resolveParams(context, args, fn);
};
return Ember.Handlebars.bind.call(context, 'content', fn, true, canAction, normalizer, args);
});
show.hbs:
{{#ifData model }}
<h1> Showme </h1>
{{/ifData}}
When testing I am updating the bound obejct using this in the console:
App.__container__.lookup('store:main').find('post',1).then(function(model){
model.set('title','mydream20');
model.save();
});