I have a "copy link" input in my template that should display the currently loaded URL (with query params) to the user for them to copy & paste.
The input with the URL is part of the template belonging to a parent route that has a number of children:
- Parent route <-- Copy URL link in this template
- Child route
- Child route
- Child route
This means that as you navigate the child routes, the URL should update in the parent route. This is turning out to be very difficult to implement.
AFAIK there is no native event for URL change so I can't create a component and simply track any browser event. I tried hashchange on the window but this obviously only tracks the #and not URL or query params.
I can't use window.location.href when the page loads as any subsequent transitions to the child routes will not be reflected in the input.
I can't get the URL with window.location.href in the didTransition action (or any other route hooks) on the parent route because at that point the URL hasn't updated yet (i.e. I get the previous URL)
EDIT:
At the moment, this is the only approach that seems to work:
// In the parent route:
actions: {
didTransition() {
this._super(...arguments);
Ember.run.schedule('afterRender', () => {
this.set('controller.currentURL', window.location.href);
});
}
}
but seems pretty hacky
Since Ember 2.15 you can use the route service for this.
Example:
import { inject as service } from '#ember/service';
import { alias } from '#ember/object/computed';
export default Component.extend({
router: service(),
currentRoute: alias('router.currentURL')
});
I think you can benefit from router's location's path property. What I mean is, you can inject router to the controller you like with an instance-initializer and define a computed property to watch the current path. Please check out the following twiddle. I wrote an instance initializer named instance-initializers\routerInjector to inject application's router to every controller. I defined a computed property named location within application.js controller as follows:
location: Ember.computed.oneWay('router.location.path')
I added this to application.hbs. If I got what you want correctly; this is what you want.
Related
Suppose I have an Ember route like the following
myApp/RouteA/RouteB/RouteC
Is there a way that I could get all the names of different route levels in either my route or controller?
The reason is because I'm trying to write an ember component that dynamically generates breadcrumbs.
Here is a little more specific of an example
While #jelhan commented a good existing addon, it's straightforward to implement your own should you need to.
Ember has a public router service which can be used to get info about the current route. One such method that would work here is currentRouteName. Imagine the route structure:
Router.map(function() {
this.route('about');
this.route('blog', function () {
this.route('post', { path: ':post_id' });
});
});
currentRouteName will return
index when you visit /
about when you visit /about
blog.index when you visit /blog
blog.post when you visit /blog/some-post-id
You can split on the separator to get the different route parts to build up your breadcrumbs. In your breadcrumbs component, simply inject the routerService and use a computed property to get the current breadcrumbs (this will update when app changes routes)
import Component from '#ember/components';
import { inject } from '#ember/service';
import { computed } from '#ember/object';
export default Component.extend({
router: inject(),
breadcrumbs: computed('router.currentRouteName', function(){
// build your breadcrumbs however you see fit
})
})
In Ember you can call an action in your current context as you would a normal function, using this.actions.actionName(). This means the action can return a value.
In my login route, I am trying to call an action from the application route in this way. However this.actions only contains the actions in my login route.
Login Route
actions: {
test: function() {
console.log(this.actions.sameRouteAction()); //5
console.log(this.actions.applicationRouteAction()); // this.actions.applicationRouteAction() is not a function.
},
sameRouteAction: function() {
return 5;
}
}
Application Route
actions: {
applicationRouteAction: function() {
return 7;
}
}
What would this.actions.applicationRouteAction() have to become in order for this to work?
As of Ember 3.1.0, there isn't a way you can call the action directly on the parent route as a normal function like you can in the context of the current route/class. You can use the send function to send an action upward:
actions: {
test: function() {
this.send('applicationRouteAction');
},
}
But you won't be able to get the return value of the applicationRouteAction function this way.
The two best options for communicating the result of the action from the parent route to the child route are:
Updating the model for the parent route via transitionTo or refresh. This will update the route tree, and your child route can get the model of its parent via modelFor. Since you are using the Application route in your example, this seems like it's probably not the method you'll want to use.
Create or use an existing Service to communicate between the routes. You can inject the service in both locations, update its state from the Application route, and read its state from the child route.
If you can add more details about what you want the actions to do I can update this answer with more details about which pattern makes more sense and why!
Hey I'm facing a problem with removing a view.
The view is used as navbar
{{view "inner-form-navbar" navbarParams=innerNavObject}}
Where params look like this
innerNavObject: {
...
routeToReturn: 'someroute.index',
...
},
On the navbar there's a small "back" button when it's clicked the parent index route is opened.
It currently works like this:
this.get('controller').transitionToRoute(routeToReturn);
But this won't work in a component and is sketchy anyways. Do i need to somehow inject router to component? Or has anyone gotten a solution for this? The navbar is used in so many places so adding a property to navbarObject to have certain action defined is not a really good solution imo.
Went for this solution :
export default {
name: 'inject-store-into-components',
after: 'store',
initialize: function(container, application) {
application.inject('component', 'store', 'service:store');
application.inject('component', 'router', 'router:main');
}
};
Now i can do
this.get('router').transitionTo('blah')
Well you can try to use a service that provides the routing capabilities and then inject into the component.
There's an addon that seems to do just that - ember-cli-routing-service
Example taken from the link, adapted for you scenario:
export default Ember.Component.extend({
routing: Ember.inject.service(),
someFunc () {
this.get('routing').transitionTo(this.get('innerNavObject'). routeToReturn);
}
});
Having a component control your route/controller is typically bad practice. Instead, you would want to have an action that lives on your route or controller. Your component can then send that action up and your route or controller will catch it (data down, actions up).
In your controller or route, you would have your transition action:
actions: {
transitionFunction(route) {
this.transitionTo(route);
}
}
You would also define the the current route name in your route or controller and pass that to your nav bar component. Controller could then look like:
export default Controller.extend({
application: inject.controller(),
currentRoute: computed('application.currentRouteName', function(){
return get(this, 'application.currentRouteName');
}),
actions: {
transitionFunction(route) {
this.transitionTo(route);
}
}
});
Then call your component and pass the currentRoute CP to it:
{{nav-bar-component currentRoute=currentRoute action='transitionFunction'}}
Then, in your component, you can have a function that finds the parent route from the currentRoute:
export default Component.extend({
click() { // or however you are handling this action
// current route gives us a string that we split by the . and append index
const indexRoute = get(this, currentRoute).split('.')[0] + '.index';
this.sendAction('action', indexRoute);
}
});
Extending a route
Per your comment, you may want to have this across multiple routes or controllers. In that case, create one route and have your others extend from it. Create your route (just as I created the Controller above) with the action. Then import it for routes you need:
import OurCustomRoute from '../routes/yourRouteName';
export default OurCustomRoute.extend({
... // additional code here
});
Then your routes will have access to any actions or properties set on your first route.
I needs to apply an "active" class to a bootstrap tab depending on the current route name. The route object contains "routeName" but how to I access this from a controller or component?
Use this this.controllerFor('application').get('currentRouteName');
In fact, you don't need to apply active class by yourself. A link-to helper will do it for you.
See here:
{{link-to}} will apply a CSS class name of 'active' when the application's current route matches the supplied routeName. For example, if the application's current route is 'photoGallery.recent' the following use of {{link-to}}:
{{#link-to 'photoGallery.recent'}}
Great Hamster Photos
{{/link-to}}
will result in
<a href="/hamster-photos/this-week" class="active">
Great Hamster Photos
</a>
In the absolutely desperate case, you can look up the router, or the application controller (which exposes a 'currentRouteName' property) via this.container.lookup("router:main") or this.container.lookup("controller:application") from within the component.
If it was a common trend for me, I would make a CurrentRouteService and inject it into my component(s) so that I can mock things more easily in my tests.
There may also be a better answer to come along - but the container.lookup() should knock down your current blocker.
Since Ember 2.15 you can do this through the public Router service.
router: service(),
myRouteName: computed('router.currentRouteName', function () {
return this.get('router.currentRouteName') + 'some modification';
}
https://www.emberjs.com/api/ember/release/classes/RouterService
Which worked really well for me since I wanted something computed off of the current route. The service exposes currentRouteName, currentURL, location, and rootURL.
currentURL has the query params, but you would need to parse them from the URL.
For Ember 2, from a controller you can try :
appController: Ember.inject.controller('application'),
currentRouteName: Ember.computed.reads('appController.currentRouteName')
Then you can pass it to component.
Try this.
export default Ember.Route.extend({
routeName: null,
beforeModel(transition){
//alert(JSON.stringify(transition.targetName) + 'typeof' + typeof transition.targetName);
this.set('routeName', transition.targetName);
},
model(){
// write your logic here to determine which one to set 'active' or pass the routeName to controller or component
}
`
Using insights from #maharaja-santhir's answer, one can think of setting the routeName property on the target controller to use, e.g., in the target's template. This way there's no need for defining the logic in multiple locations and hence code-reusability. Here's an example of how to accomplish that:
// app/routes/application.js
export default Ember.Route.extend({
...
actions: {
willTransition(transition) {
let targetController = this.controllerFor(transition.targetName);
set(targetController, 'currentRouteName', transition.targetName);
return true;
}
}
});
Defining this willTransition action in the application route allows for propagating the current route name to anywhere in the application. Note that the target controller will retain the currentRouteName property setting even after navigating away to another route. This requires manual cleanup, if needed, but it might be acceptable depending on your implementation and use case.
I have an Ember application composed from 3 routes:
router.route('territory', { path: 'localhost/app/territory/:tid' });
router.route('aggregator', { path: localhost/app/territory/:tid:/aggregator/:aid' });
router.route(territory, { path: 'localhost/app/territory/:tid/aggregator/:aid/item/:iid' });
the possibles transition are from territory to aggregator, from aggregator to item, and from item to a sub item.
The sub item use the same route (the 3rd), just changing the iID value in the model of route.
I had created an action that allows the user to move into a particular route with some logic and at the end run the command:
model={
tid: "ttt"
aid: "aaa"
iid: "iii"
}
destination = 'item'; //the name of item route
controller.transitionToRoute(destination, model);
If I'm in the item route and I want to move to an other item, the URL will update, but not the content of the page. Obviously if I refresh the page with the generate URL the content will update.
Where is the problem? in the transition method that is Deprecated, or i have to use something different?
IMPORTANT: I'm using EmberJS - V1.0.0-RC.1
Is not a bug is just a normal situation in emberjs because every route have model and setupController.
The model function is used to retrive asynchronously from WS or Data module the necessary information (is a RSVP.Promise). When is complete the information will be passed to setupController function, where will be possible set properties of the controller connected with the view of current route.
Every time that i change the value of path but not the route, only setupController will be called.
To conclude, in my case, the problem was just an organisation problem of code.