Share service variable between controllers - ember.js

I have a variable in a service that I want two share between two controllers.
As I know, Ember services are singleton.
I created a service file: app/services/data-manipulation.js:
import Ember from "ember";
export default Ember.Service.extend({
init() {
console.log('initService');
},
shouldManipulate: false
});
In first controller that is called one screen before the second controller:
dataManipulation: Ember.inject.service(),
init() {
this.updateManipulate();
},
updateManipulate: function() {
this.set("dataManipulation.shouldManipulate", true);
var currentValue = this.get("dataManipulation.shouldManipulate");
console.log(currentValue); // log true as expected
}
In second controller:
dataManipulation: Ember.inject.service(),
init() {
// it inits the service again so 'initService' is logged again.
var currentValue = this.get("dataManipulation.shouldManipulate");
console.log(currentValue); // log undefined
}
What is the problem and how can I make it works?

Related

Use Mixin property in a Controller

This is a crappy example, but I am merely trying to use a mixin's property in a controller. I did the same thing in a route and could access that property. I've tried every way to reference a property I know... What am I misunderstanding?
// app/mixins/author-data.js
import Ember from 'ember';
export default Ember.Mixin.create({
authorName: 'Example author name',
});
// app/controllers/application.js
import Ember from 'ember';
import AuthorDatas from 'app-name/mixins/author-data';
export default Ember.Controller.extend(AuthorDatas, {
siteTitle: `Site title`,
fromAuthorData: this.get('authorName'),
// returns 💩 - what is the proper syntax?
actions: {
showAuthor() {
var author = this.get('fromAuthorData');
console.log(`Author from controller: ${author}`);
},
},
});
// app/templates/application.hbs
{{fromAuthorData}}
This works...
// app/routes/application.js
import Ember from 'ember';
import AuthorDatas from 'app-name/mixins/author-data';
export default Ember.Route.extend(AuthorDatas, {
afterModel() { // arbitrary
var intro = `Author from route:`;
console.log(`${intro} this.authorName`, this.authorName );
console.log(`${intro} this.get('author-name')`, this.get('authorName') );
},
});
(I would have made an ember-twiddle - but I wasn't sure if Mixins would work the same way ~ since they aren't on the list and there is 0 documentation)
The fromAuthorData property on your controller should be defined like this (I think):
fromAuthorData: Ember.computed('authorName', function() {
return this.get('authorName'); // or whatever derived value you need
}
To understand the problem we need to talk about scope, when you extend/create an object you are merely passing in options, your code is no different than:
let options = {
siteTitle: `Site title`,
// `this` is undefined since we are in strict mode
fromAuthorData: this.get('authorName'),
actions: {
showAuthor() {
var author = this.get('fromAuthorData');
console.log(`Author from controller: ${author}`);
},
}
};
export default Ember.Controller.extend(AuthorDatas, options);
Now to access properties that rely on this being the object holding it you will need a function that is run with the object as it's context that returns that value, enter computed properties.
Your code becomes:
// app/controllers/application.js
import Ember from 'ember';
import AuthorDatas from 'app-name/mixins/author-data';
const { computed } = Ember;
export default Ember.Controller.extend(AuthorDatas, {
siteTitle: `Site title`,
// We add `authorName` as the dependent key, should it change `fromAuthorData` will update
fromAuthorData: computed('authorName', function() {
// your author data stuff
let authorName = this.get('authorName');
// ...
return authorDetails;
}),
actions: {
showAuthor() {
var author = this.get('fromAuthorData');
console.log(`Author from controller: ${author}`);
},
},
});

Ember 2.0 get another router from router/controller

Is there any way to call route action from another router/controller? Let's say I have two routes:
App.RouteOne = Ember.Object.extend({
actions: {
someCommonFunctionality: function() {
// ...
}
}
});
App.RouteTwo = Ember.Object.extend({
actions: {
// Here I want to call someCommonFunctionality function from RouteOne
}
});
Is this somehow possible? I have an AJAX get method that I do not want to repeat in RouteTwo as I have it already in RouteOne

How to get the Router instance in initializer

I have a use-case where I want to register routes dynamically in an initializer.
Because the application is a self-defining app I don't know the routes at development time.
Currently I created an instance-initializer:
import Ember from 'ember';
const myTempRouteList = ['home']; // this is retrieved from the backend
export function initialize(instance) {
let container = instance.container;
let router = container.lookup('router:main');
myTempRouteList.forEach(function (name) {
let routeName = name.dasherize();
router.map(function(){ // router.map is undefined here
this.resource(routeName, {path: routeName});
});
container.register(`route:${routeName}`, Ember.Route.extend({
}));
}, this);
}
export default {
name: 'register-routes',
initialize: initialize
};
The problem is that the router instance is present but is has no method map. In the documentation it is described as a public method. Some other methods I checked are present, f.i. hasRoute.
It turns out I had to call the lookupFactory method instead of the lookup method on the container.
export function initialize(instance) {
let container = instance.container;
let router = container.lookupFactory('router:main');
...
}
For people who are working on latest ember with ember-cli (Ember > 2.0). This might be helpful
//initializer.js
export function initialize(application) {
var routeNames = [];
var router = application.__container__.lookupFactory('router:main');
application.deferReadiness();
//if you want to have your custom routes on the highest level
if (routeNames.length > 0) {
router.map(function() {
var _this = this;
routeNames.forEach(function(item,index) {
_this.route(item);
});
});
}
//if you want to have your custom routes as a child of another parent route
if (routeNames.length > 0) {
router.map(function() {
this.route('parentRoute', {path:'/'}, function(){
var _this = this;
routeNames.forEach(function(item,index) {
_this.route(item);
});
});
});
}
application.advanceReadiness();
}

Ember Services - Create default settings for application

My app has a lot of reports, and I have dateBegin and dateEnd in all of then.
The desired behaviour is:
app first load, dateBegin = month begin (jun-01) / dateEnd = today (jun-11)
when user change dates (let's say to mai-01 / mai-31), all controllers get the new dates
The code that I have now:
// app/services/defaults.js
import Ember from 'ember';
export default Ember.Service.extend({
init: function () {
this._super();
var dateEnd = moment().format('YYYY-MM-DD');
var dateBegin = moment().startOf('month').format('YYYY-MM-DD'));
if (!this.get('dateEnd')) { this.set('dateEnd', dateEnd); }
if (!this.get('dateBegin')) { this.set('dateBegin', dateBegin }
}
});
// app/components/select-dates-in-reports.js
import Ember from 'ember';
export default Ember.Component.extend({
defaults: Ember.inject.service(),
displayDateBegin: null,
displayDateEnd: null,
dateBegin: Ember.computed.alias('defaults.dateBegin'),
dateEnd: Ember.computed.alias('defaults.dateEnd'),
setInitialParams: Ember.on('init', function () {
this.set('displayDateBegin', this.get('dateBegin'));
this.set('displayDateEnd', this.get('dateEnd'));
}),
actions: {
chooseParams: function () {
this.set('dateBegin', this.get('displayDateBegin'));
this.set('dateEnd', this.get('displayDateEnd'));
}
}
});
// app/mixins/query-params-for-reports.js
import Ember from 'ember';
export default Ember.Mixin.create({
queryParams: ['dateBegin', 'dateEnd'],
defaults: Ember.inject.service(),
dateBegin: Ember.computed.alias('defaults.dateBegin'),
dateEnd: Ember.computed.alias('defaults.dateEnd')
});
// app/mixins/routes-query-params-for-reports.js
import Ember from 'ember';
export default Ember.Mixin.create({
queryParams: {
dateBegin: {
refreshModel: true
},
dateEnd: {
refreshModel: true
}
},
model: function(params) {
return this.store.find(this.get('modelName'),
{
dateBegin: params.dateBegin,
dateEnd: params.dateEnd
}
);
}
});
It works as desired just after each controller is initialized:
user enter the app, and visit controller1. dateBegin = jun-01 / dateEnd = jun-11
on the same controller1, user change dates to dateBegin = mai-01 / dateEnd = mai-31
user visit controller2. Here is the Problem. The dates are set to dateBegin = jun-01 / dateEnd = jun-11
on the same controller2, user change dates to dateBegin = apr-01 / dateEnd = apr-30
user visit controller1 again. Now it works. The dates are set to dateBegin = apr-01 / dateEnd = apr-30
I tried all I could find over the net.
Create Initializers, used localStorage, etc. Nothing works.
Can anyone helps me??
thanks!
Ember Dependency Injection to the rescue!
You can create a singleton object to hold the dates:
App.SelectedDates = Ember.Object.extend({
dateBegin: null,
dateEnd: null // or whatever value you want...
});
Then, inject that object into ALL controllers like this:
Ember.Application.initializer({
name: 'register-global-dates',
initialize: function(container,app) {
app.register('my:globalDate', App.SelectedDates.create(), {
singleton: true,
instantiate: false
});
app.inject('controller', 'dateglobal', 'my:globalDate');
}
});
Now, in your controller(s), you can do this:
this.dateglobal.set('dateBegin', '2015/01/12');
It's the same object in all controllers.
I hope I am understanding your problem correctly and that this is the solution...

Observe Ember Data store changes in component

I have a component which creates record for a specific model like this:
export default Ember.Component.extend({
store: Ember.inject.service(),
addRecord(account) {
this.get('store').createRecord('update', {
authUid: account.get('authUid'),
service: account.get('platform')
});
}
});
I have another component that needs to observe changes done to a particular model (i.e. if records are added or deleted), and show them in that component.
export default Ember.Component.extend({
store: Ember.inject.service(),
observeStoreChanges: /*What should I write so that every time `addRecord`
pushes record in the store, a function is executed in this component*/
});
If you're a fan of the observer pattern:
// store.js
export default DS.Store.extend(Ember.Evented, {
createRecord() {
const record = this._super.apply(this, arguments);
this.trigger('recordCreated', record);
return record;
}
});
// component.js
export default Ember.Component.extend({
observesStoreChanges: function(record) {
}.on('store.recordCreated')
});