Bootstrap popover with knockout.js - ajax

I've got an application receiving some data through AJAX-call. After that, received data binds to DOM-elements using knockout.js library. I'd like to use boostrap's unobtrusive markup for creating popovers like this:
<table class="table table-condensed" data-bind="foreach: items">
<tr>
<td><b data-bind="text: $data.id"></b></td>
<td data-bind="text: $data.title"></td>
<td>Info</td>
</tr>
</table>
According to the latest bootstrap documentation, implicit call of something like $('.popover').popover() isn't required, however, it's not working.
I suppose, that boostrap.js perform some DOM-analysis on document.ready and perform all needed work for popover to work. And the question: is there some way to tell bootstrap.js to perform similar job for data after receiving AJAX response? Or how this kind of requirements can be achieved?

You can create custom dataBinding to make that element popover. Check this jsfiddle demo
ko.bindingHandlers.bootstrapPopover = {
init: function(element, valueAccessor, allBindingsAccessor, viewModel) {
var options = ko.utils.unwrapObservable(valueAccessor());
var defaultOptions = {};
options = $.extend(true, {}, defaultOptions, options);
$(element).popover(options);
}
};
var viewModel = {
items: ko.observableArray([{
"id": 1,
"title": "title-1",
"info": "info-1"},
{
"id": 2,
"title": "title-2",
"info": "info-2"},
{
"id": 3,
"title": "title-3",
"info": "info-3"}])
}
ko.applyBindings(viewModel);​
And html
<div class="container">
<div class="hero-unit">
<table class="table table-condensed" data-bind="foreach: items">
<tr>
<td><b data-bind="text: $data.id"></b></td>
<td data-bind="text: $data.title"></td>
<td>Info</td>
</tr>
</table>
</div>
</div>​

"According to the latest bootstrap documentation, implicit call of something like $('.popover').popover() isn't required, however, it's not working."
I can't find anywhere in the docs that states that. In fact, they state quite the opposite. Namely, Twitter Bootstrap does not automatically initialize popovers or tooltips on a page. From the docs:
For performance reasons, the Tooltip and Popover data-apis are opt in.
If you would like to use them just specify a selector option.
In order to "opt in", as they say, you would attach the Popover object to an element which contains all the popovers which might appear on the page. Here's one way of doing it:
$('body').popover({selector: '[rel="popover"]'});
I believe the performance considerations originally in mind came from the fact that prior to 2.1, the Popover plugin was by default triggered by mouseenter and mouseleave events, which are certainly not something you want to be constantly processing for an entire page.
Since 2.1, the default is now click, which shouldn't pose any performances issues. Nevertheless, if you can determine an element farther down the DOM than body to which to attach the Popover object, that is always preferred. However, depending on where you are displaying the AJAX content, body might be your best bet.

Here is a complete working example with array of objects, I changed the ko.applyBindings() to ko.applyBindingsToDescendants to include the binding context $root and $parent when we would like to associate a button for example to a function in a root viewModel.
$(element).click(function() {
$(this).popover('toggle');
var thePopover = document.getElementById(attribute.id+"-popover");
childBindingContext = bindingContext.createChildContext(viewModel);
ko.applyBindingsToDescendants(childBindingContext, thePopover);
});
Take a look # http://jsfiddle.net/mounir/Mv3nP/6/

Related

Knockout Viewmodel binding and Datatable Sorting

I'm using Knockout for data binding and using dataTable+YADCF for Sorting and filtering. My scenario is little complex, on clicking the Category nodes (left side) each time need to make an AJAX call and refresh the table data (right side) through Knockout. This Knockout binding functionality works fine without any issue.
HTML Code
<table class="pdm-data-table pdmList" id="ListCatAttrVal" data-link="row">
<thead>
<tr>
<th>Display Name</th>
<th>Display Source</th>
</tr>
</thead>
<tbody id="listAttribute" data-bind="foreach: attributevalue">
<tr>
<td data-bind="text: dispName"></td>
<td data-bind="text: dispSrc"></td>
</tr>
</table>
Knockout Model Code
if (!ko.dataFor(document.getElementById("listAttribute"))) {
var attributeModel = function () {
this.attributevalue = ko.observableArray();
};
attributeBinding = new attributeModel();
ko.applyBindings(attributeBinding, document.getElementById("listAttribute"));
}
Issue is after applying DataTable for the Table
$("#ListCatAttrVal").dataTable().fnClearTable();
for (var x in response.attributes) {
attributeBinding.attributevalue.push(response.attributes[x]);
}
$("#ListCatAttrVal").dataTable();
After this, Datatable Sorting is not working.
I'm trying to remove the existing generated dataTable and re-initiate it every-time when I click on the category node. But it is not working as expected.
I had a similar issue while working with knockout and datatables - my bindings inside the datatable don't seem to work initially. As a workaround I ended up initialising the datatable in the following way:
var table = $("#ListCatAttrVal").dataTable();
table.fnPageChange(0, true);
After calling fnPageChange (or any other function of datatable library, I believe) bindings seem to be working.

Can one avoid multiple DOM elements with same id, when using Backbone/Marionette view instances?

When I create multiple view instances of the Marionette view which is linked with a template html with ids, these would get duplicated for multiple instances of these views.
While it works correctly, I feel that there ought to be more architecturally correct way of doing this.
The example code is like below.
Template:
<script id="myTemplate" type="text/template">
<div id="myDiv">
<input type="text" id="myText"/>
<input type="button" id="myBtn" value="Click me!"/>
</div>
</script>
View:
MyView = Backbone.Marionette.ItemView.extend({
template: '#myTemplate',
events: {
'click #myBtn' : 'myFunc' //Correctly identifies its own 'myBtn'
},
myFunc : function() {
alert($('myText').val()); //Again, picks own 'myText'
}
});
var v1= new MyView();
v1.render();
var v2= new MyView();
v2.render(); //Duplicate IDs now present in DOM
I need some unique identification of these DOM elements and hence the ids.
Even when tying the model to this view, we need some way to identify these DOM elements.
What is the correct way of doing this without duplicating the ids.
Just pass the id to the view when you create it:
Template:
<script id="myTemplate" type="text/template">
<input type="text" class="js-myText"/>
<input type="button" class="js-myBtn" value="Click me!"/>
</script>
View def:
MyView = Backbone.Marionette.ItemView.extend({
template: '#myTemplate',
events: {
'click #myBtn' : 'myFunc' //Correctly identifies its own 'myBtn'
},
myFunc : function() {
alert($('myText').val()); //Again, picks own 'myText'
}
});
Instanciation:
var v1= new MyView({ id: "view" + number});
v1.render();
Then you can provide dynamic id values for your views (e.g. by using a model id).
That said, when using Marionette you shouldn't need to call render: you should instead show a view within a region. Take a look at the free sample to my Marionette book to get you up to speed.
If you must go for unique IDs to make sure no one accidentally duplicates a class name inside a view, you can use:
Underscore's uniqueId method to generate a unique ID for each DOM element inside the view, like: <input type="text" id= <%= _.uniqueId("myText_") %> /> This will just make sure that IDs are not duplicated. But they're not very helpful if you need to identify the elements by these IDs.
Marionette's TemplateHelpers which allow you to use helper functions from inside the templates:
//Define this inside your view:
templateHelpers: function() {
var that = this;
return {
getIdSuffix : function() { return that.idSuffix; }
/*Where idSuffix is passed to the view during instantiation
and assigned to this.idSuffix */
};
}
//In the template:
<input type="text" id= <%= "myText_" + getIdSuffix() %> />
You now know before runtime what DOM IDs you will have, provided care is taken not to give the same idSuffix to more than one view instance.
Simply put, don't use an id if it's not unique. Use a class or some other way of identifying the element.
You can use any jQuery selector to locate the element you want, ranging from the insane and brittle:
this.$('div > input:first'); // don't actually do this!
to the slower but semantically better:
this.$('[data-element-type="some-text-box-descriptive-name"]');
Although in reality, using a class is best, because that's what a class is for - for identifying a type of element. I can see that a maintainer might not know not to change your class in the template, so a data-attribute might be acceptable, or maybe even (in this case):
this.$('input[type=text]');

How do I render a partial site to another div?

I want a view with a table on the site and when you click on a table Item, i want to update a partial view with the current object I just clicked on. How do I manage to do this?
<table>
#foreach (var a in annotations)
{
<tr>
<td>
<label onclick="change stuff to new Annotation">#a.Title</label>
</td>
</tr>
}
</table>
<div id="stuff">#Html.RenderPartial("Go", "new annotation")"</div>
You're mixing client-side and server-side code. By the time the click event occurs, you're on the client and server-side code has run and finished.
You could render the partial view in a hidden div and just unhide it on the click event. Something similar to this, perhaps:
<table>
#foreach (var a in annotations)
{
<tr>
<td>
<label>#a.Title</label>
<div style="display:none;">#Html.RenderPartial("Go", a)</div>
</td>
</tr>
}
</table>
<script type="text/javascript">
$(function () {
$('table tr td label').click(function () {
$(this).closest('td').find('div').show();
});
});
</script>
There may be a more elegant way to find the correct div in the jQuery selectors, you can perhaps add classes and ids to DOM elements as needed to make it cleaner. If you have a lot of these table rows then I'd also recommend using the jQuery .on() function for binding the click event as it would perform better. Something like this:
<script type="text/javascript">
$(function () {
$('body').on('click', 'table tr td label', function () {
$(this).closest('td').find('div').show();
});
});
</script>
This would bind a single click event to the DOM instead of many, with the added bonus that dynamically added rows would still handle the event after the binding takes place.
your getting your client side and server side code mixed up here.
#Html.RenderPartial("Go", a)
is a server side method that returns some HTML.
<label onclick=
Is client side code. So by the time your client gets the data it looks like this:
<label onclick="The text returned from html.render partial">
I think you need to read up on MVC a bit more to achieve what you want as your fundamentally going down the wrong route here.

Is it possible to load a partial view by selecting a Radio Button in mvc3

Can i use radio button's to select two different partial view, Without using Jquery?
yes and no. a partial can only be loaded (after initial page load) via ajax, so a partial page refresh isn't possible without using ajax. however, you could submit the selected radio button (via javascript) to the controller action and then determine inside the controller which radio button had been selected. It would then just be a case of selecting the appropriate view.
As I said, you can't go down the partial route without ajax in the mix, so the answer is no. also, you'd still have to use javascript in order to use the radio button in the submit, in which case, an ajax solution might be worth thinking about.
[edit] with deference to Splash-X, here's a quick work up of the hidden div scenario:
#*use either #Html.RenderPartial() or #Html.RenderAction() as required*#
<div id="developerDiv" style="display: none">
This is the developer stuff, in reality,
this would be populated as such #*#Html.RenderPartial("DeveloperPartial")*#
</div>
<div id="testerDiv" style="display: none">
And here we have the testers, again,
this would be populated as such #*#Html.RenderPartial("TestersPartial")*#
</div>
<div>
Developer :#Html.RadioButton("team", "developer", new { onclick = "showResult(this)"})
Tester :#Html.RadioButton("team", "tester", new { onclick = "showResult(this)"})
</div>
<div id="partialContainer"></div>
<script type="text/javascript">
function showResult(radio) {
var selected = radio.value;
if (selected == "developer")
document.getElementById("partialContainer").innerHTML
= document.getElementById("developerDiv").innerHTML;
else if (selected == "tester")
document.getElementById("partialContainer").innerHTML
= document.getElementById("testerDiv").innerHTML;
}
</script>
enjoy..

MVC3 WebGrid: Can htmlAttributes be used on rows/columns?

I'm using a WebGrid to create a paged/sortable list in MVC3. I've created an AJAX enabled Delete button which makes the delete call via AJAX, after which I'd like it to remove the row from the table.
The way I'd like to achieve this is by having an id or data-id attribute on the <tr> in the table so that I can easily manipulate it using jQuery. However I can't work out how to add attributes to rows when using a WebGrid.
I know that attributes can easily set at the grid level like so:
#grid.GetHtml(htmlAttributes: new { id = "gridMapping", style = "width:100%;" },
However I don't know how to achieve the same at the row/column level.
#grid.GetHtml(
columns: grid.Columns(
grid.Column(format: (item) => Html.CheckBox("SelectedInvoice",new { value=item.transactionId})),
//// rest of your columns here
)
)
so one way would be putting an HTML helper method in place that can handle your htmlAttributes.
Other way - using combination of format: and Html.Raw
And the last, but may be the easiest: javascript (jQuery)
so you can try something like :
$('#grid tr').each(function(){
$(this).attr('yourhtmlattribute','value');
});
and in similar way for TDs.
Here is how I achieved this
In your webgrid set up add this to one of the colums.
grid.Column("UserName", "User Name", format: #<text><span data-id='#item.Id'>#item.UserName</span></text>, canSort: true),
No when the HTML grid is rendered you will get this.
<tr jQuery45435434="3"> <span data-userid="1">Fred Smith</span></tr>
<tr jQuery45435434="4"> <span data-userid="2">Sally Smith</span></tr>
<tr jQuery45435434="5"> <span data-userid="3">Joe Smith</span></tr>

Resources