dependency injection without singleton in ember-cli - javascript

Just converted my app to ember-cli, but I don't know how to use Ember.Application.register any more because register doesn't seem to be available when Application is started with extend rather than create.
import Ember from 'ember';
import App from 'myapp/app';
var AdminMyController = Ember.ObjectController.extend({
});
// THROWS ERROR HERE BECAUSE register isn't, uh...registered?
App.register('controller:adminMyController', AdminMyController, { singleton: false });
export default AdminMyController;
Previously, because App was a global, I could register this right in the same class.
Am I going to have to move all the register calls to an initializer so I can get access to the app instance?

I belive an initializer would do this for you. You'll need to create an initializers folder in your app directory (same level as controllers, templates, etc). This file should go there.
import Ember from 'ember';
var AdminMyController = Ember.ObjectController.extend({
...
});
export default {
name: 'adminMyController',
initialize: function (container, application) {
container.register('controller:adminMyController', AdminMyController, {singleton: false});
}
};

Related

Ember.JS: Ember-Objects and where they are exported to

I am trying to grasp the underlying system of Ember.JS. Whereto are those Ember-Objects exported and how are they used?
components:
export default Ember.Component.extend({ ... });
controllers:
export default Ember.Controller.extend({ ... });
models:
export default DS.Model.extend({ ... });
routes:
export default Ember.Route.extend({ ... });
... they all follow the same structure: they extend some object and export something.
Somebody knows more? Thanks!
I think you are interpreting the export keyword wrong here.
It doesn't mean files are written somewhere else in a different format, but:
if a module (= file) is imported, then things in that file that are exported are available to the importer.
Think of it as making some parts available to other modules, a public API in shorter terms.
Ember files usually only export one thing as there is a strong naming convention making the Ember engine work that way.
This is why if you declare a /user route, it will try to use your routes/user.js, controllers/user.js, and templates/user.hbs files if they exist, without you having to specify anything.
In a similar way, this is what makes a component usable inside a template.
Hence the one-liner export default Ember.Something.extend({ ... }); you find in these files.
However, you can have modules (= files) with multiple export statements in a single file, this is perfectly valid.
// Example from babel website
// lib/mathplusplus.js
export * from "lib/math";
export var e = 2.71828182846;
export default function(x) {
return Math.exp(x);
}
// app.js
import exp, {pi, e} from "lib/mathplusplus";
console.log("e^π = " + exp(pi));
The Learn ES2015 from Babel website has a few examples, and you can read more about the export statement on MDN.
In Ember applications, you will - from my experience - find files with multiple exports mostly in some directories meant to be used by more than one module in the application, or not bound to the Ember engine auto-import, like some utils.
The following example shows an Ember.Controller importing a variable exported from another module:
// utils/messages.js
export var hello = 'Hello!';
export var bye = 'Good Bye!'
// controllers/hello.js
import { hello } from "utils/messages";
export default Ember.Controller.extend({
firstName: 'David',
helloString: Ember.computed('firstName', function(){
return `${hello} ${this.get('firstName')}`;
})
});

Ember js 1.12.0 - Failed to create instance

I'm trying to inject my config file into all of my routes, controllers, and components instead of calling import config from '../config/environment' in every file. I get the following error however:
Uncaught Error: Failed to create an instance of 'config:main'. Most likely an improperly defined class or an invalid module export.
Below is my code as its rendered via coffeescript.
// app/initializers/config.js
define('app/initializers/config', ['exports', 'app/config/environment'], function (exports, Config) {
'use strict';
var ConfigInitializer, initialize;
initialize = function (container, app) {
var config;
config = {
config: Config['default']
};
app.register('config:main', config);
app.inject('route', 'config', 'config:main');
app.inject('controller', 'config', 'config:main');
app.inject('component', 'config', 'config:main');
};
ConfigInitializer = {
name: 'config',
initialize: initialize
};
exports['default'] = ConfigInitializer;
exports.initialize = initialize;
What am I missing?
I've stepped through everything using breakpoints and my path to my environment.js file is correct. So I know its not that. I think I'm missing something fundamental about dependency injection.
Everything looks fine in your code, except one thing. By default Ember expects to register Factories but not instances. So once the property gets injected, it tries to get an instance from registered factory. But in your case it's not a factory, it's an instance (object) itself. So the only thing you have to do is to say Ember to use registered object as is, without trying to get an instance. To achive this, just add instantiate: false to register options:
app.register('config:main', config, {instantiate: false});
It is complaining that can't instantiante a new config object when initializing. Try changing your config object into a Ember.Service
import Ember from 'ember'
initialize = function(container, app) {
var config = Ember.Service.extend({
config: Config['default']
}
...
}

Ember: Access ember data 'store' object from utility class

I have a utility class for validating usernames in my ember application and have it setup as specified in the ember-cli docs. I do client-side username validation in several places in my application (components and controllers) so I wanted to pull the validation logic out into a reusable method.
The file is at /app/utils/username-validator.js and I can successfully include the file in my app by importing it like so: import usernameValidator from 'my-app/utils/username-validator';
This works great so far and I've used the pattern for several utility classes. The problem I'm running into now is that I'd like the username-validator method to include a check to see if the username already exists.
As I am using Ember-Data I'd like to check the Ember-Data store. By default, the store appears to only be accessible in controllers and routes. How can I make it accessible in my utility class? All the injection examples I've seen deal with injecting the store into other first class Ember objects like components.
Is it possible to inject the store into a simple utility class as well?
Thank you!
I am using the following versions:
Ember-cli v0.2.6
ember.debug.js:4888 DEBUG: -------------------------------
ember.debug.js:4888 DEBUG: Ember : 1.12.0
ember.debug.js:4888 DEBUG: Ember Data : 1.0.0-beta.18
ember.debug.js:4888 DEBUG: jQuery : 1.11.3
ember.debug.js:4888 DEBUG: Ember Simple Auth : 0.8.0-beta.2
ember.debug.js:4888 DEBUG: -------------------------------
===== Updated with detailed solution based on answer from torazaburo ======
Creating a service works great. Here is how I did it using ember-cli (v0.2.6) and ember v1.12.0
Create your service inside of /app/services/<service-name>.js
The service blueprint will look like this (note the name of the service is based on the name of the file):
import Ember from "ember";
export default Ember.Service.extend({
myFunction: function(){
}
});
Create an initializer for your service in /app/initializers/<service-name>.js which is used to inject your service into the different top level Ember objects (such as routes, controllers, components etc). Note that the file name of the initializer should match the file name of your service.
The blueprint for the initializer will look like this:
export function initialize (container, app) {
// Your code here
}
export default {
name: '<service-name>',
initialize: initialize
};
To give a concrete example, lets say your service is called validator and contains a bunch of validation routines. You want to inject the validator into all controllers, and you also want to inject the Ember Data store into the validator itself. You can do it like this:
export function initialize (container, app) {
// Inject the Ember Data Store into our validator service
app.inject('service:validator', 'store', 'store:main');
// Inject the validator into all controllers and routes
app.inject('controller', 'validator', 'service:validator');
app.inject('route', 'validator', 'service:validator');
}
export default {
name: 'validator',
initialize: initialize
};
Make your utility into a "service", into which you can inject the store. Actually, it sounds like your utility should be a service anyway, even if it doesn't need the store. By making it a service, for instance, it becomes much easier to stub it out when writing tests. With a service, you need neither import anything nor do any global injections in initializers, you can simply say
export default Ember.Component.extend({
myService: Ember.inject.service(), // inject services/my-service.js
foo: function() {
this.get('myService').api1(...);
}
});

EmberJS: Change path to access a route

I have a Router.map defined to my application. I'm working with EmberJS AppKit architecture. https://github.com/stefanpenner/ember-app-kit
I'd like to access to my page "profile" using the following path:
http://localhost:8000/#/profile
But, the name of my route differ to this path, because it's call user-profile, so I did this:
router.js
var Router = Ember.Router.extend();
Router.map(function () {
this.resource('user-profile', { path: 'profile'}, function() {
//Some other things...
});
});
export default Router;
user-profile.js
export default Ember.Route.extend({
model: function () {
return this.store.find('user-profile');
}
});
When I launch my application, Ember is telling me that profile route doesn't exist, even though I defined the path:
Uncaught Error: Assertion Failed: Error: Assertion Failed: The URL '/profile' did not match any routes in your application
Do you know what's wrong with my code at this point?
Thanks
I dont use ember appkit but perhaps try with underscore, ie 'user_profile' and rename your file too. Just a shot in the dark.
I would have to guess it is the way that you are designing your router and the namespace.
Typically a barebones Ember app requires:
window.App = Ember.Application.create({
LOG_TRANSITIONS: true,
LOG_TRANSITIONS_INTERNAL: true
});
App.Router.map(function () {
this.resource('user-profile', { path: 'profile'}, function() {
//Some other things...
});
In your example your router is not in the App namespace, or whatever your root object is named (It doesn't have to be 'App'). I would give this a try or maybe post more code if there are other factors I do not see here.
Also, typically you would name your route userProfile. While i dont think the dasherized name is a problem, it doesn't follow Ember naming conventions.
Hope this helps.

Access App.method from within App.router.method

I'm building a backbone.js + require.js application and I have run into the following issue.To structure my application I have an App.js file which as the following contents:
define(function(require) {
'use strict';
var _ = require('underscore'),
Backbone = require('backbone'),
Router = require('router'),
ModuleManager= require('moduleManager');
var App = function App() {
var base = {
router: new Router(),
moduleManager: new ModuleManager(),
start: function start(){
Backbone.history.start({pushState: true});
this.router.navigate('home', {trigger: true});
}
};
return _.extend(
base,
Backbone.events
);
};
return App;
});
The application is started with window.myApp = new App();, then myApp.start();.
The contents of router.js are as follows :
define(function(require) {
'use strict';
var _ = require('underscore'),
Backbone = require('backbone');
var Router = Backbone.Router.extend({
routes: {'home': 'home'},
home: function home() {
// MY ISSUE IS HERE
App.moduleManager.add('moduleName');
}
});
return Router;
});
The moduleManager is a utility function/object for :
Adding application modules via App.moduleManager.add('module') by requiring require.js files (backbone views + collections),
Doing some checks (e.g. ensuring the module doesn't already exist),
Centrally storing modules in App.moduleManager.modules
Everything is working fine except for the following point :
How can I call App.moduleManager from within App.router.home or any other route (App.router.xyz) ?
Within App.router.home, this can't refer to App (?)
Within router.js, I can't call App = require('app') because I would be making a circular dependency between App.js and Router.js
I'm not sure if I have a global application structure problem or if there is just a simple language construct which can work around this problem. Thanks for any help you can provide.
You could pass the object you need in the router's constructor.
But I would suggest using events. In the route, trigger an event, then listen for that event in the app. This leaves the router to do a single job, responding to route changes from the browser (back/forward clicks).
In router:
home: function() {
Backbone.trigger('home:show');
}
In app:
start: function(){
Backbone.history.start({pushState: true});
Backbone.on('home:show', this.showHome, this);
},
showHome: function(){
this.router.navigate('home', {trigger: false}); // just update, dont trigger route
this.moduleManager.add('moduleName');
}
Now if you want to change what the app is showing from your code, you can just trigger this event, instead of calling navigate on the router.
Some other code, maybe a menu view:
homeClicked: function(){
Backbone.trigger('home:show');
}
This would show your home view, and also update the history.

Categories

Resources