Emberjs outlet inside a bootstrap popover - ember.js

I'm using bootstrap popover in my app and I need to render an outlet inside it.
I have this nested route :
this.resource('pages', function(){
this.resource('page', { path: ':id' }, function(){
this.resource('edit', function(){
this.resource('images', function(){
this.resource('image', { path: ':image_id'}, function(){
this.route('edit');
})
});
});
});
});
When the user is here => /pages/1/edit/ when he click on an image it route to /images but render the {{outlet}} inside the popover like this :
<div class="popover-content hide">
{{outlet}}
</div>
This is my popover initialisation :
$img.popover({
html: true,
content: function() {
return $('.popover-content').html(); //need to have the outlet here
}
});
So far, it render correctly my outlet, but inside the images template, I have some button that modify the DOM and it doesn't update the html. Unless if I close and open the popover again I can see the modification.
Is it possible to render the outlet directly inside the code ? or is it possible to have my popover being updated ?
Thanks for the help.

See these links for an alternative approach to putting Ember stuff in Bootstrap popovers:
Bootstrap Popovers with ember.js template
https://cowbell-labs.com/2013-10-20-using-twitter-bootstrap-js-widgets-with-ember.html

Ember and Handlebars don't like this because it's basically copying the html content of a div and plopping it into another. But that html alone isn't everything that's needed. Ember is magic and there's lots of stuff happening in the background.
Your hidden div is real ember stuff, so let's try not to mess with it by calling .html() on it. My idea is to literally move the DOM itself instead.
first, modify your popover function call to always create this placeholder div:
content: '<div id="placeholder"></div>',
next, detach the content div from the dom in the didInsertElement of the view:
// get the popover content div and remove it from the dom, to be added back later
var content = Ember.$('.popover-content').detach();
// find the element that opens your popover...
var btn = Ember.$('#btn-popup-trigger').get(0);
// ... and whenever the popover is opened by this element being clicked, find the placeholder div and insert the content element
// (this could be improved. we really just want to know when the popover is opened, not when the button is clicked.)
btn.addEventListener("click", function() {
content.appendTo("#placeholder");
});
since the content div is immediately detached when didInsertElement is called, you can remove the "hide" css class from the content div.
edit: i tried this on my own project and it broke two-way binding. the controller updated my handlebars elements, but any two-way bound {{input}} helpers did not update the controller/model. i ended up using a single-item dropdown menu, and used this to prevent the menu from closing too quickly:
Twitter Bootstrap - Avoid dropdown menu close on click inside

Related

EmberJS - Rendering separate route as a modal into a specific outlet

In my EmberJS applications I have two separates routes as follows,
Route A - "main/books/add"
Route B - "main/authors/add"
I have an "Add Authors" button In Route A template and when a user presses that button I want to load and render Route B in a modal to add new authors.
I know its possible to achieve somewhat smiler to this by using the route's render method to render the Route B template and respective controller.
But in that case, the "model" hook of Route B in "main/authors/add.js" file does not get invoked.
It would be really nice if someone can suggest me a method to render a separate route into a modal.
EDIT - Although this is entirely valid (the premise of rendering into using named outlets, views are now deprecated in Ember 1.1. The same can be achieved by using a Component
Yup, you can do this:
What you'd want to do is create a modal in a template and assign a named outlet into it (or create a view that is a modal with an outlet):
in modal.hbs:
<div class='modal'>
{{outlet "modalContent"}}
</div>
Then I would override your base button like so:
App.BasicButton = Em.View.extend({
context: null,
template: Em.Handlebars.compile('<button>Click Me!</button>');
click: function(evt) {
this.get('controller').send('reroute', this.get('context'));
}
});
And in your template set up your button to trigger your modal:
in trigger.hbs
<!-- content and buttons for doing stuff -->
{{View App.BasicButton context='modalContent'}}
Finally, you want to create a method in your route which handles rendering specific content into your outlet:
App.TriggerRoute = Em.Route.extend({
actions: {
reroute: function(route) {
this.render(route, {into: 'modal', outlet: route});
}
}
});
So in essence, you're rendering the template (called "modalContent") into a specific outlet (called "modalContent"), housed within the template/view (called "modal")
You would also want to write some logic to trigger the modal to open on element insertion. To do that, I would use the didInsertElement action in the modal view:
App.ModalView = Em.View.extend({
didInsertElement: function() {
this.$.css("display", "block");
//whatever other properties you need to set to get the modal to pop up
}
});

Ember.js How to bind isSelected class to the clicked view?

I have some views which will expand and show details when clicked.
For now, all the views can be clicked and expand, but the question is
How to expand only the latest clicked view?
For example, when I clicked view #1, it expand. So when I clicked view #2, the view #1 will collapse and view #2 expand etc.
I know we can bind a isSelected classname to the clicked view, but how do we tell the view to check "If any other view is selected" ?
Do we use CollectionView ? But how?
FYI this is the working JSBin.
First of all, I would change view to component. Although views have their valid use-cases, you are usually better off with a component.
Also, if you think about it, it makes sense that someone outside of the component would need to know which component was clicked last. That outside actor could be the controller, which could have a property called lastComponentClicked (which initially starts out as null)
App.IndexController = Ember.ArrayController.extend({
lastClickedComponent: null
});
Then, you can pass that property into each component and the property becomes bound between the controller and all the components as in:
<script type="text/x-handlebars" data-template-name="index">
{{#each content in model}}
{{ x-box content=content lastClickedComponent=lastClickedComponent}}
{{/each}}
</script>
So far, so good. Now, for the component itself:
App.XBoxComponent = Em.Component.extend({
classNames: ['box'],
isSelected: function(){
return this.get('lastClickedComponent') === this._uuid;
}.property('lastClickedComponent'),
click: function(){
this.set('lastClickedComponent', this._uuid);
}
});
Every time it is clicked, you can set a lastClickedComponent property, which is bound between ALL the components and the controller and thus will get reset every time. You can just set it to a value unique to the component, for example this._uuid.
isSelected computed property can then just check if lastClickedComponent property is that of THIS component, in which case the content you need to show will be expanded.
<script type="text/x-handlebars" id="components/x-box">
{{content}}
{{# if isSelected }}
<div>LAST SELECTED</div>
{{/if}}
</script>
Working solution here

How to add a carousel to an ember site

I would like to add a carousel, preferably caroufredsel http://caroufredsel.dev7studios.com/, to a few templates/routes on an ember site. I was wondering what the best way to do that would be?
All that is needed to fire the items that you want to slide is the following jQuery function:
$(document).ready(function() {
$("#foo1").carouFredSel();
});
Where do you think the best place in the app to put this is?
The best place it to create a custom View for the template that will have the carousel. Then use the didInsertElement hook to initialize the widget once the markup for the carousel once it is in the document. You can also use the willDestroyElement hook to tear down the carousel before the markup is removed from the document.
So, say that you have a /carousel route, and then you have this as your 'carousel' template.
<div id="foo1">
<!-- Other carousel markup goes here-->
</div>
Then you'd create a View like this.
App.CarouselView = Ember.View.extend({
didInsertElement : function(){
$("#foo1").carousel();
},
willDestroyElement : function(){
$("#foo1").trigger("destroy");
}
});

ember.js v1.0.0-rc.1 -- Using a modal outlet, how do I route out of the modal on close?

I'm attempting to render all my modals through application routing, but I'm having trouble figuring out the proper way to return to a previous state after I dismiss the modal.
Here's the basic setup:
I have an outlet in my application template which I'm using to display modal dialogs.
It looks something like:
{{ outlet modal }}
In my route mappings, I've defined hooks for the individual modal. For instance, my help dialog pops up with:
App.HelpRoute = Ember.Route.extend({
renderTemplate: function(controller, model) {
this.render({ outlet: 'modal' });
}
});
Right now, I can display my modal through the uri:
foo.com/#/help
I have a hook to dismiss the modal using jQuery:
$('#modalHelpWindow').modal('hide');
But this doesn't really help, because I'm just hiding the element. I need to update URI on dismissal. If I hit the same route again, the modal is already hidden, and doesn't display.
What methods should I be using to dismiss the modal, and route the application back to its previous state? Should I just be using history.back()?
Note, I am aware of this SO question, but the solution is not the preferred 'ember' way of doing things, as programmatically created views will not have their controllers associated properly What's the right way to enter and exit modal states with Ember router v2?
Looks like I can hook up the hidden handler to do a history.back() call in my view's didInsertElement method, a la:
didInsertElement: function() {
var $me = this.$();
$me.modal({backdrop:"static"});
$me.on('hidden', function() {
history.back();
});
},

Emberjs + Handlebars + Object onchange and events

I'm finding that jQuery observers aren't bound to elements that are not shown in handlebars logic.
Let's say I have the following;
{{#if person}}
Welcome back, <b>{{person.firstName}} {{person.lastName}}</b>!
{{else}}
Please <a class="login">log in</a>.
{{/if}}
<script>
$('.login').click(function() {
alert("Hi there.");
});
</script>
If I run in console, person = null (or whatever's needed to convince that person is empty) - the login observer doesn't work. I'm already using embers didInsertElement() to load a few other things, but is there a "onChange" event I can hook into so I can rebind event observers?
The big question is why you want that? Ember has excellent built in support for click handlers without going via jQuery. The reason your <script> is not working is likely to be down to the deferred way ember inserts views into the DOM. When you do Ember.View.append() the element is inserted in the DOM later.
That said, here's a fiddle that does what I think you want attaching the jQuery click handler in didInsertElement().
http://jsfiddle.net/algesten/5LPPz/1/
didInsertElement: function () {
// appending click handler directly with jQuery
$('.login').click(function() {
alert("Hi there.");
});
}
However the ember way would be to just use the click implicit handler function:
http://jsfiddle.net/algesten/5LPPz/2/
click: function () {
alert("Hi there.");
}
N.B. the latter handler attaches to the surrounding handlebar div and not the a, but clicks bubble.
The problem your facing is that javascript can only bind to elements that exist in the dom. Once you add a new element it wants you to re-bind those events. Luckily, jQuery is your friend on this one.
<script>
$('body').on('click', '.login', function() {
alert("Hi there.");
});
</script>
Ideally, your selector is the closest parent to .login that doesn't get added by javascript. The above is safe bet if you're not sure