I know Ember's mantra is URL first and the URL should be a serialization of the page contents, but I have a scenario where I want to create a route for a component/page that will live in a modal, and be accessible from various places throughout the site. I always want this URL to represent the actual route of the resource, not change if the resource happened to be nested in another route.
A similar example is Pinterest. When you are looking at the feed of pins you are on the root route. You can also do a search for pins and you are then directed to the /search/pins route.
From both of these routes, you can click on a pin, which will open it up in a modal/overlay, and also change the url to the url of the pin. Since you can view pins from different areas of the site, the background behind the overlay will be various routes depending on where you click the pin from, not a serialized version of the route.
Is this type of functionality possible in Ember? If so, any resources/hints to review?
- ac.
This SO Question might be a pointer, I have done this before using the render method inside of the activate hook in the route.
So my modal template is injected into a named outlet (e.g {{outlet 'modal'}}) when you enter a given route.
Here is an example of the method I described in a project that I am currently working on.
In the parent route I have an action called openEditModal (in my case I do a load of logic like getting the model of the view where the edit button is pressed and setting that on the edit routes controller). This action ultimately transitionTo the edit route.
openEditModal: function(eventModel) {
// Do any logic here like setting the model for the modal
// Transition to the edit route
this.transitionTo('patient.events.edit');
},
So when the edit route is active:
activate: function() {
// render the template in the 'modal' outlet
this.render('path/to/modal/template', {
controller: 'patient.events.edit', // The controller to use for the injected template
view: 'patient.events.edit', // The view to use for the injected template
into: 'patient.events', // The parent template to inject this template into
outlet: 'modal' // The name of the outlet in that parent template
});
},
Related
I'm playing with ideas for making a drag and drop interface. I have multiple components that can be dragged into position over a grid. I will have a button that will allow the user to select different arrangements.
I want a function on the component that sets its position (quite simply with left and top). I want to be able to call this function when the component is inserted, and when a different arrangement is selected.
What I want to do is create an event in the route's controller. I then want each component to be able to listen for the event, and react as necessary. I'm afraid I don't understand enough to make this work. I've read the API, and a couple of questions on here (1, 2), but no luck.
Here's what I have...
The route's controller:
import Ember from 'ember';
export default Ember.Controller.extend(Ember.Evented, {
actions: {
callPosition: function(){
this.trigger('position');
console.log('Trigger set...');
},
})
And the component:
import Ember from 'ember';
export default Ember.Component.extend(Ember.Evented, {
didInsertElement : function(){
this.get('controller').on('position', this, this.position);
},
position: function(){
console.log('event heard by the component');
},
A few things I don't understand:
Have I added the Ember.Evented mixin correctly?
I've added the listener in the didInsetElement event as per the example. Is this the correct way to ensure the component will listen for the event throughout its lifetime?
What's the scope of the event? Can it only be listened for so long at we 'get' the controller that set it?
Your advice is a great help to this budding amateur!
You should not try to fire an event on components from the controller! What you should do is to pass the variables (left, top) and then register an observer on them on the component
sizeChanged: Ember.on('didInsertElement', Ember.observer('top', 'left' function () {
// arrange it
}))
Now you pass down these attributes when you call the component:
{{my-component top=myCoolDnDElement.top left=myCoolDnDElement.left}}
And the sizeChanged will automatically be called whenever the top or left attribute changes as well as on didInsertElement.
This question is not about components, but about controller/view pairs which are not longer part of ember.
Back then every controller had an associated view, which often lead to confusion when to place things on the controller and when on the view.
Back then the separation was between controllers and views, where controllers managed global and local state and views basically managed the rendering.
With the introduction of components this has changed. Components handle local state as well as the rendering for that. Basically every component is a view. But its not anymore magically coupled to a controller, but explicit called from the template. For global state services were introduced.
Today you should follow the DDAU (Data Down Actions Up) principle and pass all actions and attributes explicit to the components in the template.
Components call actions on the parent component and pass updatable attributes and callable actions to child components. Thats how you should handle it.
I've got a Controller which backs a view that's created via {{render "foo"}}. This Controller is a singleton because I'm not passing in a model in the {{render "foo"}} call, and {{render "foo"}} can be called from many different parts of the application. So in other words, the view for this controller (which gets embedded via {{render}}) will be removed and added to the DOM over and over again. Is there a way for the controller to know when the view is rendered on and removed from the screen?
You could try and implement the didInsertElement and willDestroyElement hooks on your Ember view. http://emberjs.com/api/classes/Ember.View.html#event_didInsertElement
Then inside that implementation, you can call a function on your controller.. for example:
didInsertElement: function() {
this.get('controller').callFunctionOnController();
}
I am new to Ember and I am not sure how to do things the Ember way so I turn to you.
My problem:
I have a sidebar, I created a View for it. I have two buttons on the sidebar for the moment. I added an action for each button on it. I am not sure if I should handle it on controller or on view. I want on clicking one of these button, a new view to be inserted that would open a pop up menu and also the button that called the action to remain in a selected state.
I am not very sure how to do this. I tried to target the view with the action but I can't have access to the target element or at least I don't know how to access it (tried this.$()).
What way do you suggest to follow?
User 'actions' are handled with methods on a Controller or a Route. You should put them in an actions hash:
App.MyController = Ember.ObjectController.extend({
actions: {
doSomething: function() {
// do it here
}
}
});
Ember manipulates the DOM and inserts views automatically based on resources and routes. If you don't want to use the router, you can manually control the view hierarchy, but I'd suggest getting more familiar with Ember routing before you try manual views.
If I were you, I'd create a Component that handled the button. You will have a reference to the DOM element in the didInsertElement callback: http://emberjs.com/api/classes/Ember.Component.html#event_didInsertElement
I've got a named outlet in my application template, which I'm using for modal (popup) views only. By default I want this to be an empty, unused outlet, as only about 5% of my routes will involve a modal display. For those particular modal routes, I'm inserting the modal template from the deeply nested child route, e.g.
App.NeeplyNestedModalChildRoute = Ember.Route.extend({
renderTemplate: function() {
this.render({
into: 'application',
outlet: 'modal'
});
}
});
The issue I'm having is that I want 'closing the modal box' to involve transitioning to a different, non-modal, less deeply nested route. I'm successfully transitioning to the correct route, but I can't figure out how to clear the modal outlet. How can I force the modal outlet to clear for all the non-modal routes?
I commented on this use case on issue #intuitive pixel was talking about. It seems there is already functionality for what you're trying to do, at least in part. You can use the deactivate hook when leaving a route. In that you can clear out the outlet, I would think.
Perhaps you could create an empty template that you render into the outlet. Then when the deactivate hook is called, simply render the empty template into the outlet. That should work for now until they close the issue.
I'm using a variation of the following code to clear my modal outlet:
clearOutlet: function (container, outlet) {
var parentView = this.router._lookupActiveView(container);
parentView.disconnectOutlet(outlet);
}
see the full code from #teddyzeeny over at the Ember Discussion Board, here: http://discuss.emberjs.com/t/modal-views-can-we-agree-on-a-best-practice/707/2
I have a flow with multiple pages and when I use 'transitionTo' to go back to a route/view that has already been displayed, it's 'didInsertElement' method is not called. (it was fired the first time the view was displayed, though)
Is there an event that my view can hook into that will be called every time it is displayed?
My routes look something like this
App.Router.map(function() {
this.resource("parent", { path: "/parent" }, function() {
this.resource("child", { path: "/child" });
});
});
So when I'm in the child view and call:
this.transitionTo('parent')
The parent view does not fire 'didInsertElement'.
I found that I can use setupController in the routes to let me know each time the route is rendered. I know it's not the best solution, but so far, it is the most reliable.
The parent is already rendered, as the child is rendered inside of the outlet in the parent. What are you trying to do when you transition back to the parent view? There might be a better way to do what you want
If your routes are nested your templates should be too. So I would expect your template to look something like this:
<script type="text/x-handlebars" data-template-name="parent">
<!-- Your parent View stuff -->
{{outlet}} <!-- This is where your child gets rendered
</script>
So your parent view stuff is already inserted and stays inserted. It shouldn't be going anywhere when you transition into / out of the parent.
What exactly are you trying to do that you need the didInsertElement after transitioning back to the parent. The didInsertElement hook is really for lower level dom manipulation, like setting up jQuery plugins. If your trying to do more application logic stuff it probably belongs somewhere else.