Updating component display from route - ember.js

To learn more about using services and components, I'm trying to set up a simple flash-message style service. Within my route, I'm saving a record and receiving either a json notice or json error from the server.
Since this is a behavior I'd eventually like to use app-wide, I'm using a simple service (injected into my routes) to handle displaying the message. Within a route, I'm able to call this.get('notification').displayMessage(msg).
Currently the displayMessage function within the service is just alerting the message because I'm stuck on how to create a component that the service can "update". How can a service communicate with a component so that I can send a message and display it from the component template?
profile/index route
user.save().then( (response) => {
//display response.notice in the app-notification component template
}, (response) => {
let errors = JSON.parse(response.errors);
//display response.error in the app-notification component template
this.get('notification').displayMessage(errors[0]);
}
service
import Ember from 'ember';
const { Service } = Ember;
export default Service.extend({
displayMessage(msg) {
alert("message ---> " + msg);
}
});
component
???
component template
<h2 class="alert">{{message}}</h2>
application template
{{app-notification message=message}}

In terms of accessing the service, I like the example that they using the docs: http://guides.emberjs.com/v2.1.0/applications/services/
The ShoppingCart service is injected into components that need to access the Cart.
Once you have access to the ShoppingCart you should be able to:
set a Notification in the service for User from your route error catch.
set up a Computed Property based on the Notification service (some sort of hasNotification method) on the Component hasError that can toggle the error message.
As soon as you set a notification your component will see that the property it is watching has changed and will show/hide your notification.

Related

Using Ember.JS store to query Laravel 8 REST API

I'm developing a personal trading card DB in Ember.JS with a Laravel 8 REST API... for fun? Currently working on a page to add cards to my DB in bulk. The user will select what deck they want to add cards to and the respective deck's card checklist will be displayed, where I can select what cards will be added. I'm stuck on displaying the appropriate card's for their respective deck.
I'm using the ember package Service
import { inject as service } from '#ember/service';
...
#service store;
Inside my controller, I'm calling an action that will update the variable/displayed cards.
#action updateModel() {
let x = this.store.query('card', {
year: {
eq : yearVar
}
});
...
}
This is the error info:
Uncaught (in promise)
Object { message: "Aborted",
name: "AbortError",
stack: "exports.DOMException#http://10.0.0.0:4200/assets/vendor.js:49690:28\nfetch/WHATWGFetch</xhr.onabort/<#http://10.0.0.0:4200/assets/vendor.js:49730:22\nsetTimeout handler*fetch/WHATWGFetch</xhr.onabort#http://10.0.0.0:4200/assets/vendor.js:49729:23\nEventHandlerNonNull*fetch/WHATWGFetch<#http://10.0.0.0:4200/assets/vendor.js:49728:11\ninitializePromise#http://10.0.0.0:4200/assets/vendor.js:47175:15\nPromise#http://10.0.0.0:4200/assets/vendor.js:47631:35\nfetch#http://10.0.0.0:4200/assets/vendor.js:49697:16\nexports.default#http://10.0.0.0:4200/assets/vendor.js:49801:30\n_fetchRequest#http://10.0.0.0:4200/assets/vendor.js:52788:16\najax#http://10.0.0.0:4200/assets/vendor.js:52753:35\nquery#http://10.0.0.0:4200/assets/vendor.js:52361:19\n_query/promise<#http://10.0.0.0:4200/assets/vendor.js:71800:61\ninvokeCallback#http://10.0.0.0:4200/assets/vendor.js:47150:17\nthen/<#http://10.0.0.0:4200/assets/vendor.js:47207:40\n#http://10.0.0.0:4200/assets/vendor.js:42711:62\ninvoke#http://10.0.0.0:4200/assets/vendor.js:41296:16\nflush#http://10.0.0.0:4200/assets/vendor.js:41211:19\nflush#http://10.0.0.0:4200/assets/vendor.js:41374:21\n_end#http://10.0.0.0:4200/assets/vendor.js:41815:34\nBackburner/this._boundAutorunEnd#http://10.0.0.0:4200/assets/vendor.js:41539:14\npromise callback*buildNext/<#http://10.0.0.0:4200/assets/vendor.js:41040:35\nflush#http://10.0.0.0:4200/assets/vendor.js:26782:5\n_scheduleAutorun#http://10.0.0.0:4200/assets/vendor.js:41981:14\n_ensureInstance#http://10.0.0.0:4200/assets/vendor.js:41972:14\nschedule#http://10.0.0.0:4200/assets/vendor.js:41665:19\n#http://10.0.0.0:4200/assets/vendor.js:42711:28\nthen#http://10.0.0.0:4200/assets/vendor.js:47207:14\n_query#http://10.0.0.0:4200/assets/vendor.js:71800:42\nquery#http://10.0.0.0:4200/assets/vendor.js:72905:32\nupdateModel#http://10.0.0.0:4200/assets/cam-card-comp.js:607:18\nEventListener.handleEvent*addEventListener#http://10.0.0.0:4200/assets/vendor.js:39057:15\ninstall#http://10.0.0.0:4200/assets/vendor.js:39194:23\ncommit/tag<#http://10.0.0.0:4200/assets/vendor.js:36926:25\ntrack#http://10.0.0.0:4200/assets/vendor.js:40488:7\ncommit#http://10.0.0.0:4200/assets/vendor.js:36924:42\ncommit#http://10.0.0.0:4200/assets/vendor.js:36999:19\ninTransaction#http://10.0.0.0:4200/assets/vendor.js:37019:13\n_renderRoots#http://10.0.0.0:4200/assets/vendor.js:6446:36\n_renderRootsTransaction#http://10.0.0.0:4200/assets/vendor.js:6490:14\n_renderRoot#http://10.0.0.0:4200/assets/vendor.js:6435:12\n_appendDefinition#http://10.0.0.0:4200/assets/vendor.js:6360:12\nappendOutletView#http://10.0.0.0:4200/assets/vendor.js:6350:12\ninvoke#http://10.0.0.0:4200/assets/vendor.js:41298:16\nflush#http://10.0.0.0:4200/assets/vendor.js:41211:19\nflush#http://10.0.0.0:4200/assets/vendor.js:41374:21\n_end#http://10.0.0.0:4200/assets/vendor.js:41815:34\nBackburner/this._boundAutorunEnd#http://10.0.0.0:4200/assets/vendor.js:41539:14\npromise callback*buildNext/<#http://10.0.0.0:4200/assets/vendor.js:41040:35\nflush#http://10.0.0.0:4200/assets/vendor.js:26782:5\n_scheduleAutorun#http://10.0.0.0:4200/assets/vendor.js:41981:14\n_end#http://10.0.0.0:4200/assets/vendor.js:41821:18\nBackburner/this._boundAutorunEnd#http://10.0.0.0:4200/assets/vendor.js:41539:14\npromise callback*buildNext/<#http://10.0.0.0:4200/assets/vendor.js:41040:35\nflush#http://10.0.0.0:4200/assets/vendor.js:26782:5\n_scheduleAutorun#http://10.0.0.0:4200/assets/vendor.js:41981:14\n_end#http://10.0.0.0:4200/assets/vendor.js:41821:18\nBackburner/this._boundAutorunEnd#http://10.0.0.0:4200/assets/vendor.js:41539:14\npromise callback*buildNext/<#http://10.0.0.0:4200/assets/vendor.js:41040:35\nflush#http://10.0.0.0:4200/assets/vendor.js:26782:5\n_scheduleAutorun#http://10.0.0.0:4200/assets/vendor.js:41981:14\n" }
vendor.js:41829:18
I checked the URL the ember app was sending to the API, it is as follows, and it's correct:
http:10.0.0.0:4000/cards?year[eq]=2022
Took this URL and pasted it into my browser, it successfully returned the appropriate checklist. This tells me I'm creating the query correctly and sending it correctly. Mozilla Firefox's inspector shows the network GET request was BLOCKED, and I'm given the EmberJS error.
if request is blocked it could be CORS problem (by default it accept only same origin but in you case fornt-end served on 4200 and api on 4000), in laravel 8 check config/cors.php and allow all origines * something like this <?phpreturn['paths'=>['api/*'],'allowed_methods'=>['*'],'allowed_origins'=>['*'],'allowed_origins_patterns'=>[],'allowed_headers'=>['*'],'exposed_headers'=>false,'max_age'=>false,'supports_credentials'=>false,];

Configure Ember's back-end to communicate with a RESTful service

There seems to be a lot of discussion on SO (e.g. these questions: A, B, C, D) and other sites (e.g the Ember docs) about configuring Ember to allow cross-origin requests. That's all fine and well, but I'd rather have Ember's back-end do the work of communicating with a remote service (Ember's server-side components to make the request, rather than the user's browser). Is this documented? Can someone provide an example?
I thought I would find it easy to modify the HTTP server backing the ember serve command. Instead, I used the --proxy flag from Ember's CLI. It allows you to use remote services to provide data.
For this to work, let's assume a remote server foo.com:3000 provides JSON data at the path /resource. Configure a controller to GET the data like so:
import Ember from 'ember';
function getRemoteResource(store){
var dfd = Ember.$.Deferred();
Ember.$.ajax('/resource')
.done(function(data){
store.createRecord('model', data);
dfd.resolve();
})
.fail(function(err){
dfd.reject(new Error("An error occurred getting data.", err));
});
return dfd.promise();
}
export default Ember.Controller.extend({
actions: {
getResource:function(){
var dataStore = this.store;
return getRemoteResource(dataStore);
}
}
});
Use a template like this to invoke the controller's action:
<h2>Remote data example</h2>
<button class="my-button" {{action 'getResource' }}>
Get resource
</button>
Assuming your code is on host bar.com, start ember like this : ember serve --proxy http://foo.com:3000. Then, open your browser to the appropriate page that loads the template (somewhere like http://bar.com:4200/index) , click the button and see that remote data is loaded.

What RESTAdapter expects on server responses and what requests should server expect?

I'm using Django REST Framework, not Rails (which seems to have several magical gems to make everything work swiftly with Ember) and I've been having some difficulties trying to figure out how Ember expects responses. I'm using Ember CLI, thus I'm also using Ember data.
The documentation states only the typical GET usage, when I'm simply retrieving an object or an array of objects. Documentation states:
The JSON payload should be an object that contains the record inside a root property
And about conventions:
Attribute names in your JSON payload should be the camelCased versions of the attributes in your Ember.js models.
No problem with that.
1. But how should the API respond when there are errors?
Ok, so documentation also states you could use ajaxError to check jqXHR status for an error and then return a populated DS.Error for the record. However, how should I return different kind of errors. For example, let's say the user session is now invalid and because of that the server couldn't delete a record as requested.
2. How will Ember submit requests?
I'm quite new to REST in general. I think Ember simply use the appropriate verb for the action it wants: GET, POST, PUT, DELETE. I think it's quite clear it will send all the model's field to POST a new one, but how about DELETE? Will Ember send all the record or just the ID to delete an object?
Generally you should be able to see the requests Ember makes by just opening your browser dev tools and seeing the network requests.
Ember data likes the api to respond with an errors hash, something like this:
{"errors":{"title":["can't be blank"]}}
Then as long as you define a function to handle the error case:
Ember.Controller.extend({
actions: {
deleteUser: function() {
var user = this.model;
function success() {
// do something cool?
}
function failure() {
user.rollback();
}
user.destroyRecord().then(success, failure);
}
}
});
then user.errors will be automatically populated and you can do an if user.errors in your template.

EmberJS and WebSocket | Best Approach?

I have a again which I can't answer for my self properly, maybe because of my lack in expierience with EmberJS.
I have to develop a management interface in EmberJS, using Symfony 2 for the backend, which should act and feel like a desktop application. So far so good, but since alot of people will work with the data inside this application, i would really like to use a WebSocket adapter implementation for EmberJS, since every connected client should always know about changes in entities immediately (or asap). I could write a WebSocket adapter for EmberJS but my problem here is that the WebSocket will do much more then RESTful operations, also the server will send messages without any EmberJS request (e.g. an entity changed and the server broadcasting this change to all clients). That means that i need a "command" structure on top of RESTful operations which, as far as my expierience goes, will not work with a pure DS Adapter.
For example:
Maybe i will trigger a controller method that will send a websocket message like this:
{command: "say", parameters: {message: "Hello guys!"} }
This command is not Entity (DS) related and will never go into the application store.
Another example would be like this:
{command: "load entity", parameters: {type: "Vendor\Bundle\Entity\Type", id: 43} }
Which would load an entity which should be stored in the application store.
Well, as i said, im not that familiar with EmberJS that I could figure out which the best approach could be. Should I bypass the DS Adapter completely and check for "isDirty" and just the push methods after loading entities? I'm happy about any idea you have!
As far as I understand your question, you want to push changes from your backend to your single page app?
You can push custom JSON into your application's store in Ember by using self.store.push('modelName', json). Have a look at the docs for a better undestanding.
So for example if your server sends you JSON via websocket that looks like this
{
- "message": {
"type": "fooModel",
"data": {
... // Model attributes here
}
}
}
you can push the data into your store. The following snippet would work with SocketIO for example:
App.ApplicationRoute = Ember.Route.extend({
activate: function() {
// connect to the websocket once we enter the application route
var socket = window.io.connect('http://localhost:8080');
var self = this;
socket.on('message', function(data){
self.store.push(data.type, data.item);
});
}
});
You can easily modify this snippet to fit your needs.

How to fire an event to Ember from another framework

We are using version pre4 of ember.
We have a framework (SignalR) working parallel with ember that handles real-time notifications to our application. In the older versions of ember we were able to access the global reference of the router / controller. But with the new version of Ember this is no longer possible. (This is fine)
We have tried different approaches like setting up a global controller in the top route:
setupController: function(){
app.appController = this.controllerFor('app');
}
and sending an event to this controller, which bubbles up to the route like this:
notificator.update = function (context) {
app.appController.send('notificationOccured', context);
});
But this feels like working against the Ember team which just removed the global references.
So now to the big question: is there a better way to access the router or a controller from outside Ember? Preferably send an event to either with a context.
All help is appreciated!
So now to the big question: is there a better way to access the router or a controller from outside Ember? Preferably send an event to either with a context.
Yes. This sounds like a good fit for the ember instrumentation module. Have an appropriate controller subscribe to SignalR events, then trigger them whenever your app handles real-time notification.
First, add a method to ApplicationController for processing updates. If not defined here the event would bubble to the router.
App.ApplicationController = Ember.Controller.extend({
count: 0,
name: 'default',
signalrNotificationOccured: function(context) {
this.incrementProperty('count');
this.set('name', context.name);
}
});
Next, setup your ApplicationController by subscribing to the signalr.notificationOccured event. Use the before callback to log the event and send it's payload to the controller.
App.ApplicationRoute = Ember.Route.extend({
setupController: function (controller, model) {
Ember.Instrumentation.subscribe("signalr.notificationOccured", {
before: function(name, timestamp, payload) {
console.log('Recieved ', name, ' at ' + timestamp + ' with payload: ', payload);
controller.send('signalrNotificationOccured', payload);
},
after: function() {}
});
}
});
Then from your SignalR Application, use Ember.Instrumentation.instrument to send payload to your ApplicationController as follows:
notificator.update = function (context) {
Ember.Instrumentation.instrument("signalr.notificationOccured", context);
});
I posted a working copy with simulated SignalR notifications here: http://jsbin.com/iyexuf/1/edit
Docs on the instrumentation module can be found here, also check out the specs for more examples.
You probably shouldn't be doing this but here's a way to get access to the application's controllers, views, models and router instances. When your application is initialized, controllers, views, models and router are all registered in the application container __container__
APP.__container__.lookup('controller:foo').get('content');
APP.__container__.lookup('view:foo').get('templateName');
APP.__container__.lookup('router:main');
What i think you should do is encapsulate calls to the 3rd party library inside Ember and let Ember manage the whole application. See this attempt to make JQuery UI ember-aware