Loopback: How to add afterRemote of a model to another model - loopbackjs

I have Notification model which looks like this
"use strict";
module.exports = function(Notification) {
};
And I have another model which is Post:
"use strict";
module.exports = function(Post) {
Post.prototype.postLike = function(options, cb) {
this.likes.add(options.accessToken.userId);
cb(null, "sucess");
};
Post.remoteMethod("postLike", {
isStatic: false,
accepts: [{ arg: "options", type: "object", http: "optionsFromRequest" }],
returns: { arg: "name", type: "string" },
http: { path: "/like", verb: "post" }
});
}
What I want is to add afterRemote method of Post inside of notification model ?
Is it possible in loopback ?
It should looks like :
"use strict";
module.exports = function(Notification) {
var app = require("../../server/server.js");
var post = app.models.Post;
post.afterRemote('prototype.postLike', function(context, like, next) {
console.log('Notification after save for Like comment');
});
};
But this does not work.
NOTE: I can do it Post model itself, but I want to add all of my notification logic in Notification model for simplification and future customization.

You can use events to do.
Loopback application emits started event when it started after all boot scripts loaded here
and in Notification model do like this :
"use strict";
module.exports = function(Notification) {
var app = require("../../server/server.js");
app.on('started', function(){
var post = app.models.Post;
post.afterRemote('prototype.postLike', function(context, like, next) {
console.log('Notification after save for Like comment');
});
});
};
Or create a boot script and emit a custom event like 'allModelsLoaded'. So make sure the boot script is the last one to be run. Boot scripts run in alphabetic order by default. So make z.js and emit that custom event there then listen to that event in Notification model.

Loopback boot process first loads models, and then invoke boot scripts once all models have been loaded. If your aim is to consolidate things across models, then it is better to do this in a boot script, rather than in model.js file.

Related

How to define separate hidden props for GET and POST methods in loopback?

I want some props to be visible for GET method in loopback explorer, but I don't want to show them for POST method, for e.g. id property. How it can be done in loopback?
There are no built-in methods for this.
You need to do it in after remote for each remote method you want to be different from the default.
Model.afterRemote('GetMethod', function(ctx, instance, next){
var instance = ctx.result;
//reshape it
ctx.result = instance;
next();
});
UPDATE
If you want to affect this in explorer component so you need to create separate models with null datasource just for showing schema and use that in definition of remote method.
Model.remoteMethod('GetMethod', {
accepts: [
{
arg: 'req',
type: 'Object',
required: true,
http: {source: 'req'}
}
],
returns: {root: true, type: 'ModelDTOForSHow'},
http: {verb: 'get', status: 200, path: '/getter'}
});
And in ModelDTOForShow you hide some props and in another one some other props

emberjs find then filter

In emberjs, considering the following data
(only showing 1 record, normally there would be multiple records):
{ "service": [{
"service-_id":"service_5606ece79bdb05546479739866",
"service-_rev":"5-62dc477c13ef3ea92869bcdf1a67f1a6",
"service-company-name":"ABC co.",
"service-address":"1 2 3 Main Street",
"service-address-line-2":"",
"service-city":"asfd",
"service-state-current":"NY",
"service-zip":"12345",
"service-phone":"111",
"service-fax":"",
"service-email":"asdf#adsf.com",
"service-category-current":"web",
"service-type":"service",
"id":"service_5606ece79bdb05546479739866"
}]}
If I want to return all the records, I can simply do this:
App.ServicesRoute = Ember.Route.extend({
model: function(){
return this.store.find('service');
}
});
However, let's say I want to return all the records that have the current category as 'web'. So in the example data, there is this key: service-category-current
How would I adjust my model to find 'service' then filter where service-category-current = 'web' ?
The best way would be to make your API backend handle query params you send to it (so your records would be filtered on a backend, preferably query params could be used to query the database), so response from server would return only records that match your query. Example store.query call:
this.store.query('service', {
'service-category-current': 'web'
});
Which results in fetching records from URL:
http://api.com/services?service-category-current=web
And you're done. But, if you can't refactor your backend, you could filter records client-side:
model() {
return new Ember.RSVP.Promise(resolve => {
this.store.findAll('service').then(services => {
resolve(services.filterBy('service-category-current', 'web'));
});
});
}
Not ES2015 + using Ember.RSVP.Promise instead of native Promise (maybe will help you with Safari issue):
model: function() {
var that = this;
return new Ember.RSVP.Promise(function(resolve) {
that.store.findAll('service').then(function(services) {
resolve(services.filterBy('service-category-current', 'web'));
});
});
}

Providing a web app with real-time updates for domain entities from a Spring backend

On the server side, I use Spring 4. On the browser side, I use Ember.js.
My application has entity classes such as Person and Product. These entities are in use both on the server and browser, and are modeled identically. For example:
//this is on the server side
public interface Person {
String getId();
String getFirstName();
void setFirstName(String firstName);
String getLastName();
void setLastName(String lastName);
}
//this is on the browser side, modeled with Ember Data
App.Person = DS.Model.extend({
// id: DS.attr("string"), //property 'id' will be provided by Ember automatically
firstName: DS.attr("string"),
lastName: DS.attr("string")
});
I have the requirement to keep entities in sync between the server and the browser. So for example, when a Person's firstName property changes on the server side, this change shall be pushed to all interested browsers in real time.
I investigated Spring's WebSocket support, and after getting comfortable with Spring's "Hello WebSocket" example, I got convinced that using this technology is the right approach for my requirement.
As WebSocket/STOMP is quite low-level, I am in search for a solution that builds on top of this technology and provides observer pattern-like behavior between the browser (role of entities here would be observer) and the server (role of entities here would be subject/observable).
As I couldn't find an existing solution to this "keeping-entities-in-sync" challenge (neither a solution within Spring nor some third-party library), I want to build my own solution, yet the design already poses interesting questions, such as:
What should the protocol look like? Once a change happened, should the server send a minimal frame, only including the entity type and its ID? (e.g. once any property of a Person with ID "3" changes, then send {"type": "Person", "id": "3"} to all interested clients)
Are there real-life limitations on the number of entities that can be subscribed to? During a session, a single browser may come in contact with hundreds of Products.
I am interested in hearing which solutions have proven to successfully keep a Spring-based server's entities in sync with a JavaScript client's proxy entities (not necessarily Ember.js).
I'm going to give you a high level overview because giving all details requieres a blog post.
What You'll Need
Socket.io server.
Socket.io client, or one of their new libraries that pushes events to Redis. Ruby Socket.io client, Java Socket.io client
Hooks on all your server side data isolators to get notified of updates.
Observer on all of your Ember Data models.
You Have Two Options for Protocol
Option One: Only Event
Payload:
{
meta: {
type: 'NAME_OF_YOUR_MODEL'
id: 'ID_OF_UPDATED_MODEL'
}
}
Code:
window.socket = io()
App.ApplicationRoute = Ember.Route.extend({
activate: function(){
var store = this.store;
socket.on('recordUpdated', function(payload){
store.find(Ember.get(payload, 'meta.type'), Ember.get(payload, 'meta.id'));
};
}
}
App.Model = DS.Model.extend({
lastUpdate: function(){
var payload = {
id: this.get('id'),
type: this.constructor.toString()
};
socket.emit('recordUpdated', payload);
this.save(); // You should save the record to get it in saved state.
return payload;
}.observes('isDirty')
});
Option Two: JSON Representation of Model, With Event
Payload:
{
"meta": {
"type": "person"
"id": "18"
},
"persons": [
{"id": "18", "firstName": "Yahuda", "lastName": "Katz"}
]
}
Code:
window.socket = io();
App.ApplicationRoute = Ember.Route.extend({
activate: function(){
var store = this.store;
socket.on('recordUpdated', function(payload){
store.pushPayload(payload);
};
}
});
App.Model = DS.Model.extend({
lastUpdate: function(){
var payload = this.toJSON();
socket.emit('recordUpdated', payload);
this.save();
return payload;
}.observes('isDirty')
});
Notes
You can use Stalkable for better observation of all properties. This also eliminates need to call save as part of lastUpdate property.

Ember-data 1.0 Beta 2 Issue with transition after save for the first time

I am creating a sample application using ember 1.0 and ember-data 1.0 Beta 2.0. with RESTAdapter to connect to backend server.
When I try to save a record, it always invoke failure handler at the first submission. But the record actually gets saved at backend without fail. From the server the response for submission contains the created entity set with id.
When I try to debug the code in developer tools, it actually goes through the code for route transition, but then it returns back to Add view before completing the transition. It seems to be some callbacks from jQuery global event handlers are causing the problem.
Here is the code I am using
App.AddResourceRoute = App.ResourceManagerRoute.extend(
{
model: function () {
return this.store.createRecord('Resource');
},
actions: {
save: function () {
this.modelFor('AddResource').save().then(function (resource) {
App.Router.router.transitionToRoute('Resources');
}, function (reason) {
alert('Failure reason:' + reason);
});
}
}
});
Please help me to find out what is wrong with my code.
Thanks in advance
I think that you are receiving an error from App.Router.router.transitionToRoute('Resources'); invocation, try to update to the following:
App.AddResourceRoute = App.ResourceManagerRoute.extend(
{
model: function () {
return this.store.createRecord('Resource');
},
actions: {
save: function () {
var route = this;
this.modelFor('AddResource').save().then(function (resource) {
route.transitionTo('Resources');
}, function (reason) {
alert('Failure reason:' + reason);
});
}
}
});
You should use transitionTo(someRoute) inside of a route, or transitionToRoute(someRoute) when inside of a controller

Ember.JS: Your server returned a hash with the key id but you have no mapping for it

Consider this Ember JS Model:
App.User = DS.Model.extend({
firstName: DS.attr('string')
});
I am able to successfully save the model on the server using this as an XHR request:
{
"user": {
"first_name":"dude"
}
}
but for some reason it gives me an error while returning this XHR response:
{
"id":1,
"user":{
"first_name":"dude"
},
"createdAt":"2013-04-12T03:13:52.382Z",
"updatedAt":"2013-04-12T03:13:52.382Z"
}
The error says: Your server returned a hash with the key id but you have no mapping for it
Ember expects the output to look like:
{
"user": {
"id":1,
"first_name":"dude",
"createdAt":"2013-04-12T03:13:52.382Z",
"updatedAt":"2013-04-12T03:13:52.382Z"
}
}
I think the problem lies in the request itself, but I'm not sure.
Note that I'm using the Sails API as my backend.
You can use a controller to marshal the data format to whatever you need-- but this raises an interesting question about adding support for different front-end conventions to the API blueprints. Right now, Sails.js API blueprints support Backbone out of the box, but obviously that doesn't do you a lot of good if you're using Ember :) I created an issue for that here https://github.com/balderdashy/sails/issues/317.
Here's a hacky example of how you'd use a custom controller to send back data in this format using Sails today:
// api/controllers/UserController.js
module.exports = {
// Create action: (e.g. using default route, you'd POST to /user/create)
create: function (req,res) {
// Grab attributes from request using Ember conventions
var newAttributes = req.param('user');
// Create the user object in the datastore
User.create(newAttributes, function (err, newUser) {
// If there was an error, handle it
if (err) return res.send(err,500);
// Respond with the user object using Ember conventions
res.json({
user: newUser
});
});
}
};
That's a weirdly formatted JSON response. Do you have access to the server?
Ember expects the response as a a hash with root keys
{
"user": {
"id":1,
"first_name":"dude",
"createdAt":"2013-04-12T03:13:52.382Z",
"updatedAt":"2013-04-12T03:13:52.382Z"
}
}