I have the following route that will poll a model and refresh the data at a given interval. What I'm trying to do is trigger an alert when a new record is available in the model. I'm new to this, so I'm having some trouble figuring out how to trigger an alert site-wide without simply triggering it each time the model refreshes. I tried using 'didCreate' in the model, but it doesn't seem to recognize new records.
import Route from '#ember/routing/route';
import Ember from 'ember'
export const pollInterval = 8000 // time in milliseconds
export default Route.extend({
model() {
return Ember.RSVP.hash({
pat: this.store.findAll('pat'),
appt: this.store.findAll('appt')
})
},
getSMS () {
return this.get('store').findAll('smstext')
},
onPoll () {
return this.getSMS()
.then((users) => {
this.set('currentModel', users)
})
},
afterModel () {
let smsPoller = this.get('smsPoller')
if (!smsPoller) {
smsPoller = this.get('pollboy').add(this, this.onPoll, pollInterval)
this.set('smsPoller', smsPoller)
}
},
setupController(controller, models) {
controller.set('huddle', models.huddleappt);
controller.set('pat', models.pat);
}
})
I would recommend to use a service for this use case. You can inject your service wherever you need the data, and in the service you can handle the polling.
You can then display your data like this.
In your component file:
import Component from '#ember/component';
import { inject as service } from '#ember/service';
import { computed } from '#ember/object';
export default Component.extend({
smsService: service(),
smsData: computed('smsService.data')
// ...
And in your template you can access your data with the computed property from your components js file
Related
In my Ember app, I have a JSON file filled with data in my public directory (I.E. public/data/articles.json.
I have an article route where I would like to load in this data and display it in the front-end. How could I do this successfully? I am willing to use the model hook, however I don't know of a solution off-hand.
I currently have the following code in my controller. However, this doesn't work (most likely because the data is loaded in after it is rendered).
import Controller from '#ember/controller';
import $ from 'jquery';
export default Controller.extend({
init() {
this._super(...arguments);
$.getJSON("/data/articles.json", function (data) {
console.log("test");
console.log(data);
this.articleData = data;
}
})
You can literally just write return $.getJSON("/data/articles.json"); in your routes model hook and then access the data as model in your template/controller.
A bit more elegant would be to use fetch:
async model() {
const res = await fetch('/data/articles.json');
return res.json();
}
Are you accessing that file somewhere else? I would recommend putting the file inside your app folder and importing it. It could be inside app/utils/sample-articles.js or another folder for "static data". That way you don't have to do that extra request.
// app/utils/sample-articles.js
export default {
// Articles
};
Then in your controller you can just import it like:
import Controller from '#ember/controller';
import sampleArticles from 'your-app/utils/sample-articles';
export default Controller.extend({
articleData: sampleArticles
});
Also in your example, you're assigning the value of data to a variable in an inner scope. You would need to refactor that like:
import Controller from '#ember/controller';
import { set } from '#ember/object';
import $ from 'jquery';
export default Controller.extend({
init() {
this._super(...arguments);
$.getJSON("/data/articles.json", (data) => {
console.log("test");
console.log(data);
set(this, "articleData", data); // Use `set`
}
});
I am trying to do conditional transition in my router.js.
I need to determine the condition(Which is eligibility) based on the boolean value returned by another rest service.
Can I write a function to determine that value in router.js and how to use that value to redirect my transition to two separate routes.
If the eligibity is true, I need to do
'
this.route('coverage', {
path: '/'
});
else
this.route('inEligible', {
path: '/'
});
'
Please provide me an example. I am very new to ember.
I use this pattern in my routes:
import Route from '#ember/routing/route';
import { inject as service } from '#ember/service';
export default class SettingsRoute extends Route {
#service currentUser;
async beforeModel() {
// inside the service, I have logic for checking if the user
// authenticated, not expired, and fetched
const isLoggedIn = await this.currentUser.isLoggedIn();
if (!isLoggedIn) {
this.transitionTo('setup');
return;
}
}
}
if you're using non-native classes:
import Route from '#ember/routing/route';
import { inject as service } from '#ember/service';
export default Route.extend({
currentUser: service();
async beforeModel() {
// inside the service, I have logic for checking if the user
// authenticated, not expired, and fetched
const isLoggedIn = await this.currentUser.isLoggedIn();
if (!isLoggedIn) {
this.transitionTo('setup');
return;
}
}
});
My app is using a websocket service based on ember-phoenix to push new records from the API to the store. I would like these new records to render in my template when they're added.
I have a route where the model hook returns a filtered query promise:
import Ember from 'ember';
const {
get,
inject,
} = Ember;
export default Ember.Route.extend({
socket: inject.service(),
model(params) {
return this.store.query('my-model', {filter: {date: params.date}})
},
afterModel() {
get(this, 'socket').joinSchedule();
},
resetController() {
get(this, 'socket').leaveSchedule();
},
});
When new records are pushed to the store through the websocket, they are not rendered by my template because of how store.query works. If I change store.query to store.findAll the new records are rendered, but I want my route to only load a subset of all the records based on the date query param.
It seems like my only option is to just reload the route's model when a new record is pushed to the store. Is it possible to do this from the service? If not, is there a different approach I might want to explore?
The relevant parts of my socket service are below:
import Ember from 'ember';
import PhoenixSocket from 'phoenix/services/phoenix-socket';
const {
get,
inject,
} = Ember;
export default PhoenixSocket.extend({
session: inject.service(),
store: inject.service(),
joinSchedule() {
const channel = this.joinChannel(`v1:my-model`);
channel.on('sync', (payload) => this._handleSync(payload));
},
_handleSync(payload) {
get(this, 'store').pushPayload(payload);
},
});
Option 1
You can use Ember.Evented to subscribe and dispatch event. I have created twiddle for demonstration.
In socket service,
socket should extend Ember.Evented class
export default PhoenixSocket.extend(Ember.Evented, {
After updating store, you can just trigger myModelDataLoaded which will dispatch all the functions subscribed to myModelDataLoaded.
_handleSync(payload) {
get(this, 'store').pushPayload(payload);
this.trigger('myModelDataLoaded'); //this will call the functions subscribed to myModelDataLoaded.
}
In Route,
You can subscribe to myModelDataLoaded
afterModel() {
get(this, 'socket').joinSchedule();
get(this, 'socket').on('myModelDataLoaded', this, this.refreshRoute); //we are subscribing to myModelDataLoaded
}
Define refreshRoute function and call refresh function.
refreshRoute() {
this.refresh(); //forcing this route to refresh
}
To avoid memory leak need to off subscribtion, you can do it either in resetController or deactivate hook.
resetController() {
get(this, 'socket').leaveSchedule();
get(this, 'socket').off('myModelDataLoaded', this, this.refreshRoute);
}
Option 2.
You can watch store using peekAll with observer and refresh route.
In your controller,
1. Define postModel computed property which will return live record array.
2. Define postModelObserver dependant on postModel.[] this will ensure whenever store is updated with new row, it will be observed by myModelObserver and it will send action refreshRoute to route . where we will call refresh. As you know this will call beforeModel, model, afterModel method.
As you know computed property is lazy, when you are accessing it only then it will be computed. so if you are not using it in template, then just add this.get('myModel') in init method
Controller file
import Ember from 'ember';
const { computed } = Ember;
export default Ember.Controller.extend({
init() {
this._super(...arguments);
this.get('postModel');//this is just to trigger myModel computed property
},
postModel: computed(function() {
return this.get('store').peekAll('post');
}),
postModelObserver: Ember.observer('postModel.[]', function() {
this.send('refreshRoute');
})
});
Route file - define action refreshRoute for refreshing, since refresh is available only in route.
import Ember from 'ember';
const {
get,
inject,
} = Ember;
export default Ember.Route.extend({
socket: inject.service(),
model(params) {
return this.store.query('my-model', { filter: { date: params.date } })
},
afterModel() {
get(this, 'socket').joinSchedule();
},
resetController() {
get(this, 'socket').leaveSchedule();
},
actions:{
refreshRoute() {
this.refresh();
},
}
});
You can trigger an event from your websocket service when you receive a message on the socket, then subscribe to it in your route and then call refresh() to reload your model.
There is also https://github.com/ember-data/ember-data-filter - which retuns live array.
It is not a better way, but one way to do with your existing code is using a callback.
import Ember from 'ember';
const {
get,
inject,
} = Ember;
export default Ember.Route.extend({
socket: inject.service(),
model(params) {
return this.store.query('my-model', {filter: {date: params.date}})
},
afterModel() {
let cb = (myModelRecord) => {
this.get('model').push(myModelRecord);
};
get(this, 'socket').joinSchedule(cb);
},
resetController() {
get(this, 'socket').leaveSchedule();
},
});
Call callback method in socket service,
import Ember from 'ember';
import PhoenixSocket from 'phoenix/services/phoenix-socket';
const {
get,
inject,
} = Ember;
export default PhoenixSocket.extend({
session: inject.service(),
store: inject.service(),
joinSchedule(cb) {
const channel = this.joinChannel(`v1:my-model`);
channel.on('sync', (payload) => cb(this._handleSync(payload)));
},
_handleSync(payload) {
return get(this, 'store').pushPayload(payload);
},
});
I have a project where I need to build an Ember application. The application will have many routes and some of the routes will have some model.
My problem at the moment is that some information is global, meaning they are present in each page (.hbs) and I need to update it periodically.
I've tried to put information on the application route like the following but it didn't work, the content is not accessible on other routes:
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params) {
return Ember.$.getJSON('/api/users/current')
}
});
I've also tried to reload the information with a setInterval but this didn't work either.
import Ember from 'ember';
export default Ember.Route.extend({
init: function() {
var thyself = this;
var interval = setInterval(function(){
thyself.my_reload()
}, 1000);
this.set('interval', interval);
this.set('counter', {});
},
my_reload: function() {
var counter = this.get('counter');
if (counter >= 10) {
clearInterval(this.get('interval'));
}
this.set('data', Ember.$.getJSON('/api/users/current'));
}
});
Where can I place this information so it will be available on all routes? And how can I reload the information periodically?
I'm using ember-cli
#NicosKaralis,
you should use service for it.
You can generate it by command: ember generate service name-of-service
And there you should create methods.
When you want to get access from your controller you should inject it in your controller:
nameOfService: Ember.inject.service(), (remember camelCase here)
and if you want some method from your service in your controller you will use it like this (example with computed property, you can also use it without computed property):
someComputedFunctionInController: Ember.computed(function() {
this.get('nameOfService').yourFunctionFromService();
},
nextComputedFunctionInController: Ember.computed(function() {
this.get('nameOfService.getSomethingFromService');
}
for more:
https://guides.emberjs.com/v2.7.0/tutorial/service/
Hope, it will help you.
I've done
ember g route auth
ember g route auth/pending
Which then gave me :
app/
routes/
auth/
pending.js
auth.js
and my router has
this.route('auth', function() {
this.route('pending', { path: '/pending/:steamid/:token'});
});
Which everything is fine, when I visit
http://localhost:4200/auth/pending/1/2
The page loads, but how do I access :steamid and :token outside of the model.
I'd like to use it so that I can set values in my session service
Like:
import Ember from 'ember';
export default Ember.Route.extend({
session: Ember.inject.service(),
steamID: this.get(// Params Some How),
token: this.get(// Params some How)
thing(params) {
this.get('session').set('tokenID', token),
this.get('session').set('steamID', steamID)
}
});
^^ Pseudo code to express what I'm trying to accomplish.
While it's not in the website documentation, looking at the source code of the Transition object passed to some Route hooks (e.g. afterModel and beforeModel) it have a params property which contains the dynamic segment params.
So you can, for example:
import Ember from 'ember';
export default Ember.Route.extend({
session: Ember.inject.service(),
thing(params) {
// Do some check and returns the result
},
beforeModel (transition) {
if (!this.thing(transition.params)) {
transition.abort();
this.transitionTo('/login');
}
}
});
You can set them in your service from many different hooks:
import Ember from 'ember';
export default Ember.Route.extend({
session: Ember.inject.service(),
/* Access from beforeModel */
beforeModel(transition) {
this.get('session').setProperties({
tokenID: transition.params.token,
steamID: transition.params.steamid
});
},
/* Access from model */
model(params, transition) {
this.get('session').setProperties({
tokenID: params.token,
steamID: params.steamid
});
}
});
If you ask me model hook is the best choice. Especially if you want your query params to refresh the model every time they change (see guide).