I\'m trying to use Bootstrap Popover with EmberJS, so that the content of the popover will be a ember/handlebars template (with binding etc). How can this be done? (Ember 1.0.0-
Here is a working example for an ember bootstrap popover (see http://jsfiddle.net/72fSd/):
App.Popover = Ember.View.extend({
parentSelector: '',
contentSelector: '',
didInsertElement: function () {
var self = this;
$(self.parentSelector).popover({
html: true,
content: function() {
var $content = $(self.contentSelector);
return $content.html();
}
});
}
Instantiate the view:
{{view App.Popover templateName="my-popover-content" parentSelector=".popoverButton" contentSelector="#popovercontent"}}
Here, parentSelector might e.g. select a button. Make sure you have a div container with id #popovercontent in your my-popover-content template in order for the contentSelector to work. Of course you need to load the template prior to initialization of the view.
Two-way binding should work with that solution.
ex if you want to popup a image do something like this in you view
imgTag: '<img src="smiley.gif" alt="Smiley face" height="42" width="42">',
didInsertElement: function () {
var self = this;
Ember.run.schedule('actions', this, function () {
self.$().popover({
title: 'Smile!!!',
html: true,
content: self.get('imgTag'),
placement: 'bottom',
trigger: 'hover'
});
});
},
willDestroyElement: function () {
this.$().popover('destroy');
}
I took Terry's answer a bit further and think I've come up with a simple, general solution to this problem.
I created a bootstrap-popover component like so:
App.BootstrapPopoverComponent = Ember.Component.extend({
tagName: 'div', //whatever default you want... div is default anyway here
classNames: '', //whatever default you want
placement: 'bottom', //whatever default you want
didInsertElement: function () {
var component = this,
contents = this.$('.popoverJs');
component.$().popover({
animation: false,
placement: component.get('placement'),
html: true,
content: contents
}).on('show.bs.popover', function () {
contents.removeClass('hide');
});
},
willDestroyElement: function () {
this.$().popover('destroy');
}
});
Here is the associated template:
<script type="text/x-handlebars" id="components/bootstrap-popover">
{{title}}
<div class="popoverJs hide">
{{yield}}
</div>
</script>
Note the use of the "hide" class to hide the yielded contents initially. This class is simply "display: none". Without this, things won't work quite how you'd hope.
Once you have that, you can do simply do something like this whenever you want a popover:
{{#bootstrap-popover title="My Fancy Popover" tagName="button"}}
<ul>
<li>my</li>
<li>awesome</li>
<li>popover</li>
<li>contents</li>
<li>example</li>
</ul>
{{/bootstrap-popover}}
The contents should be able to be whatever you want -- any arbitrary HTML, rendering a component or partial, etc.. Naturally, you can specify other tagNames, classNames, title, placement, etc. as you see fit.
I hope this solution helps.
I took Robert's answer above a bit further. I created a Component and also just use the jQuery element for the content instead of calling .html(). (This alleviates the problem of having duplicated IDs in the page.)
App.CustomPopoverComponent = Ember.Component.extend({
tagName: 'button',
classNames: 'btn btn-default',
type: 'button',
popoverContentSelector: '',
didInsertElement: function () {
var component = this,
contents = $(component.get('popoverContentSelector'));
component.$().popover({
placement: 'bottom',
html: true,
content: contents
}).on('show.bs.popover', function () {
contents.removeClass('hide');
});
},
willDestroyElement: function () {
this.$().popover('destroy');
}
});
I used Bootstrap's 'hide' class to hide the contents initially. Then I removed the 'hide' class the first time to the popover is shown. From then on things work as expected.
This is how to use the component in your handlebars template:
{{#custom-popover popoverContentSelector='.popoverContents'}}
Popover Button
{{/custom-popover}}
I ran into this problem as well and had the same problem Robert mentioned above where the acceptable solution simply doesn't scale well to more complicated scenarios.
I ran into a very elegant fix, but I'm not sure how future-friendly it is. I'm taking advantage of the function renderToBuffer - see below:
//make your popover view to be created later
App.PopoverView = Ember.View.extend({
templateName : 'name-of-your-template-with-content'
});
//then you make your link that will trigger the popover
App.PopoverLinkView = Ember.View.extend({
tagName : 'a',
didInsertElement : function(){
var that = this;
this.$().popover({
'html' : true,
'content' : function(el){
var detailView = App.PopoverView.create();
var html = detailView.renderToBuffer().buffer;
return html;
}
});
}
});
The advantage here is that you can pass in a model and make things dynamic. Haven't tested it thoroughly, but wanted to get this out there to potentially help others.