kendo ui Combobox binded to a Grid losts it's value and text after Grid's Cancel button pressed - asp.net-mvc

I have two views in database. Bonuses and employees. One(employee)-to-many(bonuses).
I have kendo ui grid (kendo web), which displays ajax results from controller called Bonuses
And an autocompliting element - Employee Combobox binded with Employee filed of a grid.
Grid's datasource:
// bind json result from /Bonuses/GetPagedJsonBonuses
var bonusesDataSource = new kendo.data.DataSource({
transport: {
read: "#Url.Action("GetPagedJsonBonuses", "Bonuses")",
update: {
url: "#Url.Action("Edit", "Bonuses")",
type: "PUT"
},
create: {
url: "#Url.Action("Create", "Bonuses")",
type: "POST"
},
parameterMap: function(options, operation) {
if (operation === "update" || operation === "create") {
// updates the BonusDTO.EmployeeId with selected value
if (newValueEmployeeId !== undefined)
options.EmployeeId = newValueEmployeeId;
}
return options;
}
},
schema: {
data: "Data", // PagedResponse.Data
total: "TotalCount", // PagedResponse.TotalCount
model: {
id: "BonusId", // Data
fields: {
EmployeeId: { type: "number" },
EmployeeLastName: {
type: "string",
editable: true,
//validation: { required: {message: "Employee's last name is required"}}
},
Amount: {
type: "number",
editable: true,
nullable: false,
validation: {
required: { message: "Amount is required to be set" }
}
}
} // fields
} // model
}// schema
});
Grid element looks like this:
// creates bonuses grid control
$("#bonusesGrid").kendoGrid({
dataSource: bonusesDataSource,
toolbar: ["create"],
editable: "inline",
columns: [
"BonusId",
"EmployeeId",
{
field: "EmployeeLastName",
editor: employeeAutocompletingEditor,
template: "#=EmployeeLastName#"
},
"Amount",
{
command: ["edit"],
title: " "
}
],
save: function(e) {
if (newValueEmployeeId !== undefined && newValueEmployeeLastName !== undefined) {
e.model.EmployeeId = newValueEmployeeId; // it's a hack to bind model and autocomplete control
e.model.EmployeeLastName = newValueEmployeeLastName;
}
},
edit: function(e) {
setCurrentValueEmployeeIdAndLastName(e.model.EmployeeId, e.model.EmployeeLastName);
},
cancel: function(e) {
setCurrentValueEmployeeIdAndLastName(e.model.EmployeeId, e.model.EmployeeLastName);
}
});
Autocompleting combobox has it's own datasource using ajax:
// datasource for autocomlete combobox to lookup employees names from
var employeesDataSource = new kendo.data.DataSource({
transport: {
read: "#Url.Action("GetJsonEmployeesByLastName", "Bonuses")",
},
parameterMap: function(options, operation) {
if (operation === "update" || operation === "create") {
setNewValueEmployeeIdAndLastName(options.Id, options.LastName);
}
return options;
},
});
Autocompliting combobox look's like this:
function employeeAutocompletingEditor(container, options) {
$('<input required data-text-field="LastName" data-value-field="EmployeeId" data-bind="value:' + options.field + '"/>')
.appendTo(container)
.kendoComboBox({
// sets the local variables to update values of current row.
change: function() {
setNewValueEmployeeIdAndLastName(this.value(), this.text());
},
dataBinding: function (e) {
console.log("dataBinding: ", e, this.dataItem());
},
dataBound: function (e) {
console.log("dataBound: ", e, this.dataItem());
},
dataSource: employeesDataSource
});
}
I use Editor binding to pass values(EmployeeId) and Text (EmployeeLastName) from grid to combobox.
I also use a hack like temporal variables (newValueEmployeeId, currentValueEmployeeId) to
send selected Employee in combobox and pass it to grid for correct save. A found it's a most common practice to pass a value back to grid.
My problems is:
If I press Edit button on my grid first time The combobox displays current employee's name from grid row:
If I press Cancel button and again press Edit button, combobox doesn't display current value of grid (employee's name)
IF I type some name, change some other values, and Udate(save) value, next time combobox again displays employees name, but only once before Cancel was pressed.
I'm very new in Kendo UI and this problem drives me crazy...
I think that combobox losts it's binding or doesn't update smth. I tried to set values while onBound and onBinding events, but this doesn't help. Please help me with an advice and example.
PS all evenets and functions is my try to debug and find solution.

only one fix helped me:
var employeeList = new List<Employee>()
employeeList.add(new Emplpyee()) // add fake employee record.
return Json(employeeList)
I don't know why, but grid control start make cyclying empty ajax requests if I return empty list of employees or null. This doesn't work:
return Json(new List<Employee>());
return Json(null);
I think it's a problem in kendo combobox itself ,because it's not ready to receive and handle empty list or null as json result.
Also I heared something, that JQuery doesn't support empty or null results anymore...maybe that's the reason

Related

Angularjs ui-grid events not removed per row after data update

I have a grid that contains an edit button per each row. Everything works as expected the first time the page is loaded, but if I try to add a new entity and repopulate the grid, the edit button will fire the edit event of the first already existing entity.
This is my code:
var columnDefs = [
{
name: '', field: 'id',
cellTooltip: 'Edit',
enableSorting: false,
enableColumnMenu: false,
enableCellEdit: false,
maxWidth: 30,
headerCellTemplate: '<div class="ui-grid-cell-contents ui-grid-icon"></div>',
cellEditableCondition: false,
cellTemplate: '<div class="ui-grid-cell-contents ui-grid-icon"><div title="Edit" class="ui-edit ui-grid-icon-pencil" ng-click="grid.appScope.editNews({{row.entity.id}})"></div></div>'
},
{ name: 'shortDescription', field: 'shortDescription' }];
$scope.gridOptions = {
enableGridMenu: false,
enableSorting: false,
enableRowHashing: false,
columnDefs: columnDefs,
data: "data",
onRegisterApi: function (gridApi) {
$scope.gridApi = gridApi;
}
};
var getData = function () {
showLoading('.grid');
newsRepository.resourceFunctions.query(displayOptions,
function (data) {
if (data.length == 0) {
setEmptyGrid($scope, 'There are no news!');
$scope.data = [{
"message": 'There are no news!'
}];
}
else {
setGrid(data);
}
hideLoading('.grid');
}, //success
function (error) {
$scope.gridApi.selection.clearSelectedRows();
hideLoading("#view-container");
toaster.pop('error', "Server error",
"There was an error while processing your request. Please contact support.", 10000);
}); //error
};
function setGrid(data) {
$scope.gridOptions.columnDefs = columnDefs;
$scope.data = data;
}
This is how the grid is re-populated (the data is comming from the save AJAX request):
$scope.gridApi.selection.clearSelectedRows();
setGrid(data.items);
The edit event is inside the cellTemplate: grid.appScope.editNews({{row.entity.id}}). Also, after the grid data is updated, this template is populated with the correct entity id, but the editNews is called with the id of the older entity. Any ideas how I can fix this?
I had a similar problem to this today, although more to do with cellTemplate buttons not shifting to their correct row when a new row was added, or an existing row removed.
In your particular case just remove the {{ and }} in your call to editNews. E.g. it should be:
ng-click="grid.appScope.editNews(row.entity.id)". This worked nicely for me.

How to dynamically change kendo UI treeview datasource

I have a kendo ui treeview and a dropdownbox on my asp.net mvc page. The dropdown box can have two values. Depending upon which one has been chosen, I want to change the datasource url, which is a method in a controller. The data type and structure of data from both the url will be the same. Essentially I want to change url: '#Url.Content("~/Document/GetMyDocument")' dynamically. The following is my treeview code:
<script>
$("#treeview").kendoTreeView({
checkboxes: {
checkChildren: true,
},
dataSource: {
transport: {
read: {
url: '#Url.Content("~/Document/GetMyDocument")',
type: "post",
dataType: "json"
}
},
schema: {
model: {
text: "Name",
spriteCssClass: "fa fa-folder",
children: "Files"
}
}
},
dataTextField: ["Name", "FileName"],
dataValueField: ["id"],
check: onCheck
});
You can specify the URL of the treeview dataSource as function
transport: {
read: {
url: function (e) { }
}
}
You can then listen for the change event of the DropDownList and call
$('#treeview').data('kendoTreeView').dataSource.read();
Inside the url function you can get the value of the DropDownList and resolve the url dynamically:
var ddl = $('#myDDl').data('kendoDropDownList');
var value = ddl.value();
return value == "someValue" ? "/foo/bar": "/bar/baz";

Update Kendo Grid based On Search Form Post

I've been doing alot of searching but haven't found a clear answer for this. I have a set up textboxes that and a submit button and a Kendo UI grid. I want to post the data to the grid's datasource so that it will return the results based on the criteria. I am not using the MVC wrappers.
EDIT:
I've gotten closer but I can't seem to get the datasource to send the post data when I click submit. I've debugged and in my $("#fmSearch").submit it is hitting the jquery plugin and I've confirmed that it is converting the form data to JSON properly, but it seems as though it it not sending the updated information to the server so that the Action can read it.
Javascript
var dsGalleryItem = new kendo.data.DataSource({
transport: {
read: {
url: '#Url.Content("~/Intranet/GalleryItem/SearchGalleryItems")',
type: "POST",
data: $("#fmSearch").serializeFormToJSON(),
cache: false
}
},
schema: {
model: {
id: "galleryItemID",
fields: {
galleryItemID: {
nullable: true
},
imageName: {},
collectionName: {},
categoryName: {},
lastUpdatedOn: { type: "date" }
}
}
}
});
var gvResults = $("#gvResults").kendoGrid({
autoBind:false,
columns: [{
field: "imageName",
title: "Item Name",
template: "<a href='#Url.Content("~/Intranet/GalleryItem/Details/")#=galleryItemID#'> #=imageName#</a>"
}, {
field: "collectionName",
title: "Collection"
}, {
field: "categoryName",
title: "Category"
}, {
field: "lastUpdatedOn",
title: "Last Updated",
format: "{0:M/d/yyyy}"
}
],
selectable: "row",
change: onRowSelect,
dataSource: dsGalleryItem
});
$("#fmSearch").submit(
function (event) {
event.preventDefault();
dsGalleryItem.read({ data: $("#fmSearch").serializeFormToJSON() });
});
MVC Action
[HttpPost]
public JsonResult SearchGalleryItems(string keyword, int? category, int? collection, DateTime? startDate, DateTime? endDate)
{
var galleryItemList = (from g in db.GalleryItems
//where g.imageName.Contains(keyword)
select new GalleryItemViewModel
{
galleryItemID = g.galleryItemID,
imageName = g.imageName,
collectionName = g.collection.collectionName,
categoryName = g.category.categoryName,
lastUpdatedOn = g.lastUpdatedOn
});
var galleryItemCount = Json(galleryItemList.ToList());
return Json(galleryItemList.ToList()); ;
}
The action is not setup to retrieve different data right now I just need to know how to connect the form to the grid.
Found the problem. I had this:
dsGalleryItem.read({ data: $("#fmSearch").serializeFormToJSON() });
It needed to be this:
dsGalleryItem.read($("#fmSearch").serializeFormToJSON());

Kendo UI - Specify parameter name on dataSource read

With Kendo UI, I am using an autocomplete box to try and retrieve data from my server. It is hitting an ASP.NET MVC controller with the following signature.
public ActionResult aspect(string term){
// ...
}
This means that the request needs to have the correct parameter in the url. Now the issue I am running into is that I cannot discover a way to specify this in the dataSource mechanics. I have read the documentation on parameterMap dozens of times and it makes absolutely no sense to me in any way.
This is complicated further by the fact that the page in question actually has 10-15 autocomplete text boxes at any one time, each dynamically created with dynamic identity.
The code I am using so far is as follows;
$(".autocomplete").kendoAutoComplete({
dataTextField: "Name",
dataSource: {
type: "json",
transport: {
read: {
url: "/search/aspect"
}
}
}
});
So is there anything I can do to tell it how to name the parameter it passes?
To make it more clear what I am trying to do, if I were doing this in jQuery, I would use ...
$.ajax({ url: '/search/aspects', data: { term: (insert the data here) } });
But because of the way all of this works, there is no set "selector" to get the autocomplete input, so I cannot retrieve its value from the input form element.
First, enable server-side filtering by setting this option:
dataSource: {
serverFiltering: true,
Then the value is passed as one of the parameters into the transport.parameterMap function.
If you were to log the object passed in to the parameterMap function like this:
$(".autocomplete").kendoAutoComplete({
dataTextField: "Name",
dataSource: {
serverFiltering: true,
type: "json",
transport: {
read: {
url: "/search/aspect"
},
parameterMap: function (data, action) {
console.log(data);
}
}
}
});
then you would get an object that looks like this:
{
"filter":{
"logic":"and",
"filters":[
{
"value":"something",
"operator":"contains",
"field":"Name",
"ignoreCase":true
}
]
}
}
So you can use this to get the value entered into the AutoComplete box by doing:
$(".autocomplete").kendoAutoComplete({
dataTextField: "Name",
dataSource: {
serverFiltering: true,
type: "json",
transport: {
read: {
url: "/search/aspect"
},
parameterMap: function (data, action) {
if(action === "read") {
return {
term: data.filter.filters[0].value
};
} else {
return data;
}
}
}
}
});
I think that there is a misunderstanding about the relation between DataSource and AutoComplete. AutoComplete has the input and uses a DataSource for retrieving the data: the input does not belong to the AutoComplete and as consequence you cannot get the input that is using a DataSource from a method that is from the DataSource (as transport.read.data or transport.parameterMap).
You need to unique identify which element has the input and the value that it contains.
What I do propose is getting the value using document.activeElement.value. Since you are typing it, the element that has the focus should be the element that you are using.
The code would be:
$(".autocomplete").kendoAutoComplete({
dataTextField: "Name",
dataSource: {
type: "json",
transport: {
read: {
url: "/search/aspect",
},
parameterMap : function (data, type) {
if (type === "read") {
return { term : document.activeElement.value }
}
}
}
}
})
Alternatively, you can enable serverFiltering and then Kendo UI links the input field with the filtering condition. The code would be:
$(".autocomplete").kendoAutoComplete({
dataTextField: "Name",
dataSource : {
serverFiltering: true,
type : "json",
transport : {
read : {
url : "/search/aspect"
},
parameterMap: function (data, type) {
if (type === "read") {
return { term : data.filter.filters[0].value }
}
}
}
}
});
I'm a little confused as to what you're wanting to do. If you are just trying to pass the string term to the controller you can specify the data:
$(".autocomplete").kendoAutoComplete({
dataTextField: "Name",
dataSource: {
type: "json",
transport: {
read: {
url: "/search/aspect",
data: { term: "value" }
}
}
}
})
Thanks for the clarification and help OnaBai. Here is the code that I got working after hours of frustration!
$("#contractsSearchField").kendoComboBox({
dataTextField: "name",
dataValueField: "id",
autoBind: false,
placeholder:'select...',
filter: "contains",// a filter must be present for the datasources serverFiltering argument to work properly.
minLength: 3,
dataSource: new kendo.data.DataSource({
serverFiltering: true,//We must turn on serverFiltering and sorting, otherwise, the combobox only works once and will not change it's values.
serverSorting: true,
group: { field: "searchtype" },
transport: {
read: {
url: "contract.cfc?method=getContractForDropdown",
// We are not passing the data here like we do in the autosuggest. The combobox is a different type of an animal.
dataType: "json",
contentType: "application/json; charset=utf-8", // Note: when posting json via the request body to a coldfusion page, we must use this content type or we will get a 'IllegalArgumentException' on the ColdFusion processing page.
type: "GET"
},//read
// Pass the search term that was typed in by the user. This works because when the user types into the input box, it becomes that active element in the form.
parameterMap : function (data, type) {
if (type === "read") {
return { searchTerm : document.activeElement.value }
//return { searchTerm: data.filter.filters[0].value }
}
}//parameterMap
}//transport
})//dataSource
}); //...kendoComboBox...

Select2 dropdown but allow new values by user?

I want to have a dropdown with a set of values but also allow the user to "select" a new value not listed there.
I see that select2 supports this if you are using it in tags mode, but is there a way to do it without using tags?
The excellent answer provided by #fmpwizard works for Select2 3.5.2 and below, but it will not work in 4.0.0.
Since very early on (but perhaps not as early as this question), Select2 has supported "tagging": where users can add in their own value if you allow them to. This can be enabled through the tags option, and you can play around with an example in the documentation.
$("select").select2({
tags: true
});
By default, this will create an option that has the same text as the search term that they have entered. You can modify the object that is used if you are looking to mark it in a special way, or create the object remotely once it is selected.
$("select").select2({
tags: true,
createTag: function (params) {
return {
id: params.term,
text: params.term,
newOption: true
}
}
});
In addition to serving as an easy to spot flag on the object passed in through the select2:select event, the extra property also allows you to render the option slightly differently in the result. So if you wanted to visually signal the fact that it is a new option by putting "(new)" next to it, you could do something like this.
$("select").select2({
tags: true,
createTag: function (params) {
return {
id: params.term,
text: params.term,
newOption: true
}
},
templateResult: function (data) {
var $result = $("<span></span>");
$result.text(data.text);
if (data.newOption) {
$result.append(" <em>(new)</em>");
}
return $result;
}
});
For version 4+ check this answer below by Kevin Brown
In Select2 3.5.2 and below, you can use something like:
$(selector).select2({
minimumInputLength:1,
"ajax": {
data:function (term, page) {
return { term:term, page:page };
},
dataType:"json",
quietMillis:100,
results: function (data, page) {
return {results: data.results};
},
"url": url
},
id: function(object) {
return object.text;
},
//Allow manually entered text in drop down.
createSearchChoice:function(term, data) {
if ( $(data).filter( function() {
return this.text.localeCompare(term)===0;
}).length===0) {
return {id:term, text:term};
}
},
});
(taken from an answer on the select2 mailing list, but cannot find the link now)
Just for the sake of keep the code alive, I'm posting #rrauenza Fiddle's code from his comment.
HTML
<input type='hidden' id='tags' style='width:300px'/>
jQuery
$("#tags").select2({
createSearchChoice:function(term, data) {
if ($(data).filter(function() {
return this.text.localeCompare(term)===0;
}).length===0)
{return {id:term, text:term};}
},
multiple: false,
data: [{id: 0, text: 'story'},{id: 1, text: 'bug'},{id: 2, text: 'task'}]
});
Since many of these answers don't work in 4.0+, if you are using ajax, you could have the server add the new value as an option. So it would work like this:
User searches for value (which makes ajax request to server)
If value found great, return the option. If not just have the server append that option like this: [{"text":" my NEW option)","id":"0"}]
When the form is submitted just check to see if that option is in the db and if not create it before saving.
There is a better solution I think now
simply set tagging to true on the select options ?
tags: true
from https://select2.org/tagging
Improvent on #fmpwizard answer:
//Allow manually entered text in drop down.
createSearchChoice:function(term, data) {
if ( $(data).filter( function() {
return term.localeCompare(this.text)===0; //even if the this.text is undefined it works
}).length===0) {
return {id:term, text:term};
}
},
//solution to this error: Uncaught TypeError: Cannot read property 'localeCompare' of undefined
Thanks for the help guys, I used the code below within Codeigniter I I am using version: 3.5.2 of select2.
var results = [];
var location_url = <?php echo json_encode(site_url('job/location')); ?>;
$('.location_select').select2({
ajax: {
url: location_url,
dataType: 'json',
quietMillis: 100,
data: function (term) {
return {
term: term
};
},
results: function (data) {
results = [];
$.each(data, function(index, item){
results.push({
id: item.location_id,
text: item.location_name
});
});
return {
results: results
};
}
},
//Allow manually entered text in drop down.
createSearchChoice:function(term, results) {
if ($(results).filter( function() {
return term.localeCompare(this.text)===0;
}).length===0) {
return {id:term, text:term + ' [New]'};
}
},
});
I just stumbled upon this from Kevin Brown.
https://stackoverflow.com/a/30019966/112680
All you have to do for v4.0.6 is use tags: true parameter.
var text = 'New York Mills';
var term = 'new york mills';
return text.localeCompare(term)===0;
In most cases we need to compare values with insensitive register. And this code will return false, which will lead to the creation of duplicate records in the database. Moreover String.prototype.localeCompare () is not supported by browser Safary and this code will not work in this browser;
return this.text.localeCompare(term)===0;
will better replace to
return this.text.toLowerCase() === term.toLowerCase();

Resources