data-bind not working in kendo.template - asp.net-mvc

I'm having troubles trying to implement a custom remove button in my Kendo Grid. This is the code I have:
View:
<div id="gridAdditionalLines">
Javascript:
var dataSourceGrid = new kendo.data.DataSource({
data: MyData, --> It's a type of Kendo.Observable
schema: {
model: {
Id: "Id",
fields: {
Id: { editable: false, nullable: true },
Column1: { editable: false, nullable: true },
Description: { validation: { required: true} },
Value: { validation: { required: true} }
}
}
}
});
$("#MyGrid").kendoGrid({
dataSource: dataSourceGrid,
editable: true,
navigatable: true,
toolbar: ["create"],
columns: [
{ field: "Description" },
{ field: "Value" },
{ command: [{name: "destroy", template: kendo.template($("#DeleteTemplate").html())}], width: 60}
]
});
This is the template I'm using for the remove button for each row:
<script type="text/x-kendo-template" id="DeleteTemplate">
<button class="btn btn-default" data-bind="click: Applicability.deleteLine">
<span class="glyphicon glyphicon-remove"></span>
</button>
With the code above, the Kendro Grid display the data properly. However, when trying to remove any row, at the moment to click in the Remove button, nothing happens.
do I missing something?

It has been a little while since I used Kendo, but if I remember correctly, since the grid itself is not MVVM bound, none of its child elements (including the rendered template) will be checked for MVVM data-bind attributes either.
If your grid was initialized with MVVM using data-role="grid" then I think the templates would be bound too.
I forget exactly how to do it, but I believe when the grid triggers its dataBound event, you can manually call kendo.bind(...) on the grid's child elements to get them to MVVM bind.

Your function for button click is missing here. Once you add this script a button is added to the grid, but what happens when you click on button is not specified
<script type="text/x-kendo-template" id="DeleteTemplate">
<button class="btn btn-default" data-bind="click: Applicability.deleteLine">
<span class="glyphicon glyphicon-remove"></span>
</button>
Then you have to add an onclick function for the button:
$('.btn.btn-default').on('click', function() {
var grid = $("#grid").data("kendoGrid");
var dataItem = grid.dataItem($(this).closest("tr"));
if(confirm('Are you sure you want to delete : ' + dataItem.name)) {
grid.dataSource.remove(dataItem);
grid.dataSource.sync();
grid.refresh();
}
});
Check jsbin here

Related

Kendoui ComboBox prefetch & preselect item with server filtering

I need to see an example of a kendoui Combobox (either MVC or client side code) preselecting an item (not only value) from datasource.The datasource has server filtering enabled.The issue i am facing is that when i bind my mvc combobox to a model property (e.g. UserID), only the value (which is the user ID) is bound and not the Name which is the textfiled.When clicking on the combobox arrow, the selected item is not poping up,meaning that there is no selected item and only the widget element (e.g. input) value is set.Most of the examples i have seen shows how to set the selected value or text, but does not address the issue of the absence of a selected item.Here is my code:
#Html.Kendo().ComboBoxFor(model => model.UserID).DataValueField("ID").DataTextField("Name").Filter(FilterType.Contains).MinLength(3).DataSource(source =>
{
source.Custom().Type("aspnetmvc-ajax").Transport(transport => transport.Read(read =>
{
read.Action("GetAllUsers", "User");
})).ServerFiltering(true).ServerPaging(true).PageSize(50);
}).AutoBind(false)
The autoBind is false so that the combobox does not hit the data service with an empty filter which would return a useless 50 records.Instead of this, the desired behaviour is that the combobox datasource should send the filter (e.g: UserID=50) by default and should return the record and add the item to the combobox.I have found a workround for this issue but do not know if this the easiest way to do it:
combobox.dataSource.filter({ field: 'UserID', operator: 'eq', value: 50 });
If I call the above code, the item is preselected, and my combobox would have only one item which is what i wanted, but the next time i try to change the selected item by typing another user name, the filter would fail because the earlier filter is still attached to the datasource.I solved this by calling:
combobox.dataSource.filter().filters.shift()
Any help for finding a shortcut will be appreciated.
There's documentation for this here. The example below is taken from the page and based on DropDownList which is applicable to ComboBox too.
<div id="example">
<div class="demo-section k-header">
<h4>View Order Details</h4>
<p>
<label for="categories">Categories:</label><input id="categories" style="width: 270px" />
</p>
<p>
<label for="products">Products:</label><input id="products" disabled="disabled" style="width: 270px" />
</p>
<p>
<label for="orders">Orders:</label><input id="orders" disabled="disabled" style="width: 270px" />
</p>
<button class="k-button" id="get">View Order</button>
</div>
<style scoped>
.demo-section {
width: 400px;
}
.demo-section p {
margin-top: 1em;
}
.demo-section label {
display: inline-block;
width: 100px;
padding-right: 5px;
text-align: right;
}
.demo-section .k-button {
margin: 1em 0 0 105px;
}
.k-readonly
{
color: gray;
}
</style>
<script>
$(document).ready(function() {
var categories = $("#categories").kendoDropDownList({
optionLabel: "Select category...",
dataTextField: "CategoryName",
dataValueField: "CategoryID",
dataSource: {
type: "odata",
serverFiltering: true,
transport: {
read: "https://demos.telerik.com/kendo-ui/service/Northwind.svc/Categories"
}
},
dataBound: function() {
this.search("Grains/Cereals");
this.select(this.selectedIndex);
}
}).data("kendoDropDownList");
var products = $("#products").kendoDropDownList({
autoBind: false,
cascadeFrom: "categories",
optionLabel: "Select product...",
dataTextField: "ProductName",
dataValueField: "ProductID",
dataSource: {
type: "odata",
serverFiltering: true,
transport: {
read: "https://demos.telerik.com/kendo-ui/service/Northwind.svc/Products"
}
},
dataBound: function() {
this.search("Gnocchi di nonna Alice");
this.select(this.selectedIndex);
}
}).data("kendoDropDownList");
var orders = $("#orders").kendoDropDownList({
autoBind: false,
cascadeFrom: "products",
optionLabel: "Select order...",
dataTextField: "Order.ShipCity",
dataValueField: "OrderID",
dataSource: {
type: "odata",
serverFiltering: true,
transport: {
read: "https://demos.telerik.com/kendo-ui/service/Northwind.svc/Order_Details?$expand=Order"
}
},
dataBound: function() {
this.search("Albuquerque");
}
}).data("kendoDropDownList");
});
</script>
</div>

JQuery UI highlight effect color parameter ignored in Knockout foreach

Im trying to apply the JQuery UI highlight effect to an element when an item that is bound to a knockout observablearray is updated.
The highlight effect is applied but the highlight color used is always the elements current background color. even if I specify the highlight color using the { color: 'XXXXXXX' } option.
any ideas what might be happening?
Thanks,
Steve.
Code below: The element is the span.tag
<div class="row">
<div class="span12">
<div class="tagsinput favs span12" style="height: 100%;" data-bind="foreach: favs, visible: favs().length > 0">
<span class="tag" data-bind="css: $root.selectedFav() == userPrefID() ? 'selected-fav' : '', attr: { id: 'fav_' + userPrefID() }">
<span data-bind="text: name, click: $root.loadFav.bind($data)"></span>
<a class="tagsinput-fav-link"><i class="icon-trash" data-bind="click: $root.delFav.bind($data)"></i></a>
<a class="tagsinput-fav-link-two" data-bind="visible: $root.selectedFav() == userPrefID()"><i class="icon-save" data-bind=" click: $root.saveFav.bind($data)""></i></a>
</span>
</div>
</div>
</div>
// This is the code that does a save via ajax then highlights the element when done.
$.getJSON('#Url.Action("SaveFav","User")', { id: item.userPrefID(), fav: window.JSON.stringify(fav) }, function (result) {
var savedFav = ko.utils.arrayFirst(self.favs(), function (aFav) {
return aFav.userPrefID() == result.userPrefID; // <-- is this the desired fav?
});
// Fav found?
if (savedFav) {
// Update the fav!
savedFav.value(result.value);
}
}).done(function () {
var elementID = "#fav_" + item.userPrefID();
highlightElement(elementID);
});
// Function to highlight the element
function highlightElement(element) {
$(element).effect("highlight", {}, 1500);
}
I would do this the 'knockout' way... use a custom bindingHandler. You shouldn't be directly manipulating DOM in your viewModel, but only touching properties of your viewModel.
Taking this approach, you simply set a boolean value to true when your save is complete... this triggers the highlight effect (the jquery/dom manipulation neatly hidden away from your viewmodel) and when highlight effect completes, the handler sets the boolean back to false. Nice and tidy.
HTML:
<div id="#fav" data-bind="highlight: done">This is a test div</div>
<br />
<button data-bind="click: save">Simulate Save</button>
Javascript:
ko.bindingHandlers.highlight = {
update: function(element, valueAccessor) {
var obs = valueAccessor();
var val = ko.unwrap(obs);
if (val) {
$(element).effect("highlight", {}, 1500, function() {
obs(false);
});
}
}
};
var vm = function() {
var self = this;
self.done = ko.observable(false);
self.save = function() {
self.done(true);
};
}
ko.applyBindings(new vm());
Fiddle:
http://jsfiddle.net/brettwgreen/pd14q4f5/

How to use flexigrid as a partial view in ASP.NET MVC application?

I am able to display Flexigrid in a normal view called from my main menu. I am using this sample http://mvc4beginner.com/Sample-Code/Insert-Update-Delete/Asp-.Net-MVC-Ajax-Insert-Update-Delete-Using-Flexigrid.html to make it work and it works fine for me.
However, my idea is to use a bit more complex interface - have a regular view with the search controls and on pressing search button show the grid with data for the items I searched.
I tried couple of things so far and can not make it to work. Here is the latest Index view I tried:
#model CardNumbers.Objects.Client
#{
ViewBag.Title = "Clients";
}
<h2>Clients</h2>
<br />
#using (Ajax.BeginForm("Search", "Client",
new AjaxOptions
{
HttpMethod = "POST",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "ClientsResults"
}))
{
<fieldset>
<legend>Search</legend>
<label for="clientNo">Client No: </label>
<input type="number" name="searchClientNo" class="numericOnly" /><br />
<label for="clientName">Client Name: </label>
<input type = "text" size =25 data-autocomplete="#Url.Action("QuickSearch", "Client")" name ="searchClientName" />
<div>
<input type="submit" value="Find / Refresh" />
#*<input type="button" value="Find / Refresh" id="ClientsSearch" data-url="#Url.Action("Client", "Client")" />
#*<input type="submit" value="Find / Refresh" />*#
#* #Ajax.ActionLink("Find / Refresh", "Client", new AjaxOptions {UpdateTargetId = "ClientResults",
InsertionMode = InsertionMode.Replace, HttpMethod = "POST"}) *#
#*}*#
</div>
</fieldset>
<div style="padding-left:150px; padding-top:50px; padding-bottom:50px;" id="ClientsResults">
#*#{Html.RenderPartial("_Client", Model); }*#
#*<table id="flexClients" style="display:none"/>*#
</div>
}
#*<br />*#
You can see all the commented attempts here also. So, the Search method in the Clients controller now has this code:
public ActionResult Search(int? searchClientNo = null, string searchClientName = null)
{
// Assume we want to select everything
var clients = Db.Clients; // Should set type of clients to IQueryable<Clients>
if ((searchClientNo ?? 0) != 0) //Number was supplied
clients = clients.Where(c => (c.Number == searchClientNo));
// If clientNo was supplied, clients is now filtered by that. If not, it still has the full list. The following will further filter it.
if (!String.IsNullOrWhiteSpace(searchClientName)) // Part of the name was supplied
clients = clients.Where(c => (c.Name.Contains(searchClientName)));
return PartialView("_ClientsSearch", clients);
//return PartialView("_Client", clients);
}
The commented view is the partial view which has a flexigrid and it's not working. The _ClientsSearch view is the "normal" index view created by using the template and this works.
Do you see what exactly I am missing? The flexigrid method is simply not firing at all when I attempt to use it as a partial view from that main view.
I haven't figured out the more complex scenario I had originally in mind, but I was able to make it work using the regular view. The helpful idea first came from this FAQ:
http://code.google.com/p/flexigrid/wiki/FAQ and also looking a bit closer to that sample I used.
So, now my Client view is this:
#model CardNumbers.Objects.Client
#{
ViewBag.Title = "Client";
}
<form id="frmClientsSearch">
<label for="clientNo">Client No: </label>
<input type="number" name="searchClientNo" class="numericOnly" /><br />
<label for="clientName">Client Name: </label>
<input type = "text" size =25 value ="Please enter the search value"
name ="searchClientName" />
<input type="button" id="btnClientsSearch" value ="Find / Refresh" />
</form>
<div style="padding-left: 150px; padding-top: 50px; padding-bottom: 50px;" id="ClientsResults">
<table id="flexClients" style="display: none">
</table>
</div>
<div style="display: none">
<form id="sform">
<input type="hidden" id="fntype" name="fntype">
<input type="hidden" id="Id" name="Id">
<label for="Number">Client No: </label>
<input type="number" id="Number" name="Number" class="numericOnly" />
<label for="Name">Client Name: </label>
<input type="text" size="25" id="Name" name="Name" /><br />
<label for="Contact11">Contact 1: </label>
<input type="text" size="25" id="Contact1" name="Contact1" /><br />
<div class="float-right">
<button type="Submit" id="btnSave">Submit</button>
<button type=reset id="btnCancel">Cancel</button>
</div>
</form>
</div>
And the main work is done in the .js file (note the AddFormData method):
/// <reference path = "jquery-1.5.1-vsdoc.js"/>
/// <reference path = "jquery-ui-1.8.11.js"/>
$(document).ready(function() {
$(":input[data-autocomplete]").each(function() {
$(this).autocomplete({ source: $(this).attr("data-autocomplete") });
});
});
$(function () {
$('input[name="delete"]').click(function () {
return confirm('Are you sure?');
});
});
$(".numericOnly").keypress(function (e) {
if (String.fromCharCode(e.keyCode).match(/[^0-9]/g)) return false;
});
$("#flexClients").flexigrid({
url: '/Client/Client/',
dataType: 'json',
colModel: [
{ display: 'Client Id', name: 'Id', width: 100, sortable: true, align: 'center', hide: true},
{ display: 'Client #', name: 'Number', width: 100, sortable: true, align: 'center' },
{ display: 'Name', name: 'Name', width: 350, sortable: true, align: 'center' },
{ display: 'Contact 1', name: 'Contact1', width: 350, sortable: true, align: 'center' },
],
buttons: [
{ name: 'Add', bclass: 'add', onpress: test },
{ name: 'Edit', bclass: 'edit', onpress: test },
{ name: 'Delete', bclass: 'delete', onpress: test },
{ separator: true }
],
searchitems: [
{ display: 'Client Name', name: 'Name' }
],
sortname: "Name",
sortorder: "asc",
usepager: true,
title: 'Clients',
useRp: true,
rp: 15,
showTableToggleBtn: true,
width: 1000,
onSubmit: addFormData,
height: 300
});
//This function adds parameters to the post of flexigrid. You can add a verification as well by return to false if you don't want flexigrid to submit
function addFormData() {
//passing a form object to serializeArray will get the valid data from all the objects, but, if the you pass a non-form object, you have to specify the input elements that the data will come from
var dt = $('#sform').serializeArray();
dt = dt.concat($('#frmClientsSearch').serializeArray());
$("#flexClients").flexOptions({ params: dt });
return true;
}
$('#sform').submit(function () {
$('#flexClients').flexOptions({ newp: 1 }).flexReload();
alert("Hello World");
return false;
});
function test(com, grid) {
if (com === 'Delete') {
var clientName = $('.trSelected td:eq(2)').text();
if (clientName) //Variable is defined and not empty
{
if (confirm("Are you sure you want to delete " + $.trim(clientName) + "?"))
return false;
$('#fntype').val('Delete');
$('#Id').val($('.trSelected td:eq(0)').text());
$('#Number').val('');
$('#Name').val('');
$('#Contact1').val('');
$('.trSelected', grid).each(function () {
var id = $(this).attr('id');
id = id.substring(id.lastIndexOf('row') + 3);
addFormData(); $('#flexClients').flexOptions({ url: '/Client/Client/' }).flexReload();
});
clearForm();
}
} else if (com === 'Add') {
$("#sform").dialog({
autoOpen: false,
show: "blind",
width: 1000,
height: 500
});
$("#sform").dialog("open");
$('#fntype').val('Add');
$('#Number').val('');
$('#Name').val('');
$('#Contact1').val('');
} else if (com === 'Edit') {
$('.trSelected', grid).each(function () {
$("#sform").dialog({
autoOpen: false,
show: "blind",
width: 1000,
height: 500
});
$("#sform").dialog("open");
$('#fntype').val('Edit');
$('#Id').val($('.trSelected td:eq(0)').text());
$('#Number').val($('.trSelected td:eq(1)').text());
$('#Name').val($('.trSelected td:eq(2)').text());
$('#Contact1').val($('.trSelected td:eq(3)').text());
});
}
}
function clearForm() {
$("#sform input").val("");
};
$(function () {
$('#btnSave').click(function () {
addFormData();
$('#flexClients').flexOptions({ url: '/Client/Client/' }).flexReload();
clearForm();
$('#sform').dialog('close');
return false;
});
});
$(function () {
$('#btnCancel').click(function () {
// clearForm();
$('#sform').dialog('close');
return false;
});
});
$(function () {
$('#btnClientsSearch').click(function () {
addFormData();
$('#flexClients').flexOptions({ url: '/Client/Client/' }).flexReload();
//$.ajax({
// url: $(this).data('url'),
// type: 'GET',
// cache: false,
// success: function (result) {
// $('#ClientsResults').html(result);
// }
//});
return;//false;
});
});
And my Client method in the controller is the same as it used to be with minor change.
Now, my next challenge is to generalize the above and also somehow instead of calling the form sForm I showed use a more complex form with validations as I if it is from the Create/Edit view.

knockout.js, jquery-ui, button click and param

I'm using jQuery UI to create a "button" to a given html element. I'm using Knockout.js to generate the html element (foreach).
However, I can't find the way how to pass a parameter to the click event for knockout.js generated items. In the following example, the somewhat static sampleButton works, but not the itemButton items.
http://jsfiddle.net/patware/QVeVH/
function ViewModel() {
var self = this;
self.ping = 'pong';
self.items = ko.observableArray([
{ display: 'Cars', id: 1 },
{ display: 'Fruits', id: 2 },
{ display: 'Humans', id: 3 },
{ display: 'Software', id: 4 },
{ display: 'Movies', id: 5 },
]);
}
ko.applyBindings(new ViewModel());
$("#sampleButton").button().data('someData',101);
$("#sampleButton").click(function(e){
alert('clicked sample: [' + $(this).data('someData') + ']');
});
$(".itemButton").button().data('someData',$(this).id);
$(".itemButton").click(function(){
alert('clicked item: [' + $(this).attr('foo') + ']');
});
ping-<span data-bind="text: ping"></span>
<div id="sample">
<div id="sampleButton">
<h3>Sample Button</h3>
Click here too
</div>
</div>
<div data-bind="foreach: items">
<div class="itemButton" data-bind="foo: id">
<h3 data-bind="text:display"></h3>
</div>
</div>​
Consider using ko.dataFor instead of applying data with jquery.
Working sample based on your example http://jsfiddle.net/QVeVH/6/
You can set everything up using a custom binding.
http://jsfiddle.net/jearles/QVeVH/7/

Presenting a second form with Jquery UI Dialog and KnockoutJS

I have a primary form defined and nicely laid out, it does what it needs to do...
#{ Html.BeginForm(); }
#Html.ValidationSummary(false)
#Html.AntiForgeryToken()
#Html.EditorFor(model => model)
<h2>Properties</h2>
<hr />
#* I want to put some stuff here... *#
<br class="space" />
<div class="clearfix">>
<button type="submit" data-bind="click: save">
Save
</button>
</div>
#{ Html.EndForm(); }
Now, then. This model (or ViewModel, rather) has an IList<PropertyViewModel> attached to it.
A PropertyViewModel has its own set of validations. They are pretty simple for now, but there is a chance that later there will be more complicated uses for this setup.
I am using KnockoutJS for my viewModel consistency. Though I suppose it is fairly irrelevent. I want to display a second form in a jQuery UI Dialog and return the result, essentially..
<script type="text/javascript">
var viewModel = {
name: ko.observable(),
description: ko.observable(),
properties: ko.observableArray(),
save: function () {
alert(ko.toJSON(viewModel));
},
includeProperty: function () {
$("#dialog").dialog({
width: 500,
closeText: '',
resizable: true,
buttons: {
'Submit': function () {
$(this).dialog('close');
callback( #* I want the new data to get sent back *# );
},
'Cancel': function () {
$(this).dialog('close');
return false;
}
}
});
}
};
function callback(value) {
alert(ko.toJSON(value)); // (I will push the new property to the viewmodel here)
}
ko.applyBindings(viewModel);
</script>
However, I am not really sure how to actually put the EditorTemplate into the dialog, moreover I am not sure how to get the data back out of it.
I don't completely understand your question, but from what I understood what you are trying to do is pass data to the dialog and retrieve data from the dialog. If that is the case then this may be useful:
http://api.jquery.com/jQuery.data/
Here is detailed example on how to use:
Passing data to a jQuery UI Dialog

Resources