emberjs (unknown mixin) error when working with Ember.Object.Extend - ember.js

I'm creating an initializer in my ember app (using ember-cli 0.2.0 beta). In it, I define a user object (that I define with Ember.Object.extend) that I want to register and inject into the app's controllers.
When i print the user object (right after defining it) to the console, Iā€™m getting ā€œ(unkown mixin)ā€. I've looked around but can't seem to find a solution or tell why this is the case. Here's what my initializer file looks like:
// app/initializers/application.js
var currentUser = Ember.Object.extend({
authToken: localStorage['authToken'],
isAuthenticated: function () {
return !!this.get('authToken');
}.property('authToken')
});
console.log(currentUser); // logs (unknown mixin)
export function initialize(container, application) {
// register current user factory
application.register('user:current', currentUser, {singleton: true});
// inject factory
application.inject('controllers', 'currentUser', 'user:current');
}
export default {
name: 'application',
initialize: initialize
};

I would suggest using Ember.Object.create(). What's the point of extending the object but never creating an instance of it?

Related

How to set an application wide variable? (on app controller?)

I am using the latest Ember, 2.1, and am wondering how to set some application wide variables, such as user id, username, email, etc, presumably on the application controller.
While ember applications have tons of files, I haven't really done a lot yet, I'm somewhat confident I'm sharing just the right code. I don't have a login route file. I have the ember simple auth plugin installed, but I'm not actually using/invoking it any special way, except for mixing it into my application route:
import ApplicationRouteMixin from 'simple-auth/mixins/application-route-mixin';
export default Ember.Route.extend(ApplicationRouteMixin)
My router:
this.route('login')
My login template:
<button {{action 'forceLogin'}}>Force login of devinrhode2#gmail.com by clicking this action button</button>
<p>Your account id is: {{account_id}}</p>
My login controller:
export default Ember.Controller.extend({
actions: {
forceLogin: () => {
var data = {
"account_id": 123,
"email":"devinrhode2#gmail.com",
"name":"Devin Rhode"
}
this.setProperties(data)
}
}
});
I have the forceLogin controller action being called, but, the {{account_id}} is not populating into the template. How could I get the account_id to render back into the template? How could I make the account_id globally accessible to my ember application by calling this.get('account_id') wherever I need it?
Currently I get the error:
Cannot read property 'setProperties' of undefined
You get the error because of the way you define forceLogin. Arrow functions are bound to the context where they're defined. Here's what your code compiles to:
var _this = this; // we capture `this` from out here!
export default Ember.Controller.extend({
actions: {
forceLogin() {
...
_this.setProperties(data) // `_this` is the window!
}
}
});
That's no good because this should be the instance of the controller and instead it's the window.
Instead you should define forceLogin like this:
export default Ember.Controller.extend({
actions: {
forceLogin() {
...
this.setProperties(data) // `this` is our controller instance
}
}
});
To get the account_id from somewhere else, you can inject the login controller:
// in some other controller
export default Ember.Controller.extend({
login: Ember.inject.controller(),
actions: {
doSomethingWithTheAccountId() {
var accountId = this.get('login.account_id');
...
}
}
});
It would be cleaner to move those properties to a service, which you can inject anywhere with Ember.inject.service()

Ember Simple Auth - injecting current user into every route

I am building a site using Ember Simple Auth.
I followed these instructions to try and add the current user object to the session and it worked, using this slightly adapted code:
import Ember from 'ember';
import Session from 'simple-auth/session';
export default {
name: "current-user",
before: "simple-auth",
initialize: function(container) {
Session.reopen({
setCurrentUser: function() {
var accessToken = this.get('secure.token');
var _this = this;
if (!Ember.isEmpty(accessToken)) {
return container.lookup('store:main').find('user', 'me').then(function(user) {
_this.set('content.currentUser', user);
});
}
}.observes('secure.token'),
setAccount: function() {
var _this = this;
return container.lookup('store:main').find('account', this.get('content.currentUser.account.content.id')).then(function(account) {
_this.set('content.account', account);
});
}.observes('content.currentUser'),
});
}
};
However, using the latest version of Ember I'm getting the following:
DEPRECATION: lookup was called on a Registry. The initializer API no longer receives a container, and you should use an instanceInitializer to look up objects from the container. See http://emberjs.com/guides/deprecations#toc_deprecate-access-to-instances-in-initializers for more details.
I know that I need to split the above into /app/initializers and /app/instance-initializers (as per the notes here) but I'm not quite sure how to go about it.
Of course, if there is an easier/cleaner way to make the user and account objects available to every route/template I'd love to hear them :)
Thanks
This works for me on:
ember-cli: 0.2.7 (ember: 1.12.0, ember-data: 1.0.0-beta.18)
ember-cli-simple-auth: 0.8.0-beta.3
Note:
ember-data: 1.13. Store is registered in an initializer, should work as is
ember-data: 1.0.0-beta.19. Store is registered in an instance-initializer, some adjustments needed
1) Customize session
//config/environment.js
ENV['simple-auth'] = {
session: 'session:custom',
...
}
//app/sessions/custom.js
import Session from 'simple-auth/session';
export default Session.extend({
// here _store is ember-data store injected by initializer
// why "_store"? because "store" is already used by simple-auth as localStorage
// why initializer? I tried
// _store: Ember.inject.service('store') and got error
currentUser: function() {
var userId = this.get('secure.userId');
if (userId && this.get('isAuthenticated')) {
return this._store.find('user', userId);
}
}.property('secure.userId', 'isAuthenticated')
});
2) Inject store to session by initializer (otherwise find() wouldn't work)
//app/initializers/session-store
export function initialize(container, application) {
application.inject('session:custom', '_store', 'store:main')
// "store:main" is highly dynamic depepeding on ember-data version
// in 1.0.0-beta.19 (June 5, 2015) => "store:application"
// in 1.13 (June 16, 2015) => "service:store"
}
export default {
name: 'session-store',
after: 'ember-data',
initialize: initialize
}
3) In template
{{#if session.isAuthenticated}}
{{session.currentUser.name}}
{{/if}}
Note: this does not relieve you from deprecations generated by ember-simple-auth itself.
First of all you shouldn't reopen the session but use a custom session instead (see this example: https://github.com/simplabs/ember-simple-auth/blob/master/examples/4-authenticated-account.html#L132). Also you you shouldn't only load the current user when the access token is set but when the session is authenticated ('session.get('isAuthenticated')') which makes your code not dependent on the authenticator.
The deprecation warnings regarding the use of the registry in the initializer will go away in ESA 0.9.0 hopefully.
Here's a before and after of an initializer/instance-initializer that I did the other day.
Before
export function initialize( container, application ) {
var session = Ember.Object.create({
user:null,
authorization:null
});
application.register('session:main', session, { instantiate: false });
application.inject('route', 'session', 'session:main');
application.inject('controller', 'session', 'session:main');
application.inject('adapter', 'session', 'session:main');
}
After
export function initialize( instance) {
var session = Ember.Object.create({
user:null,
authorization:null
});
instance.registry.register('session:main', session, { instantiate: false });
instance.registry.injection('route', 'session', 'session:main');
instance.registry.injection('controller', 'session', 'session:main');
instance.registry.injection('adapter', 'session', 'session:main');
}
Ember Data Stuff
Ember Data in the latest iterations should be fetched using store:application
export function initialize(instance) {
var store = instance.container.lookup('store:application');
....
}
export default {
name: 'socket',
initialize: initialize,
after:['ember-data']
};

How to create a inject helper for something other than service?

https://github.com/emberjs/ember.js/blob/5fd2d035b30aa9ebfe73de824b3b283ec8e589cc/packages/ember-runtime/lib/system/service.js#L31
In the line I reference above the ember-core team imports this createInjectionHelper and uses it to add a clean/simple api for injecting services like so
App.ApplicationRoute = Ember.Route.extend({
authManager: Ember.inject.service('auth'),
model: function() {
return this.get('authManager').findCurrentUser();
}
});
How can I create something like this myself for a non service?
Your example usage will change slightly from what you have above. We will cover what the injectRepositories does in a little bit.
import injectRepositories from 'app/utils/inject';
export default Ember.Route.extend({
repository: injectRepositories('person'),
model: function() {
var repository = this.get('repository');
return repository.find();
}
});
The initializer can be improved with the following changes:
import registerWithContainer from "ember-cli-auto-register/register";
export function initialize(_, application) {
registerWithContainer("repositories", application);
application.inject("repositories", "store", "store:main");
}
export default {
name: "repositories",
after: "store",
initialize: initialize
};
Let's break down what is happening in each line.
registerWithContainer("repositories", application);
In the line above, we are deferring to the ember-addon ember-cli-auto-register. This addon will take a directory, in this situation, the repositories directory and register each object into the Ember container to able to be accessed via a lookup. They will be inserted as if doing the following:
application.register("repositories:person", PersonRepository);
Then we add a function to do the injection using the ember-addon ember-cli-injection:
// app/utils/inject.js
import inject from "ember-cli-injection/inject";
var injectRepositories = inject("repositories");
export default injectRepositories;
This then allows us the opportunity to use the newly created function above to access these objects with the code below:
import injectRepositories from 'app/utils/inject';
export default Ember.Route.extend({
repository: injectRepositories('person'),
model: function() {
var repository = this.get('repository');
return repository.find();
}
});
Since each object is now in the container, we can look it up and inject at runtime instead of during the initialization of the application. We register the repositories key in the function and this then returns a computed property (see code below from ember-cli-injection). We do this as a computed property to allow lazy loading. The object is not fetched from the container until the property is accessed.
import Ember from 'ember';
var injection = function(key) {
return function(name) {
return Ember.computed(function(propertyName) {
var objectName = name || propertyName;
return this.container.lookup(key + ':' + objectName);
});
};
};
export default injection;
We also allow for a name to passed to the repositories function, for example repository: injectRepositories('person'). This allows you to name your object whatever you would like when injecting it.
If you would like to just name the object the same as the name of the repository injected into the container you can alternatively do person: injectRepositories(). This will pass the person key to the computed property as the propertyName and since the name was left null when injecting, the objectName will instead be person. This matches the API produces similar results but is not the same as that of the Ember.inject.service and Ember.inject.controller API that is available as of Ember 1.10.
I don't think it's their intention for you to use it this way. The standard way is to use App.inject() if you're using plain ember, or do this in an initializer if you're using ember-cli.
In your case:
// Register the service
App.register('service:auth', {
findCurrentUser: function() {
// Do your magic
}
}, { instantiate: false });
App.inject('route', 'auth', 'service:auth');
Then in your model hook you can use this.auth.findCurrentUser();. You can also inject this into controllers and components if you need. Also note, to keep it clean, that you might want to include a separate module instead of defining your auth module in the service registration.
More info here:
http://emberjs.com/guides/understanding-ember/dependency-injection-and-service-lookup/#toc_dependency-injection-with-code-register-inject-code
NOTE
A service is also not a "special" thing. You can inject anything you want into pretty much anything you want using this method.

How to allow two ember-cli services to cross communicate?

Using ember-cli version 0.0.42
Run these commands
ember new myApp
cd myApp
ember generate service serviceOne
ember generate service serviceTwo
ember generate controller application
Make these updates
controllers/application.js
import Ember from 'ember';
export default Ember.Controller.extend({
init: function() {
var a = this.get('serviceOneService.testFunction');
}
});
initializers/service-one-service.js
export default {
name: 'service-one-service',
initialize: function(container, app) {
app.inject('route', 'serviceOneService', 'service:service-one');
app.inject('controller', 'serviceOneService', 'service:service-one');
}
};
services/service-one.js
import Ember from 'ember';
export default Ember.Object.extend({
testFunction: function(){
//How would I make a call to serviceTwo.testFunction here instead of returning 123
return "123"
}
});
services/service-two.js
import Ember from 'ember';
export default Ember.Object.extend({
testFunction: function(){
return "Test function from service 2"
}
});
My question is how to I inject serviceOne into service two and serviceTwo into service one. One I do that how do I access service two from service one. Inside when calling I have an idea of how to injest the service directly into all controllers but when I try and inject into another object (service) inside of testFunction "this" refers to the window and not an ember object.
My first thought was to just inject each service into the overal service namespace but I get this message:
Uncaught Error: Cannot inject a `service:service-one` on other service(s). Register the `service:service-one` as a different type and perform the typeInjection.
You can inject one service into the other by defining an initializer and setting setting the after property to something like after: ['service-one', 'service-two'], which would cause it to run after both of those services have been registered.
The initializer would look something like...
Ember.Application.initializer({
after: ['service one', 'service two'],
name: 'service injections',
initialize: function(container, application){
application.inject('service:service-one', 'serviceTwoService', 'service:service-two');
}
});
Unfortunately if you then try to inject service:service-one into service:service-two you'll get an error stating Maximum call stack size exceeded which happens because the container ends up in a loop while trying lookup all the needed injections.
You can see a working bin here: http://emberjs.jsbin.com/fitaka/1/edit

Injecting function with initializer in ember-cli

I have an app built on Ember-cli. I am trying to inject a method into all routes, controllers, and views. I am aware I could utilize the app/utils directory and import the method's module into all the files that call it, but I would like the method to be automatically available. Hence, I have chosen to inject the method using an initializer.
The initializer looks like this:
export default {
name: 'injectMethod',
initialize: function(container, app) {
var someFunction = function(message) {
};
app.register('function:main', someFunction);
Em.A(['route', 'controller', 'view']).forEach(function(place) {
app.inject(place, 'someFunction', 'function:main');
});
}
};
This results in the following error message: Uncaught TypeError: undefined is not a function. The error disappears when I remove the app.inject() line.
Are initializers handled differently in ember-cli and/or is something in the above code incorrect? Or is they a better way to achieve my goal than using an initializer?
Ember expects you to register a factory which it can create instances using the create method. If you are passing in an instance (or just a method) you would need to tell Ember not to attempt to instantiate it and just use the instance passed in.
export default {
name: 'injectMethod',
initialize: function(container, app) {
var someFunction = function(message) {
};
app.register('function:main', someFunction, {instantiate: false});
Em.A(['route', 'controller', 'view']).forEach(function(place) {
app.inject(place, 'someFunction', 'function:main');
});
}
};
Example: http://emberjs.jsbin.com/xaboliwu/1/edit