I'm trying to create a service to handle modal dialogs in Ember 1.12 with Ember-cli. Maybe a service isn't the best way to approach this, but I want to access it from just about anywhere in the app, and be able to dynamically insert content into the popup, so it seemed like the right way to go.
Here is my service:
import Ember from 'ember';
export default Ember.Service.extend({
route: Ember.inject.service('route'),
open: function(content){
console.log('open popup', content);
this.get('route').render('popup-box', { //popup-box is a component
into: 'application',
outlet: 'popup'
});
},
close: function(){
//TODO
}
});
When I call the open method, I get this error:
Uncaught Error: Attempting to inject an unknown injection:
service:route
I'm not sure what I'm missing. Suggestions?
You should check out ember-wormhole, https://github.com/yapplabs/ember-wormhole. It will let you target a section of your template to an anchor somewhere else in the dom. It's perfect for modals!
Additional Info:
AS #runspired pointed out, you can't inject the router like you have it.
If you did want to inject the router, you could do so with Ember.inject.service('-routing') or via application.inject('<myTarget>', 'router', 'router:main'); from an initializer.
However, you do not have access to a render method and this could be considered a smell.
Related
EmberJS 3.4
I'm loading a Project entity from a backend which takes a couple of seconds. Now I would like to show a spinner during loading.
as described I created a project-loading.hbs (also tried with loading.hbs) https://guides.emberjs.com/release/routing/loading-and-error-substates/
project model class:
export default AuthenticatedRoute.extend({
model(params) {
return this.store.findRecord("project", params.projectname);
},
actions: {
refresh: function() {
this.refresh();
}
}
});
though it takes time to load the entity, the loading template seems not to be rendered/shown. Am I doing something wrong ?
For a route called project (routes/project.js), the loading template should be called project-loading.hbs.
I cloned your project and actually made it work (Ember CLI 3.4.3) by adding templates/project-loading.hbs, adding a sleep(30) call to your /api/projects/:name endpoint and going to a URL like http://localhost:4200/projects/hallo.
Do you have the problem when transitioning to the route internally (by using transitionTo or the {{link-to}} helper with a model for instance) or by entering the URL manually? Note that model hook is not executed when you transition to a route and pass in a model context (see https://guides.emberjs.com/v3.4.0/routing/specifying-a-routes-model/).
I ended up with adding the following code to the application adapter:
// not very ember way of doing this, but quite simple :)
$(document).ajaxStart(() => {
$('#spinner').removeClass('hide');
});
$(document).ajaxStop(() => {
$('#spinner').addClass('hide');
});
I really would prefer doing it the ember way. for now, this seems to do the trick.
for anyone interested, here's the complete project: https://github.com/puzzle/mailbox-watcher/tree/master/frontend
as the title says, I have some problems understanding how to use an adapter in an ember-engines.
I am currently running my application with ember#2.15, ember-data#2.15 and ember-engines#0.5.14.
I already used an adapter in my main application, but if I tried to reproduce a basic adapter in my application, the page is loading indefinitely.
In my route, I call my adapter with the findAll method:
model()
{
"use strict";
console.log('In my route');
return this.get('store').findAll('my-adapter-name', {reload: true});
}
And in my adapter, I respect the syntax that I used in my in-app adapter:
import DS from 'ember-data';
import Ember from 'ember';
export default DS.Adapter.extend({
findAll: function (store, type, sinceToken, snapshotRecordArray)
{
console.log("In my adapter");
return new Ember.RSVP.Promise(function (resolve, reject)
{
// I accede to my websocket service here, nothing to do with my question
});
},
/* *
* My other function, like findRecord, ect...
*/
});
As you can see, I put some console.log in my code, and I can access the message in my route, which is in my engine, but I can't access the message in my adapter, which is also in my engine.
I tried to put a console.log in an adapter in my application, and the message is properly shown, so I am sure this is because I can't access to the adapter in my engine, so if anybody has an idea to how we should configure our adapter in our ember-engines, this would be very appreciated.
For your information, this is an in-repo engines.
Just found it, this is a bit tricky, but your models (and adapters) should be in the myApp/lib/myEngine/app/models/, and not in myApp/lib/myEngine/addon/models.
I don't know if this is intended this way, but this is the only way I found to add model in your in-repo ember-engines.
EDIT
This will do the trick for the serializers and the transform.
I'm using ember-cli 1.13.8 and I have a service that handles most of my logic. Right now I have a function that listens to whether certain things are true or false and then can make a route change based upon that. I'd rather not have to call that function from inside every route since I want it to happen on every route. Its goal is to determine whether the player won and every interaction in the game drives this.
Inside of my game service:
init() {
...
if(true) {
console.log("you've won!");
this.transitionTo("congratulations");
}
},
Of course, this fails because this isn't a route like Ember expects. I know I can call this method from inside of every route instead but I'm wondering if there is a better way to do this.
Thanks
Edit
So far I've tried importing in the App and then trying to extend the Router. This seems like a bad idea though.
You can use the routing service (which is a private API):
routing: Ember.inject.service('-routing'),
init() {
...
if(true) {
console.log("you've won!");
this.get("routing").transitionTo("congratulations");
}
},
As of Ember 2.15, there is a public router service for exactly this use case. Just add router: Ember.inject.service(), to your Ember class and call this.get('router').transitionTo(...);, easy!
Generally this is a bad idea, but in some cases it's easier than passing through route actions in 100 places (personal experience).
The better way to do this from anywhere is to look the router up on the container:
Ember.getOwner(this).lookup('router:main').transitionTo(...);
this has to be some container allocated Ember object, which includes components, services, and Ember Data models.
Note also that if this will be called a lot, you will want to store the router as a property. You can do this in the init hook:
init() {
this._super(...arguments);
this.set('router', Ember.getOwner(this).lookup('router:main'));
}
...
this.get('router').transitionTo(...);
Ember.getOwner(this) works in Ember 2.3+, prior to that you can use this.get('container') instead.
Ember 1.13:
Create another service called routing:
import Ember from 'ember';
export default Ember.Service.extend({
_router: null,
init() {
this._super();
this.set('_router', this.get('container').lookup('router:main'));
},
transitionTo() {
this.get('_router').transitionTo(...arguments);
}
});
Then you can:
routing: Ember.inject.service(),
goSomewhere() {
this.get('routing').transitionTo('index');
}
I'm very new to EmberJS 2.0 and trying to slowly understand it by building my own website with it. Anyways, I've managed to get Firebase integrated with Ember and my controller is able to authenticate correctly. However, I'd like to understand why when I execute:
this.send('toggleModal');
inside the authenticate action property function (.then()) it doesn't work but if I execute it outside then everything works fine.
1) Is the 'this' keyword getting confused with something other than the Ember controller?
Here is the sample:
// /app/controllers/application.js
import Ember from 'ember';
export default Ember.Controller.extend({
isShowingModal: false,
actions: {
toggleModal: function() {
this.toggleProperty('isShowingModal');
},
authenticate: function(username, pass) {
this.get('session').open('firebase', {
provider: "password",
email: username,
password: pass
}).then(function (data) {
console.log(data.currentUser);
console.log(session.isAuthenticated); //Why is 'session' not defined?
this.send('toggleModal'); //This doesn't work. Throws an error.
});
this.send('toggleModal'); //This works.
},
logOut: function() {
this.get('session').close();
}
}
});
2) Also, I've noticed that when using Emberfire I'm able to use the property 'session.isAuthenticated' within the template application.hbs however, shouldn't 'session' be an object that is injected to all routes and controllers using Torii? Why is that property inaccessible/undefined within the application.js controller? I'm using https://www.firebase.com/docs/web/libraries/ember/guide.html#section-authentication as a reference.
3) In the guide above the actions for authentication are put inside the route. However, according to this quora post the route should only handle template rendering and model interfacing. Is this post incorrect? The authentication logic should reside in the application.js controller correct? https://www.quora.com/What-is-the-best-way-to-learn-Ember-js
1) Is the 'this' keyword getting confused with something other than the Ember controller?
Yes. This is one of the most common sticking points of Javascript. There's a lot of articles out there about it, but this one looked pretty good. To solve it you'll either need to use an arrow function, bind the function to the current context, or save the context in a local variable. (Read that article first though.)
2) Also, I've noticed that when using Emberfire I'm able to use the property 'session.isAuthenticated' within the template application.hbs however, shouldn't 'session' be an object that is injected to all routes and controllers using Torii? Why is that property inaccessible/undefined within the application.js controller? ...
That's because the template pulls the property from the current context (your controller). Inside of your controller you'll have to use this.get('session') instead. (After you fix the issue I mentioned above.)
3) ... Is this post incorrect? ...
I wouldn't say incorrect, just a bit oversimplified. I would follow whatever conventions the library uses as that's probably the best way given the library's requirements.
You're partially right about this although it's not really confused. this (where you're modal call doesn't work) isn't scoped to the Controller anymore, because it's inside a function. Either:
replace the function (data) call with data => if you're using ember cli. Or
var _self = this; up top and reference _self instead.
This should at least get you started.
I've got openModal action defined on application route. I'm trying to call this action from within a component.
If I use syntax for action bubbling:
{{my-component openModal="openModal"}}
then everything works as expected and I can trigger this action using this.sendAction("openModal").
However, I'm not sure how to get the same result using the new closure syntax:
{{my-component openModal=(action "openModal")}}
In this case, Ember complains that there's no action openModal defined on the controller. Do I have to define this action on every controller that uses my-component? Is there a way to somehow use target option to tell Ember that this action is defined on a route? Is it ok to mix bubbling and closure syntax in a single component?
I'm using Ember 2.0 beta 1.
Until routable components are introduced somewhere in Ember 2.1 or 2.2 or 2.3 or 2.4 or 2.5 or 2.6 or 2.7, it is impossible to pass a closure action from a route.
For now, you can only pass closure actions from a controller and on to child components.
UPD: Miko Paderes hints that an addon is available: https://github.com/dockyard/ember-route-action-helper
Try this:
{{my-component openModal=(action send "openModal")}}
...which makes use of the send method on the controller.
I can't say I fully understand it given that the second argument to send is the context but it is still passing additional arguments to my action in the route correctly. I'm currently using version 2.4.5.
You can send closure actions to the route by proxying them through the controller. This works with any Ember version that supports closure actions. The only caveat is that the action name must differ from controller and route.
For instance, given this template:
{{foo-component myAction=(action 'foo')}}
You would need a foo action on your controller, which could proxy to the route:
proxyFooTo: 'fooBar',
actions: {
foo(context) {
this.send('proxyFooTo', context);
}
}
Then on the route
actions: {
fooBar(context) { ... handle fooBar ... }
}
It is also possible to avoid creating a controller, or if there is one already avoid adding more logic in it, by specifying the target property
http://emberjs.jsbin.com/fiwokenoyu/1/edit?html,js,output
in route
js
setupController(controller,model){
this._super(...arguments);
controller.set('theRoute', this);
},
actions:{
openModal(data){
/*...*/
}
}
in template
hbs
{{my-component openModalAction=(action 'openModal' target=theRoute)}}
Try the Ember add-on called ember-route-action-helper,
https://github.com/dockyard/ember-route-action-helper
You can just replace route-action with action at the time "routable- components" become available.
{{todo-list todos=model addTodo=(route-action "addTodo")}}
So the code above has a similar effect as
Inside the route,
setupController(controller,model){
this._super(...arguments);
controller.set('theRoute', this);
},
actions:{
addTodo(data){
/*...*/
}
}
Inside the HBS,
{{todo-list addTodo=(action 'addTodo' target=theRoute)}}