Kendo Grid: Setting Model fields on combox selection breaks the combobox up/down arrow scrolling - kendo-ui

this follows in from another post to do with setting a grid's selected fields when we have a data source field (and combo fields) with a json object (as opposed to a simple string).
So if we look at the change event handler for the following combox...
function createCombo(container, options, data) {
var input = $('<input name="' + options.field + '" />')
input.appendTo(container)
input.kendoComboBox({
autoBind: true,
filter: "contains",
placeholder: "select...",
suggest: true,
dataTextField: "display",
dataValueField: "rego",
dataSource: data,
change: function () {
var dataItem = this.dataItem();
var dataField = options.field.split('.');
var fieldName = dataField[0];
options.model.set(fieldName + '.rego', dataItem.rego);
options.model.set(fieldName + '.display', dataItem.display);
}
});
}
I am setting my 2 fields as follows...
options.model.set(fieldName + '.rego', dataItem.rego);
options.model.set(fieldName + '.display', dataItem.display);
(each item in the combo, and the grid data source has a json object with a 'rego' and 'display' field, see full example here.
This seemed to work exactly as I wanted, but I had just had someone point out to me that when you scroll the combobox with the up/down arrow keys, it just seems to toggle between 2 values in the list, as opposed to iterating through all the items. If I remove the 2 'options.model.set' statements, the combo then behaves.
I am really hoping there is a work around this, but everything I Have tried makes no different.
If there were any suggestion to finish this off, it would be greatly appreciated!
Thanks in advance for any help

Since you're modifying the model manually, you should to remove the name=... attribute from the input (otherwise the grid will also modify the model; you could also use name="car.rego" - it has to be the value field - and then not set the combobox value in the config) and also only call set for the last change you make on the model (otherwise, the grid's save event will get triggered twice, once with invalid data).
So you editor would look like this:
function createCombo(container, options, data) {
var dataField = options.field.split('.');
var fieldName = dataField[0];
var input = $('<input/>')
input.appendTo(container)
input.kendoComboBox({
autoBind: true,
filter: "contains",
placeholder: "select...",
suggest: true,
dataTextField: "display",
dataValueField: "rego",
dataSource: data,
value: options.model[fieldName].rego,
change: function (e) {
var dataItem = this.dataItem();
options.model[fieldName]['rego'] = dataItem.rego;
options.model.set(fieldName + '.display', dataItem.display);
}
});
}
Also, your data should be consistent (in one DS, you use "C1" as rego, in the other "CAR1").
(demo)

Related

Kendo Grid: Filter On Different Property Than Property Column Is Bound To

I want to be able to filter a column based on values from a different column.
I have bound a column to ID property and showing Name (using template). When the user filters/sorts, the values of the bound property (ID) are used. I want to use the values of a different property/column.
I have already figured out a way to handle sort (using compare function for kendo.ui.GridColumnSortable) but cannot find a way to handle filter.
PS: I tried using filterMemberPath and sortMemberPath but they only seem to work for server side filtering/sorting.
Talked to Kendo support and found a couple of ways to handle this issue:
if using Kendo version 2016.3 or above, the filter event can be used
filterable: true,
filter: function(e) {
if (e.filter.filters[0].field == "age") {
e.preventDefault();
this.dataSource.filter({ field: "name", operator: "startswith", value: "Jane" });
}
but if using kendo version 2016.1 or below, the following approach works which uses filterMenuInit and binds the click event on filter button
filterable: true,
filterMenuInit: function(e) {
var button = e.container.find(".k-primary");
var fieldName = e.field;
var grid = this;
button.on("click", function(e) {
e.preventDefault()
grid.dataSource.filter({ field: "name", operator: "startswith", value: "Jane" });
});
},

Kendo MultiSelect in grid no show default values

I use kendo MultiSelect as custom editor in kendo grid ,
MultiSelect work correctly when save changes but no show textvalue when press edit row button (MultiSelect is empty).
my custom editor function is:
function GRID_MULTISELECT_CUSTOM_EDITOR(container, options) {
var columnValue = String(options.model.POST_HISTORY).replace(/,/g,'","');
$('<input name="GRID_POST_LVL_MULTISELECT" id="GRID_POST_LVL_MULTISELECT" data-value-primitive="true" data-bind="value:' + options.field + '"/>')
.appendTo(container)
.kendoMultiSelect({
filter: "contains",
optionLabel: " ",
width: "100px",
dataTextField: "NAME_UNIT",
dataValueField: "CD_UNIT",
dataSource: prsListDataSource,
value: [columnValue],
change: function(e) {
selectedValue = e.sender.value();
apex.event.trigger($("#PRS_LIST_REG_POST_HISTORY_MULTISELECT"),"kapex-multiselect-change");
apex.event.trigger($("#PRS_LIST_REG_POST_HISTORY_MULTISELECT"),"kapex-multiselect-databound");
}
});
var ms = $("#GRID_MULTISELECT_CUSTOM_EDITOR").data("kendoMultiSelect");
console.log(ms.value());
}
console.log(ms.value()); show value is set but textvalue no show in MultiSelect widget.
When 1 value is stored in database MultiSelect work correctly and textvalue show in edit. but when sotre multi value, textvalue not show.
I store datavalue with this format in database column as varchar.
001,100,110,111,112
I recommend you to serialize the POST_HISTORY values as arrays to the client. This will spare the need to modify the model value on the fly in the custom editor function. You will also don't need to think how to transform the MultiSelect's array value back to a comma-separated string value after the user finishes editing a row.
http://dojo.telerik.com/IDUBI
Keep in mind that using the value configuration in the MultiSelect declaration will not work in this case, because the MVVM value binding is applied at a later stage and takes precedence.
On the other hand, if you absolutely need to serialize the POST_HISTORY values as comma-separated strings to the client, then use dataSource.schema.parse to transform these strings to arrays before the Grid is databound:
http://dojo.telerik.com/OQAGa
Finally, create the MultiSelect widget from a <select multiple>, not <input>.

Kendo Grid/Detail Grid - How to access a dropdown correctly on the detail grid?

I have a grid/detail grid set up. On the detail grid, I have a drop down list. The editor function for the drop down is:
function ActionTypeEditor(container, options) {
$('<input id="ddlActionType" data-text-field="name" data-value-field="id"> data-bind="value:' + options.field + '" ').appendTo(container).kendoDropDownList({
dataTextField: "name",
dataValueField: "id",
autoBind: false,
dataSource: GC.ViewModels.Config.AlertAction.actionTypeArray
}).appendTo(container).data("kendoDropDownList").text(options.model.ActionTypeId);
var dropdownlist = $("#ddlActionType").data("kendoDropDownList");
dropdownlist.value(options.model.ActionTypeId);
}
This works fine when I edit one row on a detail grid associated with the "parent" grid row. However, if I edit another detail row associated with another parent row BELOW the first, the next to last stamement
where I select the dropdown list always gets the first one on the page, rather than the one for the lower row. How do I get the correct drop
down list?
Why, you use
var dropdownlist = $(container.find("#ddlActionType")).data("kendoDropDownList");
instead.
You're welcome!

Kendo AutoComplete - force users to make valid selection

I've got a few Kendo AutoComplete fields linked to remote data (hundreds of possibilities, so DropDownList is not an option).
How can I force users to make a selection from the displayed list?
I'm also retrieving the additional data that's returned from the data source, e.g.
$("#station").kendoAutoComplete({
dataSource: stationData,
minLength: 2,
dataTextField: 'name',
select: function(e){
var dataItem = this.dataItem(e.item.index());
console.dir(dataItem);
}
});
I'm doing additional stuff with the data in dataItem and it needs to be a valid selection.
Thanks
SOLVED:
I think I was possibly over-complicating things. The answer is pretty simple and is posted below.
var valid;
$("#staton").kendoAutoComplete({
minLength: 2,
dataTextField: "name",
open: function(e) {
valid = false;
},
select: function(e){
valid = true;
},
close: function(e){
// if no valid selection - clear input
if (!valid) this.value('');
},
dataSource: datasource
});
This method allows users to type whatever they like into the AutoComplete if list not opens. There are two corrections to fix this:
initialize variable valid as false:
var valid = false;
Check if no valid selection in change event, but not in close:
...
change: function(e){ if (!valid) this.value(''); }
The answer the OP posted, in addition to the answer suggested by #Rock'n'muse are definitely good propositions, but both miss an important and desired functional aspect.
When utilizing the solution given by #Mat and implementing the change-vice-close suggestion from #Rock'n'muse, the typed-in value indeed clears from the widget if no selection is made from the filtered data source. This is great; however, if the user types in something valid and selects a value from the filtered list, then places the cursor at the end of the value and types something which now invalidates the value (doesn't return any valid selections from the data source), the typed-in value is not cleared from the widget.
What's happening is that the isValid value remains true if the previously typed-in (and valid) value should be altered. The solution to this is to set isValid to false as soon as the filtering event is triggered. When the user changes the typed-in value, the widget attempts to filter the data source in search of the typed-in value. Setting isValid to false as soon as the filter event is triggered ensures a "clean slate" for the change event as suggested by the solution from #Rock'n'muse.
Because we're setting isValid to false as soon as the filtering event is triggered, we do not need to do so in the open event (as data source filtering must occur before the user will ever see an option to select). Because of this, the open event binding was removed from #Mat's solution. This also means the initial assignment of false upon declaration of isValid is superfluous, but variable assignment upon declaration is always a good idea.
Below is the solution from #Mat along with the suggestions from #Rock'n'muse and with the filtering implementation applied:
var isValid = false;
$("#staton").kendoAutoComplete({
minLength: 2,
dataTextField: "name",
select: function () {
valid = true;
},
change: function (e) {
// if no valid selection - clear input
if (!valid) {
e.sender.value("");
}
},
filtering: function () {
valid = false;
},
dataSource: datasource
});
As an addendum, using the select event binding to set and evaluate a simple boolean value as #Mat proposes is much cleaner, simpler and faster than using a jQuery $.each(...) on the data source in order to make sure the typed-in value matches an actual item of the data source within the change event. This was my first thought at working a solution before I found the solution from #Mat (this page) and such is my reasoning for up-voting his solution and his question.
Perhaps, can you make your own validation by using the blur event :
$("#station").blur(function() {
var data = stationData,
nbData = data.length,
found = false;
for(var iData = 0; iData < nbData; iData++) {
if(this.value === data[iData].yourfieldname) // replace "yourfieldname" by the corresponding one if needed
found = true;
}
console.log(found);
});
You can check this fiddle.
You most likely need some custom logic which to intercept each key stroke after the min length of two symbols is surpassed, and prevent the option to enter a character which makes the user string not match any of the items from the autocomplete list.
For this purpose intercept the change event of the Kendo autocomplete and compare the current input value from the user against the items from the filtered list.
Hope this helps,
$("#autocomplete_id").val("");
$("#autocomplete").kendoAutoComplete({
dataSource: datasource,
minLength: 1,
dataTextField: "catname",
dataValueField:"id",
select: function(e) {
var dataItem = this.dataItem(e.item.index());
$("#autocomplete_id").val(dataItem.id);
},
dataBound: function(e){
$("#autocomplete_id").val("");
}
});
FYI, autocomplete_id is a hidden field to store the value of the autocomplete. - Sometimes, we would like to have the dataValueField other than dataTextField. So, it serves its purpose.
In this you can get the value of the autocomplete "id" from the element autocomplete_id - which is dataValueField from serverside.
In databound, its values is set to null, and on select, it is assigned the "id" value.
Although the accepted answer works, it is not the best solution.
The solution provided doesn't take into account if the user enters a value before even the kendo AutoComplete widget triggers the open event. In result, the value entered is not forced and therefore, the entry/selection is invalid.
My approach assumes that the application is running in MVC and the array is passed in ViewData. But this can be changed to suit your environment.
My approach:
var validSelect, isSelected;
$("#staton").kendoAutoComplete({
minLength: 2,
filter: "startswith",
dataTextField: "name",
filtering: function(e) {
validSelect = false;
dataArr = #Html.Raw(Json.Encode(ViewData["allStatons"]));
// for loop within the ViewData array to find for matching ID
for (var i = 0; i < dataArr .length; i++){
if (dataArr[i].ID.toString().match("^" + $("#staton").val())) {
validSelect = true;
break;
}
}
// if value entered was not found in array - clear input
if (!validSelect) $("#staton").val("");
},
select: function(e){
isSelected = true;
},
close: function(e){
// if selection is invalid or not selected from the list - clear input
if (!validSelect || !isSelected) $("#staton").val("");
},
dataSource: datasource
});
As you can see, this approach requires the array from the server side on load so that it can match during the filtering event of the widget.
I found this on Telerik's site by just using the change event. For me this works best. I added the "value === ''" check. This will catch when the user "clears" the selection.
Here's the link to the full article.
$("#countries").kendoAutoComplete({
dataSource: data,
filter: "startswith",
placeholder: "Select country...",
change: function() {
var value = this.value();
if (value === '') return;
var found = false;
var data = this.dataSource.view();
for(var idx = 0, length = data.length; idx < length; idx++) {
if (data[idx] === value) {
found = true;
break;
}
}
if (!found) {
this.value("");
alert("Custom values are not allowed");
}
}
});
In my AutoComplete Change event I check for a selected item and clear the cell if not selected.
function myAutoComplete_OnChange(e)
{
if (this.dataItem())
{
// Don't use filtered value as display, instead use this value.
e.sender.element[0].value = this.dataItem().WidgetNumber;
}
else
{
var grid = $("#grid").data("kendoGrid");
grid.dataItems()[grid.select().index()].WidgetKey = null;
grid.dataItems()[grid.select().index()].WidgetNumber = null;
}
}

Add a hyperlink in one of the columns in JQGrid and clicking on Hyperlink should open a new window

I have a jqgrid with certain columns and I need hyperlink in one of the columns, clicking on the hyperlink should open a new window, basically call a window.open().
Also when I call the window.open(), I need the hyperlink column value.
Please provide me with some sample code.Anyhelp would be highly appreciated.
Thanks
Oleg, I tried the below code and it is throwing error "object expected" in load().
{name:'FileName', FileName:'price', width:60, align:"center", formatter:returnMyLink}
function returnMyLink(cellValue, options, rowdata)
{
return "<a href='javascript:load();'>Open Window</a>";
}
function load()
{
var guid = 'CEF9C407-2500-4619-95E3-8E6227B65954';
window.open ('/irj/servlet/prt/portal/prtroot/com.medline.medpack.ExcelViewerPL.ExcelViewer?report=CustomerBenefit&reportId='+guid );
}
I did try the document.delegate to capture the a href event.
$(document).delegate('#CustomerSavingsView .jqgrow td a[href="#"]', 'click',function()
{
alert('test');
}
I was not able to capture this event either.
Sorry Im new to Jquery. Please correct me if Im wrong.
Thanks
This is how I solved it. In the grid complete event added the following code.
hl = "<a href='#Test' target='_blank' id='hlink"+cl+"'>Test</a>";
And then added a event handler for it.
$(document).delegate('#CustomerSavingsView .jqgrow td a[href*="#Test"]', 'click', function ()
{
var guid = 'CEF9C407-2500-4619-95E3-8E6227B65954';
window.open('/irj/servlet/prt/portal/prtroot/com.medline.medpack.ExcelViewerPL.ExcelViewer?report=CustomerBenefit&reportId='+guid );
}
This solved the purpose. Thanks again Oleg and Walter.
maybe this will be help:
in colModel,define a col: {name:'test',formatter:linkformatter}
and in javascript create a function named linkformatter which returns a link;
like:
function linkformatter( cellvalue, options, rowObject){
return '<a href='xxxxxx' />';
}
The predefined formatter 'showlink' can be used to create the link in the grid column. You can use target property of the formatoptions options to define target of the link.
First declare the Jquery JQGrid column definition as follows
colModel: [{ name: 'Notes/Memos', width: "5", sortable: true, classes: 'ellip', resizable: false, formatter: MethodFormatter }]
The formatter property takes the method name which is invoked with the three parameters which internally having the cells value and its id and the following method returns the hyperlink.
function MethodFormatter(cellValue, options, rowObject) {
var selectedRowId = options.rowId;
return '<a href="javascript:MethodJS(' + selectedRowId + ')" style="color: #3366ff" id="' + selectedRowId + '" >' + cellValue + '</a>';}
The following JS Function is invoked after clicking the hyperlink which opens up another page in a window.
function MethodJS(selectedRowId) {
document.location.href = "ViewContact.aspx?NoteID=" + selectedRowId;
}
My approach involves fewer lines of code and gives the solution asked for. In my grid, a column called Project Number is formatted as a hyper link. It opens a new page and passes the project number as a parameter.
colNames: ["Project #", ...],
colModel: [
{ name: 'Project Number', index: 'Project Number', width: 80, key: true, formatter: 'showlink', formatoptions: { baseLinkUrl: 'Details.aspx', target: '_new' } },
Note where I have key: true. Without this, the url returns the row number. The url returned is http://localhost:57631/Details.aspx?id=2103
I'm using jqGrid version 5.0.1
This is my pattern. As I said, it is much more code than Oleg's suggestion of using the showlink formatter, but it is more customizable.
// bind a live event handler to any elements matching the selector 'a.linkWindowOpener'
$('a.linkWindowOpener').live('click', linkWindowOpener);
// colModel settings
{ name: 'ItemDescription', index: 'ItemDescription', formatter: itemDescription_formatter, unformat: itemDescription_unformatter },
// custom formatter to create the hyperlink
function itemDescription_formatter(cellvalue, options, rowObject) {
var html = '';
var itemID = rowObject.itemID;
var itemDescription = cellvalue;
var a = $('<a>')
.attr('href', '/Forms/WorkOrder/ViewItem.aspx?ItemID=' + itemID)
.attr('data-itemDescription', itemDescription )
.html(itemDescription)
.addClass('linkWindowOpener');
html = a.getHtml();
return html;
}
// unformatter to return the raw value
function itemDescription_unformatter( cellvalue, options, cell) {
return $('a', cell).attr('data-itemDescription');
}
// event handler to call when clicking the hyperlink
function linkWindowOpener(event) {
event.preventDefault();
event.stopPropagation();
var o = $(event.currentTarget);
var url = o.attr('href');
window.open(url);
return false;
}
// jQuery extenision function I wrote to get the HTML of an element
// returns the HTML of an element. It works by wrapping the element
// inside a DIV and calling DIV.html(). It then returns the element back to
// it's original DOM location
jQuery.fn.getHtml = function () {
var elm = $(this[0]);
// create a div
var div = $('<div>');
// append it to the parent of the target element
elm.parent().append(div);
// append the element to the div
div.append(elm);
// get the html of the div
var html = div.html();
// move element back to its parent
div.parent().append(elm);
div.remove();
return html;
}

Resources