When the main view of my application is switched (new route that reconnects the main outlet of my application controller) I want the page to be scrolled to the top. Otherwise it's a bit strange that I navigate to another page-like view and the viewport is still lost somewhere where I left off.
I hacked a solution and wonder if there's a better way or if anyone has the same thing.
Here's what I do:
App.ApplicationController = Ember.Controller.extend({
connectOutlet: function(){
window.scrollTo(0, 0);
this._super.apply(this, arguments);
}
});
#Baruch's solution is good, but when I implemented it I had render on elements within my application state and would cause a scrollTop when it was not needed.
I found this to be much more effective as it only runs on the path change:
App.ApplicationController = Ember.Controller.extend({
currentPathChanged: function () {
window.scrollTo(0, 0);
}.observes('currentPath')
});
I achieved this with the following code:
Ember.Route.reopen({
render: function(controller, model) {
this._super();
window.scrollTo(0, 0);
}
});
Coffee Script:
Ember.Route.reopen
activate: ->
#_super()
window.scrollTo(0, 0)
Javascript:
Ember.Route.reopen({
activate: function() {
this._super();
window.scrollTo(0, 0);
}
});
You should probably try and extend Ember.Route and add your window.scrollTo in the enter callback. Then instead of using Ember's Route for your leaf routes, you call your route .extend(), so they'll automatically scroll up when you enter a route/state. Something similar to this:
// define your custom route and extend "enter"
var MyRoute = Em.Route.extend({
enter: function(router) {
// for now on, all the routes that extend this,
// will fire the code in this block every time
// the application enters this state
// do whatever you need to do here: scroll and whatnot
}
});
App.Router = Em.Router.extend({
enableLogging: true,
location: 'hash',
index: Em.Route.extend({
route: '/',
connectOutlets: function(router) {
...
},
// on your leaf routes, use your own custom route that
// does your scroll thing or whatever you need to do
home: MyRoute.extend({
route: '/',
connectOutlets: function (router, context) {
...
}
}),
// other routes...
})
});
does it make sense?
It's now render(name, options), and if you are specifically calling render (ie with a modal) you want to pass that to super()
Ember.Route.reopen({
render: function(name, options) {
if (name != null) {
return this._super(name, options);
} else {
return this._super();
}
}
});
Ember 3.12+ (this is technically 3.20 code listed here)
import EmberRouter from '#ember/routing/router';
const Router = EmberRouter.extend({
init() {
// call event everytime route changes
this.on('routeDidChange', () => {
this._super(...arguments);
window.scrollTo(0, 0); // scrolls to top
});
}
});
Router.map(function () {
// your mapping code goes here
});
export default Router;
Prior to 3.12 (this is technically 3.4 but the key code should be the same)
import EmberRouter from '#ember/routing/router';
const Router = EmberRouter.extend({
didTransition() {
this._super(...arguments);
window.scrollTo(0, 0);
}
});
Router.map(function () {
// your mapping code goes here
});
export default Router;
We have handled this problem serveral times and the way we've found that is the easiest and most straight-forward way is to configure this once in the router.js file using a 'route transition' event function. We used didTransition before it got deprecated in Ember 3.12 in lieu of routeDidChange. I've posted both examples below. Some syntax may differ slightly depending on which version of Ember you are on but this core code should be the same.
Related
I looked through related posts for hours, but could not find the right answer to fix the problem I'm having.
I keep getting the error:
Uncaught Error: Nothing handled the action 'edit'. If you did handle the action, this error can be caused by returning true from an action handler in a controller, causing the action to bubble.
I think that the controller is handling things wrong, or it's bubbling up to a wrong route?
App.EventDetailsController = Ember.ObjectController.extend({
isEditing: false,
actions: {
edit: function() {
this.set('isEditing', true);
},
doneEditing: function() {
this.set('isEditing', false);
}
}
});
App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
this.route('events', {path: '/events'});
this.route('createevent', {path: '/createevent'});
this.route('eventdetails', {path: ':eventdetails_id'});
});
App.EventsRoute = Ember.Route.extend({
model: function() {
return events;
}
});
App.EventDetailsRoute = Ember.Route.extend({
model: function(params) {
return events.findBy('id', params.eventdetails_id);
}
});
Does anyone know why this wouldn't work?
You probably want to define your routes like this:
App.Router.map(function() {
this.resource('events', function() { // /events <-- your event listing
this.resource('event', {path: ':event_id'}, function() { // /events/1 <-- your event details
this.route('edit'); // /events/1/edit <-- edit an event
});
this.route('create'); // /events/create <-- create your event
});
});
But aside from that, note that actions bubble up through the Routes, so try moving your actions handler to the EventDetailsRoute instead.
Read the part in the guide that talks about it here: http://emberjs.com/guides/templates/actions/#toc_action-bubbling
App.EventDetailsRoute = Ember.Route.extend({
actions: {
edit: function() {
this.set('isEditing', true);
},
doneEditing: function() {
this.set('isEditing', false);
},
//or maybe better:
toggleEditing: function() {
this.toggleProperty('isEditing');
}
},
model: function(params) {
return events.findBy('id', params.eventdetails_id);
}
});
I have a suspicion it has to do with not using the proper naming conventions. If your route's name is EventDetailsRoute then the route should be referenced in the router as event-details.
This problem is caused when our template and controller name is different. Please check your template and controller name
I have also faced similar problem. But in my case, I have called a route action from a controller action in which, I have used transitionToRoute.
Since the transition to another route was finished even before the completion of route action, the error was thrown " Nothing handled the action actionName. If you did handle the action, this error can be caused by returning true from an action handler in a controller, causing the action to bubble".
Reference: https://discuss.emberjs.com/t/sending-action-from-current-controller-to-current-route/6018
Given a application route like this:
App.ApplicationRoute = Ember.Route.extend({
model: function() {
return new Ember.RSVP.Promise(function(resolve) {
setTimeout(function() {
resolve();
}, 3000);
});
}
});
How do I show a loading template while this model hook is waiting?
I tried something like this:
<script type="text/x-handlebars" id="loading">
<h3>Loading...</h3>
</script>
But this only displays when a sub route of the application is loading. How do I show a loading template when the application itself is still loading?
Thank you.
You could make use of some sort of loading overlay (which could be some static html/css) and the afterModel route hook:
App.ApplicationRoute = Ember.Route.extend({
model: function() {
return new Ember.RSVP.Promise(function(resolve) {
setTimeout(resolve, 3000);
});
},
afterModel: function (model, transition) {
$('.loading-overlay').fadeOut();
}
});
You would have to determine the best place to put your overlay, but this should work.
Working example: http://jsbin.com/rixukune/3
Take a look at this hook's details here in the docs: http://emberjs.com/api/classes/Ember.Route.html#method_afterModel
Something like document ready, but after all Ember views rendering
I am doing this right now with an override on ApplicationView didInsertElement, which seems to be working so far:
App.ApplicationView = Em.View.extend({
didInsertElement: function() {
// Do your magic.
}
});
I am wondering if this is the right way for an Ember document ready, or if Ember has a more native support for this simple and very common thing.
You can easily add a "post render" hook by reopening the base View class and adding it into the render queue.
Here's some code to show you how:
Ember.View.reopen({
didInsertElement : function() {
this._super();
Ember.run.scheduleOnce('afterRender', this, this.didRenderElement);
},
didRenderElement : function() {
// Override this in your View's
}
});
The didInsertElement is the right place, but if you want to be completely sure your render queue is completely flushed you could also listen to the afterRender event, something like this:
App.ApplicationView = Ember.View.extend({
didInsertElement: function() {
Ember.run.scheduleOnce('afterRender', this, 'processChildElements');
},
processChildElements: function() {
// do here what you want with the DOM
}
});
Hope it helps.
App.ApplicationView = Ember.View.extend({
afterRender: function () {
Ember.run.next(this, function () {
// This will run one time, after the full initial render.
});
}
});
My routing structure:
App.ready = function() {
App.Router.map(function() {
this.resource('contacts', function() {
this.resource('contact', function() {
});
});
});
}
Now in my contactsController I respond to and add action that transitions to the contact route. I would then like to call the add method on my contactController.
I have placed the needs: ['contact'] on my ContactController but then I get this message:
<App.ContactsController:ember197> needs controller:contact but it does not exist
When I use controllerFor (which is deprecated) I also get an error:
this.controllerFor('contact').add();
So Ember.js RC1 appears to only create the controllers (and other related instances) once one actually transitions to the appropriate route.
Is there a way around this.
So Ember.js RC1 appears to only create the controllers (and other related instances) once one actually transitions to the appropriate route.
Interesting - I had thought ember generated controllers earlier but guess not.
Is there a way around this?
Workaround is to define App.ContactController manually. Something like this will work:
App = Ember.Application.create({});
App.Router.map(function() {
this.resource('contacts', function() {
this.resource('contact', function() {
});
});
});
App.ContactController = Ember.Controller.extend({
add: function() {
alert('App.ContactController.add() was called!');
}
});
App.ContactsController = Ember.Controller.extend({
needs: ['contact'],
add: function() {
this.get('controllers.contact').add();
}
});
http://jsbin.com/osapal/1/edit
So I'm writing a POC app, and I am running into an issue after upgrading my Ember library to RC1. I noticed that when I was transitioning to a route in the new version, a stringified version of the object appears to show up in the URL, like so...
http://localhost:3000/posts/<App.Post:ember269:511401b8c589137c34000001>
The routes work successfully when transitioned to like this, but obviously trying to visit a URL like that a second time won't work. So I figured I would edit my code to transition to the ID instead.
For my edit route, I have the following save event.
events: {
save: function(post){
post.one('didUpdate', this, function(){
this.transitionTo('posts.show', post);
});
post.get('transaction').commit();
}
}
This produces a URL like the above when the transition happens. So I corrected it to the following...
events: {
save: function(post){
post.one('didUpdate', this, function(){
this.transitionTo('posts.show', post.id);
});
post.get('transaction').commit();
}
}
This produces the correct URL format, but the show route doesn't produce any output. (note that show output DOES produce output when I visit the URL for the first time with a correct format, just not when I transition to it from the edit route).
App.PostsShowRoute = Em.Route.extend({
model: function(params){
return App.Post.find(params.id);
},
setupController: function(controller, model){
controller.set('content', model);
}
});
So I'm confused. Any insight into the cause of this problem (and if you know why the RC produces it) would be much appreciated. Help me have my cake and eat it, too. Thanks!
From your App.PostsShowRoute I can guess that you set up your route mapping like this:
App.Router.map(function() {
this.resource('posts', function() {
this.route('show', { path:'/:id' });
});
});
You need change :id to :post_id:
App.Router.map(function() {
this.resource('posts', function() {
this.route('show', { path:'/:post_id' });
});
});
Now, since you are using Ember conventions, you can take advantage of that by deleting the whole App.PostsShowRoute = Em.Route.extend ... because Ember can take care of it for you.
And use your first method which was correct:
events: {
save: function(post){
post.one('didUpdate', this, function(){
this.transitionTo('posts.show', post);
});
post.get('transaction').commit();
}
}