Can loopback.io operation hooks be registered only once per model? - loopbackjs

I'm trying out operation hooks http://docs.strongloop.com/display/public/LB/Operation+hooks
Here is what I did in app code:
mymodel.observe('before save', doSomething);
//after some time elapses or based on an event we want to change the behaviour
mymodel.observe('before save', doSomethingElse);
var doSomething = function (ctx, next) {
//do something
next();
};
var doSomethingElse = function (ctx, next) {
//do something else
next();
};
When I test this code I find that always doSomething is executed which makes me wonder if the observer function can be registered only once per model or is it a bug?
If it is as per design, could you please tell the reason behind it?

Disclaimer: I am a core developer of LoopBack and the author of Operation hooks.
after some time elapses or based on an event we want to change the behaviour
The Operation hooks do not support unregistering of handler functions yet. Each call of observe() adds the handler to the list of methods invoked when a hook is triggered.
When I test this code I find that always doSomething is executed which makes me wonder if the observer function can be registered only once per model or is it a bug?
You can register multiple observers. Once you have registered doSomething, it will be always called. When you register doSomethingElse, it will be called too, after doSomething returns via next().

You can now unregister all observers with the clearObservers method and remove a single observer with the removeObserver method. See the ObserverMixin documentation.
According to the changelog, this feature was added in version 2.23.0 of the datasource juggler.

Related

How to trigger didReceiveAttrs in Ember component

Using version 2.17. I have an Ember component inside an /edit route with a controller:
// edit.hbs
{{ingredient-table recipe=model ingredients=model.ingredients}}
Inside my component, I am using a didRecieveAttrs hook to loop through ingredients on render, create proxy objects based off of each, and then build an ingredient table using those proxy objects.
// ingredient-table.js
didReceiveAttrs() {
let uniqueIngredients = {};
this.get('ingredients').forEach((ingredient) => {
// do some stuff
})
this.set('recipeIngredients', Object.values(uniqueIngredients));
}
I also have a delete action, which I invoke when a user wishes to delete a row in the ingredient table. My delete action looks like this:
// ingredient-table.js
deleteIngredient(ingredient) {
ingredient.deleteRecord();
ingredient.save().then(() => {
// yay! deleted!
})
}
Everything mentioned above is working fine. The problem is that the deleted ingredient row remains in the table until the page refreshes. It should disappear immediately after the user deletes it, without page refresh. I need to trigger the didReceiveAttrs hook again. If I manually call that hook, all my problems are solved. But I don't think I should be manually calling it.
Based on the docs, it is my understanding that this hook will fire again on page load, and on re-renders (not initiated internally). I'm having some trouble figuring out what this means, I guess. Here's what I've tried:
1) calling ingredients.reload() in the promise handler of my save in ingredient-table.js (I also tried recipe.reload() here).
2) creating a controller function that calls model.ingredients.reload(), and passing that through to my component, then calling it in the promise handler. (I also tried model.reload() here).
Neither worked. Am I even using the right hook?
I suppose recipeIngredients is the items listed in the table. If that is the case; please remove the code within didReceiveAttrs hook and make recipeIngredients a computed property within the component. Let the code talk:
// ingredient-table.js
recipeIngredients: Ember.computed('ingredients.[]', function() {
let uniqueIngredients = {};
this.get('ingredients').forEach((ingredient) => {
// do some stuff
})
return Object.values(uniqueIngredients)
})
My guess is didReceiveAttrs hook is not triggered again; because the array ingredients passed to the component is not changed; so attrs are not changed. By the way; do your best to rely on Ember's computed properties whenever possible; they are in the hearth of Ember design.

What loopback hook should I use?

I am trying to do the following thing:
I have a model, say myModel which has some method calculateSomething. I defined that function by writing something like this in the MyModel.js file:
MyModel.prototype.calculateSomething = function(cb){
...
return cb(null,result)
}
Now I want to include the result of calculateSomething in the json whenever an instance of MyModel is returned from the api.
How do I do this? I tried using the "loaded" hook, but I believe this hook gets executed before the MyModel instance is created, so I can't call the calculateSomehing method there.
EDIT: It turns out that I can just use the "loaded" hook. I can use the ctx.instance in the hook to get the object.
I was confused by the documentation :
"LoopBack invokes this hook after the connector fetches data, but before creating a model instance from that data". Is the documentation wrong or am I misunderstanding it?
How about using Remote Hooks (on mymodel.js):
// run before any static method eg. MyModel.find
MyModel.beforeRemote('*', function(ctx, myModel, next) {
myModel.calculateSomething(function(err, something) {
if (err) throw err
myModel.something = something
next()
})
});
OR
If you need to do it on object initialization phase (while operation hook loaded seems to be not working) maybe you can try the model hook afterInitialize assuming no async call invoked from calculateSomething:
MyModel.afterInitialize = function() {
this.something = this.calculateSomething(function(err, result) {
return result
})
}
OR
As discussed below, if you need do async call and/or want to have this logic on subclasses, I think you should consider implementing createSomething not as object/prototype method but as a mixins. I haven't tried this personally though but it looks quite suitable to your need.

Ember.JS A BEFORE() function for every controller action

Is it possible to create some sort of before() function for every action inside a controller?
I know I can do an init() for every action, but what if I have a some code I want to run for every thing? It would be way too redundant to create the same init() function for each action.
No, there is no way to run code before every action without explicitly calling it yourself. At one point there was an idea to lookup actions using get(), which would have allowed this, but it never got implemented. Not sure what your use case is, but if you really do need to call functionality before every action call, just call it yourself. It'll be much more readable than any clever hacks we could come up with.
You could possibly do something like this:
First make sure your controller extends the `Ember.TargetActionSupport' mixin.
Ember.Controller.extend(Ember.TargetActionSupport,{
...
});
This will allow you to trigger actions programmatically. See more about this: Ember.TargetActionSupport
Then you would make an entry point action that contains your common code and a call to trigger the proper action:
Ember.Controller.extend(Ember.TargetActionSupport,{
actions: {
common: function (actionName) {
//Do common logic here
this.set('foo', 'bar');
//And then trigger the intended action
this.triggerAction({
action: actionName,
target: this
});
},
otherAction: function () {
//Do some more logic here
this.set('bar', 'baz');
}
}
});
You would call this action from your template like this:
<button {{action 'common' 'otherAction'}}>Press Me</button>

Yii how to invoke an event handler on each request

Is it possible in Yii to invoke an event handler so that it executes on each controller action call.
Basically I have a RESTful application. On each request, currently, it explicitly calls an authentication function. What I want is the authentication function calls when any request is made.
What I did
class MyController extends RestController{
public function actionDosomething(){
$this->authenticate();// I don't want this line to be put in every controller action.
}
}
Your answer is the beforeAction callback. Place this in your main Controller file.
public function beforeAction($action) {
if(in_array($action, array( /* you list of actions */ )))
{
//do your thing
}
}
Another option (in my opinion the more Yii-like approach) is to write a filter and then apply it as desired using the filters method.
It will give you even more flexibility in the future:
http://www.yiiframework.com/doc/guide/1.1/en/basics.controller#filter

Catch app 'ready' event outside the app

I need to run some code after ember application got initialized. I don't want to invoke this code from App.ready to avoid tight coupling. It would be nice to have something like this:
App.on 'ready, -> console.log('do stuff')
But it won't work since Em.Application object is not subscribable and ready isn't really an event despite that docs said so
A simple way you can achieve this would be to extend your Application class with the Ember.Evented mixin:
App = Ember.Application.createWithMixins(Ember.Evented, {
ready: function() {
console.log('App ready');
this.trigger('appReady');
}
});
And you hook inside the ready event inside your app and trigger your custom event using this.trigger(...)
At this point you can use .on(...) to be notified when the event is triggered.
App.on('appReady', function() {
console.log('App already ready');
});
Example demo.
Hope it helps.
An other possibility may be to invoke your code from the resolve callback of the application.
App.then(function(app) {
console.log("App is resolved, so it's ready");
});
example stolen from #intuitivepixel ;) http://jsbin.com/AdOVala/66/edit
Edit/Note:
App.then() has been deprecated, see http://emberjs.com/deprecations/v1.x/#toc_code-then-code-on-ember-application:
As part of the Ember.DeferredMixin deprecation, using .then on an
Ember.Application instance itself has been deprecated.
You can use the ready hook or initializers to defer/advance readiness instead.