Can i access ember service variables in my whole application? - ember.js

I Created an ember service and i initialized its value now i updated that value in a controller. I want this updated variable in my complete application ? What should i supposed to do. Any help will be appreciated. Thanks

Just dummy example
Service
// header.js
import Service from '#ember/service';
export default Service.extend({
currentRoute: null
});
Route
import Route from '#ember/routing/route';
import { inject as service } from '#ember/service';
import { set } from '#ember/object';
export default Route.extend({
header: service(),
beforeModel() {
set(this, 'header.currentRoute', 'login');
}
});
Any controller
import Controller from '#ember/controller';
import { computed, get } from '#ember/object';
import { inject as service } from '#ember/service';
export default Controller.extend({
header: service(),
currentRoute: computed('header.currentRoute', function() {
return get(this, 'header.currentRoute');
})
});
I hope you understood the idea

Please look at this article from the ember guide https://guides.emberjs.com/release/components/triggering-changes-with-actions/#toc_invoking-actions-directly-on-component-collaborators
You did the correct thing, once you already set an updated value of that variable in your service that value will be the same until you instructed your code to change the value of that variable. To answer your question, Can i access ember service variables in my whole application? Yes you can, you can do the following to access the variable into your application.
Assuming that this is your variable and service name
//services/current-session.js
import Service from '#ember/service';
export default Service.extend({
foo: 'foo bar baz',
});
If you want to access it in your controller please do the following
import Controller from '#ember/controller';
import { computed } from '#ember/object';
import { inject as injectService } from '#ember/service';
export default Controller.extend({
currentSession: injectService(),
bar: computed('currentSession.foo', function() {
return this.currentSession.foo;
}),
});
If you want to access it in your component
import Component from '#ember/component';
import { computed } from '#ember/object';
import { inject as injectService } from '#ember/service';
export default Component.extend({
currentSession: injectService(),
bar: computed('currentSession.foo', function() {
return this.currentSession.foo;
}),
});
The approach above is based on ember-cli 3.4 Here's an ember-twiddle you can play around. https://ember-twiddle.com/b92a7d4fa03a1699a5b8a79aca5d2782?openFiles=controllers.application.js%2C
If you want to change the value of your variable in your service through your controller you can do this
import Controller from '#ember/controller';
import { set } from '#ember/object';
import { inject as injectService } from '#ember/service';
export default Controller.extend({
currentSession: injectService(),
actions: {
change() {
this.currentSession.set('foo', 'foo bar baz please');
}
}
});

working twiddle.
Steps:
1) create a service:
app/services/service-name.js
import Service from '#ember/service';
export default Service.extend({
name: "hello",
changeName(name) {
this.set('name', name);
}
});
2) Create an initializer so you need not to inject the service in every route/controller/component.
app/initializers/inject-service-name.js
export function initialize(application) {
application.inject('controller', 'serviceName', 'service:service-name');
// if needed
// application.inject('route', 'serviceName', 'service:service-name');
// application.inject('component', 'serviceName', 'service:service-name');
}
export default {
initialize
};
once initializer is created kindly restart your ember server.
3) Usage:
app/controller/application.js
import Controller from '#ember/controller';
export default Controller.extend({
init() {
this._super(...arguments);
// print initial value
console.log(this.get('serviceName').name); // hello
// change value of name property in service
this.get('serviceName').changeName("hai");
// print changed value
console.log(this.get('serviceName').name); // hai
}
});

Related

Adding a custom variable to EmberRouter and then using it in another file

I recently tried extended the EmberRouter to include the following piece of information.
router.js
import EmberRouter from '#ember/routing/router';
const Router = EmberRouter.extend({
lastVisitedURL: null,
init() {
this._super(...arguments);
this.on('routeWillChange', () => {
this._super(...arguments);
this.lastVisitedURL = this.currentURL;
});
}
// Router.map code (not important)
})
I would now like to extract lastVisitedURL from a controller file. However, I'm not sure how to do it. Some of the things I've tried include importing the EmberRouter directly (I.E.):
import Router from '../router';
export default Controller.extend({
someFunction() {
console.log(Router.lastVisitedURL); // returns undefined
}
});
I'm not perfectly sure what the problem is with this approach, but it appears to be returning me some sort of other object or function that doesn't truly contain the state of the router.
So the next approach, that seems to be a little more accepted, was to try to use the RouterService object that I believe is meant to provide an API to the EmberRouter.
import Router from '../router';
export default Controller.extend({
router: service(),
someFunction() {
console.log(this.router.lastVisitedURL) // returns undefined
}
});
The problem I encountered with this solution though is that even though the routerService can store the state of the EmberRouter, it doesn't store my specific new variable. So I now need a way to add this specific pice of data to the RouterService as well as the EmberRouter.
I'm not really sure how to do this or if there is a better approach to the problem I'm trying to solve. Any thoughts appreciated!
I'm a little bit confused about your use case to be honest. The current URL is available on the RouterService, which is shipped with Ember by default. You could access it like this:
import Controller from '#ember/controller';
import { inject as service } from '#ember/service';
export default Controller.extend({
router: service(),
someFunction() {
console.log(this.router.currentURL);
}
});
It seems like you are trying to reinvent that feature.
If you want to go with declaring a property on the EmberRouter instance and use it at other places you need to look up the router on the container. It's available as router:main. You can't import it directly as it's neither a service nor a controller. The code would look like:
import Controller from '#ember/controller';
import { getOwner } from '#ember/application';
export default Controller.extend({
someFunction() {
let owner = getOwner(this);
let router = owner.lookup('router:main');
console.log(router.currentURL);
}
});
I would not recommend such a pattern. I don't think it's officially supported. As far as I'm aware router:main is private API. So it might be broken in a minor release.
This could be way better addressed by a service:
// app/services/location-history.js
import Service from '#ember/service';
import { inject as service } from '#ember/service';
import { action } from '#ember/object';
export default Service.extend({
router: service(),
updateRoute: action(function() {
this.visitedURLs = [...this.visitedRoutes, this.router.currentURL];
}),
init() {
this.router.on('routeDidChange', this.updateRoute);
this.set('visitedURLs', []);
},
willDestroy() {
this.router.off('routeDidChange', this.updateRoute);
}
});
If you find that syntax hard to read I recommend switching to native ECMAScript classes, which is the default syntax since Ember Octance:
// app/services/location-history.js
import Service from '#ember/service';
import { inject as service } from '#ember/service';
import { action } from '#ember/object';
export default class LocationHistoryService extends Service {
#service router;
visitedURLs = [];
#action
updateRoute() {
this.visitedURLs = [...this.visitedRoutes, this.router.currentURL];
}
constructor() {
this.router.on('routeDidChange', this.updateRoute);
},
willDestroy() {
this.router.off('routeDidChange', this.updateRoute);
}
});

How to access a JSON file in my controller

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`
}
});

ReferenceError: Can't find variable: authenticator Ember js Error

I got an error ReferenceError: Can't find variable: authenticator Ember error in browser console and authenticator is not defined in terminal with this code
import Controller from '#ember/controller';
import { inject } from '#ember/service';
export default Controller.extend({
session: inject('session'),
actions: {
authenticate: function(){
var credentials = this.getProperties('username','password');
authenticator = 'authenticator:jwt';
this.get('session').authenticate(authenticator,credentials).catch((reason) => {
this.set('errorMessage', reason.error || reason);
});
}
}
});
As #jelhan said in the comments, you need to declare the variable authenticator by using let. This is a requirement of JavaScript and not anything specific to Ember.
Example:
import Controller from '#ember/controller';
import { inject } from '#ember/service';
export default Controller.extend({
session: inject('session'),
actions: {
authenticate: function(){
var credentials = this.getProperties('username','password');
let authenticator = 'authenticator:jwt';
this.get('session').authenticate(authenticator,credentials).catch((reason) => {
this.set('errorMessage', reason.error || reason);
});
}
}
});

Create Custom Emberjs Service Error: Attempting to inject an unknown injection: `service:titleService`

I hit the above Error: Attempting to inject an unknown injection: service:titleService with the following code:
// initializers/titleService
export default {
name: 'titleService',
initialize: function(container, application) {
application.inject('route', 'titleService', 'service:titleService');
}
};
// services/titleService.js
import Ember from 'ember';
export default Ember.Service.extend({
title(name) {
this.set('title', name);
}
});
// routes/login.js
import Ember from 'ember';
export default Ember.Route.extend({
titleService: Ember.inject.service(),
actions: {
didTransition: function() {
this.set('titleService.title', 'Login Page');
}
}
});
// templates/application.hbs
<div class="page-header">
<h1>{{titleService.title}}</h1>
</div>
{{outlet}}
am I missing anything?
You have to follow the naming conventions of Ember - If you're referring to your service as titleService, then you want the file to be title-service.js, not titleService.js.
Seems like there is an issue where you are trying to inject route into your TitleService. It's probably a typo that should be router instead of route. If you want to use the router inside your service you could also inject the -routing service, but be careful since it is part of the private API.
Example:
import Ember from 'ember';
export default Ember.Service.extend({
routing: inject.service('-routing'),
someFunc() {
const router = get(this, 'routing').router;
// Do something with the router here
}
});
More information can be found in this thread: http://discuss.emberjs.com/t/routing-as-a-service/8550/3

Why must I lookup store on my simple-auth custom session instead of injecting it as a service?

I have a initializer like this:
import Ember from 'ember';
import Session from 'simple-auth/session';
var SessionWithCurrentUser = Session.extend({
store: Ember.inject.service(),
currentUser: function() {
console.log(this.get('store'));
console.log(this.store);
console.log(this.container.lookup('service:store'));
}.property('secure.access_token')
});
export default {
name: 'custom-session',
after: 'ember-data',
initialize(registry) {
registry.register('session:withCurrentUser', SessionWithCurrentUser);
}
};
currentUser gets called on user interaction, long after my app has finished loading. Only the last container lookup gives the store, the other 2 is an object:
{
_lastData: Object,
key: "ember_simple_auth:session"
[..]
}
What's going on? Why can't I inject the store?
It's because store in the current version of simple-auth is being overridden by an instance-initializer with the session storage. The next major version of simple-auth will turn the session storage into a service and we'll be able to do:
import Ember from 'ember';
const { service } = Ember.inject;
export default Ember.Service.extend({
session: service('session'),
store: Ember.inject.service(),
account: Ember.computed('session.content.secure.account_id', function() {
const accountId = this.get('session.content.secure.account_id');
if (!Ember.isEmpty(accountId)) {
return DS.PromiseObject.create({
promise: this.get('store').find('account', accountId)
});
}
})
});
From the dummy app, once https://github.com/simplabs/ember-simple-auth/pull/602 is merged.