In the ember guides on models it says (1) :
Ember Data is also designed to work with streaming APIs like socket.io, Firebase, or WebSockets. You can open a socket to your server and push changes to records into the store whenever they occur.
I tried writing a custom adapter that uses a websocket but i'm not getting very far. I couldn't find any working examples anywhere.
This is my totally unfinished prototype:
DS.WSAdapter = DS.Adapter.extend(Ember.Evented, {
websocket: undefined,
init: function () {
if(this.websocket === undefined)
{
this.websocket = new WebSocket('ws://localhost:8887');
this.websocket.onopen = function(e) {
console.log("Connection established!");
};
this.websocket.onmessage = function(e) {
// What to do here?
};
}
this._loadData();
},
//....
Can somone please help me with the websocket adapter?
My main problem is that I have no clue what to do when the websocket.onmessage() gets executed. I can't even access the store (using DS.get('defaultStore')) or anything
I don't have experience working directly with sockets in Ember, however I have recently completed an Ember Data + Firebase adapter which should follow very similar methodologies.
You should, at the least, be able to use it as inspiration:
https://github.com/sandersonet/ember-data-firebase
Firebase does provide an additional layer of abstraction from the sockets underneath, but the methodologies are very similar.
Have a look at http://emberjs.com/guides/models/frequently-asked-questions/#toc_how-do-i-inform-ember-data-about-new-records-created-on-the-backend
Some applications may want to add or update records in the store
without requesting the record via store.find. To accomplish this you
can use the DS.Store's push, pushPayload, or update methods. This is
useful for web applications that have a channel (such as SSE or Web
Sockets) to notify it of new or updated records on the backend.
Basically, you need to deserialize data you receive in your onmessage hook and push new objects to the data store using store.push('model', record) or alternative methods.
Related
I want to periodically call an API from my service worker to send data stored in the localStorage. This data will be produced and saved in localStorage when a user browses my website. Consider it something like saving stats in localStorage and sending it periodically through the service worker. How should I do this? I understand that I can't access localStorage from the service worker and will have to use the postMessage API. Any help would be highly appreciated.
You cannot access localStorage (and also sessionStorage) from a webworker process, they result will be undefined, this is for security reasons.
You need to use postMessage() back to the Worker's originating code, and have that code store the data in localStorage.
You should use localStorage.setItem() and localStorage.getItem() to save and get data from local storage.
More info:
Worker.postMessage()
Window.localStorage
Pseudo code below, hoping it gets you started:
// include your worker
var myWorker = new Worker('YourWorker.js'),
data,
changeData = function() {
// save data to local storage
localStorage.setItem('data', (new Date).getTime().toString());
// get data from local storage
data = localStorage.getItem('data');
sendToWorker();
},
sendToWorker = function() {
// send data to your worker
myWorker.postMessage({
data: data
});
};
setInterval(changeData, 1000)
Broadcast Channel API is easier
There are several ways to communicate between the client and the controlling service worker, but localStorage is not one of them.
IndexedDB is, but this might be an overkill for a PWA that by all means should remain slim.
Of all means, the Broadcast Channel API results the easiest. It is by far much easier to implement than above-mentioned postMessage() with the MessageChannel API.
Here is how broadcasting works
Define a new broadcasting channel in both the service worker and the client.
const channel4Broadcast = new BroadcastChannel('channel4');
To send a broadcast message in either the worker or the client:
channel4Broadcast.postMessage({key: value});
To receive a broadcast message in either the worker or the client:
channel4Broadcast.onmessage = (event) => {
value = event.data.key;
}
I've been using this package called localforage that provides a localStorage-like interface that wraps around IndexedDB. https://github.com/localForage/localForage
You can then import it by placing it in your public directory, so it is served by your webserver, and then calling: self.importScripts('localforage.js'); within your service worker.
https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers says
Note: localStorage works in a similar way to service worker cache, but it is synchronous, so not allowed in service workers.
Note: IndexedDB can be used inside a service worker for data storage if you require it.
Also there is a bit of discussion here: How do I access the local storage using service workers in angular?
Stumbling over this question myself for a tiny webapp-project, I considered the following solution:
When the user is online, the data can be sent immediately. When he is offline, I use the SyncEvent.tag property to send information from the client to the serviceworker. Like this:
//offline_page.html (loads only, when user is offline)
button.onclick = function() {
//on click: store new value in localStorage and prepare new value for synchronization
localStorage.setItem("actual", numberField.value);
navigator.serviceWorker.ready.then(function(swRegistration) {
return swRegistration.sync.register('newval:'+numberField.value);
});
}
//sw.js
self.addEventListener('sync', function(event) {
//let's say everything in front of ':' is the option, everything afterwards is the value
let option = event.tag.replace(/(.*?)\:.*?$/, "$1");
let value = event.tag.replace(/.*?\:(.*?)$/, "$1");
if(option == "newval") {
event.waitUntil(
fetch("update.php?newval="+value)
.then(function(response) {
console.log(response);
})
);
}
});
update.php saves the new value to backend, as soon as the user goes online.
This won't give the service worker access to the localStorage, but it will send him every change made.
Just starting to get used to this syncing topic. So I would really be interested, wheather this is helpful and what others think about this solution.
Let's say I want to create a ToDo list using angular. I have a REST API that stores the items in db and provides basic operations. Now when I want to connect my angular app to the REST api I found two ways to do so following some tutorials online:
1.Data gets handled in backend only: A service gets created that has a getAllTodos function. This function gets directly attached to scope (e.g. to use it in ng-repeat):
var getAllTodos = function() {
//Todo: Cache http request
return $http...;
}
var addTodo = function(todo) {
//Todo: Clear cache of getAllTodos
$http...
}
2.Data gets handled in frontend too. There is a init function that initializes the todo variable in the service.
var todos = [];
var init = function() {
$http...
todos = //result of $http;
};
var getAllTodos = function() {
return todos;
};
var addTodo = function(todo) {
$http...
todos.push(todo);
}
I've seen both ways in several tutorials but I'm wondering what would be the best way? The first one is used in many tutorials where the author from the start has in mind to attach it to a REST API. The second one is often used when the author at first wants to create the functionality in the frontend and later wants to store data permanently using a backend.
Both ways have its advantages and disadvantages. The first one reduces code duplication in frontend and backend, the second one allows faster operations because it can be handled frontend first and the backend can be informed about changed afterwards.
//EDIT: Frontend is Angular.JS Client for me, backend the REST API on the server.
Separation of Frontend and Backend is often done for security reasons. You can locate Backend on a separate machine and then restrict access to that machine to only calls originating from the Frontend. The theory is that if the Frontend is compromised, the Backend has a lower risk factor. In reality if someone has compromised any machine on your network then the entire network is at risk on one level or another.
Another reason for a Backend/Frontend separation would be to provide database access through the Backend to multiple frontend clients. You have a single Backend with access to the DB and either multiple copies of the Frontend or different Frontends that access the Backend.
Your final design needs to take into account the possible security risks and also deployment and versioning. With the multiple-tier approach you can deploy individual Frontends without having to drop the Backend, and you can also "relocate" parts of the application without downtime. The more flexible the design of your application, the deployment may be more complicated. The needs of your application will depend on if you are writing a simple Blog or a large Enterprise application.
You need frontend and backend functionality. In frontend you preprape data which are being send and in the backend you make request to server.
I just read this post, and I do understand what the difference is. But still in my head I have the question. Can/Should I use it in the same App/Website? Say I want the AngularJs to fetch content and update my page, connecting to a REST api and all of that top stuff. But on top of that I also want a realtime chat, or to trigger events on other clients when there is an update or a message received.
Does Angular support that? Or I need to use something like Socket.io to trigger those events? Does it make sense to use both?
If someone could help me or point me to some good reading about that if there is a purpose for using both of them together.
Hope I'm clear enough. thank you for any help.
Javascript supports WebSocket, so you don't need an additional client side framework to use it. Please take a look at this $connection service declared in this WebSocket based AngularJS application.
Basically you can listen for messages:
$connection.listen(function (msg) { return msg.type == "CreatedTerminalEvent"; },
function (msg) {
addTerminal(msg);
$scope.$$phase || $scope.$apply();
});
Listen once (great for request/response):
$connection.listenOnce(function (data) {
return data.correlationId && data.correlationId == crrId;
}).then(function (data) {
$rootScope.addAlert({ msg: "Console " + data.terminalType + " created", type: "success" });
});
And send messages:
$connection.send({
type: "TerminalInputRequest",
input: cmd,
terminalId: $scope.terminalId,
correlationId: $connection.nextCorrelationId()
});
Usually, since a WebSocket connection is bidirectional and has a good support, you can also use it for getting data from the server in request/response model. You can have the two models:
Publisher/Subscriber: Where the client declares its interest in some topics and set handlers for messages with that topic, and then the server publish (or push) messages whenever it sees fit.
Request/response: Where the client sends a message with a requestID (or correlationId), and listen for a single response for that requestId.
Still, you can have both if you want, and use REST for getting data, and WebSocket for getting updates.
In server side, you may need to use Socket.io or whatever server side framework in order to have a backend with WebSocket support.
As noted in the answer in your linked post, Angular does not currently have built-in support for Websockets. So, you would need to directly use the Websockets API, or use an additional library like Socket.io.
However, to answer your question of if you should use both a REST api and Websockets in a single Angular application, there is no reason you can't have both standard XmlHttpRequest requests for interacting with a REST api, using $http or another data layer library such as BreezeJS, for certain functionality included in various parts of the application and also use Wesockets for another part (e.g. real time chat).
Angular is designed to assist with handling this type of scenario. A typical solution to would be to create one or more controllers to handle the application functionality and update your page and then creating separate Services or Factories that encapsulate the data management of each of your data end points (i.e. the REST api and the realtime chat server), which are then injected into the Controllers.
There is a great deal of information available on using angular services/factories for managing data connections. If you're looking for a resource to help guide you on how to build an Angular application and where data services would fit in, I would recommend checking out John Papa's AngularJS Styleguide, which includes a section on Data Services.
For more information about factories and services, you can check out AngularJS : When to use service instead of factory
I am using the inbuilt socket capabilities of SailsJS which have been working great. Now I've come across a hurdle that I can't find any info on.
My model is set-up to populate some of the attributes using waterline model associations for example:
getAll: function() {
return Issue.find()
.sort('createdAt DESC')
.populate('author')
.populate('group')
.populate('tags')
.then(function (models) {
return [models];
});
},
This is working fine when calling this method through the API. However in the case where an update is made via a put and Issue.publishUpdate(id, update); is called, the attributes are then sent un-populated to subscribed clients. This is not the behaviour I had expected as publishCreate, on the otherhand, sends populated results.
To workaround the hurdle I could manually populate the attributes before sending the publishUpdate, however this doesn't seem like the right way to do it with Sails? So before I go that route I would be interested to hear anyone else's thoughts or experience.
Sails v0.10.1
The blueprint API calls publishUpdate, but it is actually a standalone method that can be used anywhere:
http://sailsjs.org/#/documentation/reference/websockets/resourceful-pubsub/publishUpdate.html
publishUpdate broadcasts the data that you pass in, so to broadcast populated data, you'll need to pass it in.
I'm using v0.10.
Simple blueprint request for a messaging app (my model is named message)
var socket = io.connect('http://localhost:1337');
//initiate the request
socket.request('/message', {}, function(users) {});
socket.on('message', function(m){
console.log(m)
});
Using postman to to delete a message sends the delete to the client, however create does not send anything. Thank you.
UPDATE:
created this repo to reproduce the issues: https://github.com/jamescharlesworth/testProject
Take a look to http://beta.sailsjs.org/#/documentation/reference/Upgrading search that page for "socket" and there you'll find the differences on sails 0.10.
The most important one now is that instead of the "type of message", the first parameter of the "on" is the model. As it was previously used to call "message" to one of the types of messages, perhaps there is a remaining bug or something that filters your "message" model notifications when creating.
Have you tried naming your model different? Just to validate that the issue is with your model's name.
Additionally: if you want some transparent binding of models into an angular app, you can do it seamlessly with angular-sails-bind:
https://github.com/diegopamio/angular-sails-bind
I made it for my own project and then decided to put it as a separated library so everybody could benefit and I could have my first experience developing a bower package.
I hope it could help you.
In your example, you are using autosubscribe: ['destroy', 'create', 'update'],
while in sails 0.10 they have with "ed":
The events that were formerly create, update, and destroy are now created, updated, and destroyed.
That may be your issue.