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
Related
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?
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.
I've got a base-route encapsulating all other routes in my application.
I'd like to make the categories I retrieve from the store via this.store.find('category') available everywhere in my application.
I tried retrieving it in the base-controller with this code:
import Ember from 'ember';
export default Ember.ArrayController.extend({
// retrieve all categories as they are needed in several places
categories: function() {
return this.get('store').find('category');
}
});
and creating an alias from child-controllers via:
categories: Ember.computed.alias('controllers.base.categories')
but it gives this error-message:
Uncaught Error: Assertion Failed: The value that #each loops over must be an Array. You passed function () {
return this.get('store').find('category');
}
How can I solve my problem?
Make it a computed property.
categories: function() {
return this.get('store').find('category');
}.property()
Controllers are being deprecated in 2.0, so I'd look into using a service architecture instead of a base controller architecture.
Using Ember-Cli, I'm trying to add a register an adapter and then inject it into controllers. I don't understand what values I'm supposed to pass into either function, or why their in that format.
(Following provided by emberjs webpage):
App.register('network:main', App.NetworkAdapter);
App.inject('controller', 'network', 'network:main');
Specifically, I'm unsure of what this "type:name" format is, nor do I understand how the inject values relate to what is in the register function.
Also, does anybody know what the following error means in relation to the application.inject function?
"Error while processing route: index Failed to create an instance of
'qservice:main'. Most likely an improperly defined class or an invalid
module export."
Code:
export default {
name: 'qservice',
initialize: function(container, application){
application.register("qservice:main", "qservice", {singleton: true});
application.inject("controller",'qservice',"qservice:main");
//application.inject("route",'qservice','adapter:main');
}
};
The second parameter of the register function should be a factory (or instance if instantiate is set to false). A factory is something you can call create on and it will return an instance. Singleton is true by default, so there is no need to specify it.
var qservice = Em.Object.extend();
application.register("qservice:main", qservice);
var qserviceInstance = Em.Object.create();
application.register("qservice:main", qserviceInstance, {instantiate:false});
I am migrating from ember-data 0.0.14 to ember-data 1.0.0-beta.6. I have been following the guide
I am preloading some data needed by my application, triggering this pre-load when the application is ready. But I have lost the store!
/// application.js
var App = Ember.Application.createWithMixins({
...
ready: function () {
this.preLoadData();
},
...
});
/// load_data.js
function preLoadData() {
var store = this.Store;
if (DEBUG) { console.log('preLoadData > this=%o store=%o', this, store); }
store.find('node'); // was this.Node.find();
}
App.preLoadData = preLoadData;
But this.Store is not the store (I do not know what that is!). Also tried with this.store, but it is undefined, so I get:
Uncaught TypeError: Cannot call method 'find' of undefined
I have even tried doing:
// inject the store into all components
SettingsApp.inject('component', 'store', 'store:main');
Whatever that magic means (what is 'component'? Is it component 'component', or any component? What is a component?), but didn't help.
How can I access an instance of the store directly from my App?
Edit: I have found component in the documentation, but this is not what I need: I want to access the store from the App.
You are trying to access store from Application object which is not possible. You could use something like this,
var store = App.__container__.lookup('store:main')
To initialize your app
But I believe you want to initialize your app with some preloaded data. You could use initializer to do such work.
Ember.Application.initializer({
name: "preload data",
initialize: function(container, application) {
var store = container.lookup('store:main');
store.find('node');
}
});