可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm making a reusable (sort of polymorphic) comments widget. I want a button that enables me to go back to the parent route (e.g., if I'm at /blog_posts/1/comments, I want the button to take me back to /blog_posts/1).
I'm currently using transitionToRoute('blog_post')
, but this won't work in the long run because I want this code to generically run with any model (i.e. it won't know anything about the route's name, so I can't call 'blog_post'). I guess I'm looking for either something like Rails' url_for
, or a way of saying router.get('parent_route')
(or controller.get('parent_route')
).
Any pointers or ideas? Thanks.
回答1:
This was answered in the year 2013 for ember v0.9.8 or something, the framework has come a long way since then. I think there might be a much better solution with the current version of the framework. Since, I'm not in touch with Ember, can't really update this answer!
Inspired from a private function in the source code, Add the following method to the Routes which returns the name of the parentRoute
Ember.Route.reopen({ getParentRoute: function(){ var route = this; var handlerInfos = route.router.router.currentHandlerInfos; var parent, current; for (var i=0, l=handlerInfos.length; i<l; i++) { current = handlerInfos[i].handler; if((current.routeName == route.routeName)||(current.routeName.match(/./) && current.routeName.split('.')[1] == route.routeName )){ return parent.routeName; } parent = current; } } })
Use inside routes as follows
App.SomeRoute = Ember.Route.extend({ events: { goBack: function(){ this.transitionTo(this.getParentRoute()); } } })
Handlebars
<script type="text/x-handlebars" data-template-name="some"> <a href="#" {{action goBack}}>Back</a> </script>
For the actual code, open this and do a CTRL+F for function parentRoute
回答2:
You can use a generic {{action }}
in your template but have the specifics of the "go back" implemented in the router. This works because Ember bubbles up the goBack
event first to the controller, then the current route, then the parent route, etc, until an action handler is found. In this case, there is no matching action on the controller, so it is handled by the current route. This keeps your template/view/controller agnostic to the specifics, but when you are hooking up the widget you can specify how the "go back" should be handled.
In the example the same button causes the route to change to different routes depending on which route is the current route:
- in route
/posts
the back button transitions to /
- in route
/posts/new
the back button transitions to /posts
JSBin example
Javascript:
App = Ember.Application.create({}); App.Router.map(function() { this.resource('posts', function() { this.route('new'); }); }); App.PostsRoute = Ember.Route.extend({ events: { goBack: function(){ this.transitionTo('index'); } } }); App.PostsNewRoute = Ember.Route.extend({ events: { goBack: function(){ this.transitionTo('posts'); } } });
Template:
<script type="text/x-handlebars" data-template-name="index"> <h2>Index Content:</h2> {{#linkTo posts}}Posts{{/linkTo}} {{outlet}} </script> <script type="text/x-handlebars" data-template-name="posts"> <h2>Posts</h2> <button {{action goBack}}>Go back</button> {{#linkTo posts.new}}New{{/linkTo}} {{outlet}} </script> <script type="text/x-handlebars" data-template-name="posts/new"> <h2>Posts/New</h2> </script>
回答3:
Based on Mudasi Ali's lead and reviewing the Ember v1.5 source I use the following:
Coffeescript (use coffeescript.org to transpile if you want JS/ES):
Ember.Route.reopen parentRoute: Em.computed -> r = @router.router if r.currentTransition handlerInfos = r.currentTransition.state.handlerInfos else handlerInfos = r.state.handlerInfos handlerInfos = this.router.router.state.handlerInfos return unless handlerInfos parent = @ for info in handlerInfos break if info.handler == @ parent = info.handler parent parentRouteName: Em.computed.alias('parentRoute.routeName') parentController: -> @controllerFor @get('parentRouteName') parentModel: -> @modelFor @get('parentRouteName')
The above provides the properties parentRoute
and parentRouteName
, on all your routes and two handy functions parentController()
and parentModel()
which return the parent controller and model respectively which can be useful for many situations, especially if you represent editing a resource as a nested route.
You can also define some actions to use in your views/controllers etc for cancel/back handling as follows:
Ember.Route.reopen actions: goBack: -> @transitionTo @get('parentRouteName')
If you have a deep routing hierarchy and wanted to say skip an intermediate route, you just need to override goBack as follows:
App.SomeIntermediateRouteToSkipOnBack = Em.Route.extend actions: goBack: -> # skip the immediate parent and use the grandparent route @transitionTo @get('parentRoute.parentRouteName)
回答4:
Updated for Ember 2.6 in file app/initializers/parent_route.js
import Ember from 'ember'; var alreadyRun = false; export default { name: 'parent-route', initialize: function() { if (alreadyRun) { return; } else { alreadyRun = true; } Ember.Route.reopen({ parentRoute: Ember.computed(function() { let handlerInfos, i, info, len, parent, r; r = this.router.router; if (r.activeTransition) { handlerInfos = r.activeTransition.state.handlerInfos; } else { handlerInfos = r.state.handlerInfos; } if (!handlerInfos) { return; } parent = this; for (i = 0, len = handlerInfos.length; i < len; i++) { info = handlerInfos[i]; if (info.handler === this) { break; } parent = info.handler; } return parent; }), parentRouteName: Ember.computed.alias('parentRoute.routeName'), parentController: Ember.computed(function() { return this.controllerFor(this.get('parentRouteName')); }), parentModel: Ember.computed(function() { return this.modelFor(this.get('parentRouteName')); }) }); } };
In your routes you can access the parentcontroller like this.get('parentController')
and the model like this.get('parentModel')