I have a .net core 8 web application, that has a grid that loads data via Ajax. When I edit a row, I'd like the `Contract` column to be a multi-select dropdown. When I edit a row, the control is rendered, but there is no data in it. I have set static data and the data does show up.
Here's my grid:
@(
Html.Kendo().Grid<ContractBenefitViewModel>()
.Name("ContractBenefitGrid")
.Columns(columns =>
{
columns.Bound(c => c.Id).Hidden(true);
columns.Bound(c => c.Contracts)
.ClientTemplate("#= displayContracts(data) #")
.EditorTemplateName("Contracts")
.Width(400)
.Filterable(false)
.Sortable(false);
columns.ForeignKey(c => c.BenefitId, (IEnumerable)ViewBag.Benefits, "Id", "DisplayName");
columns.Bound(c => c.Gl)
.Width(150);
columns.Bound(c => c.Comments);
columns.Bound(c => c.Active).Hidden(true);
columns.Bound(c => c.Deleted).Hidden(true);
columns.Command(command =>
{
command.Edit();
command.Destroy();
})
.HtmlAttributes(new { style = "text-align: center;" })
.Width(200);
})
.Editable(editable => editable.Mode(GridEditMode.InLine))
.ToolBar(toolbar =>
{
toolbar.Create();
})
.Pageable(pager =>
{
pager.Refresh(true);
pager.PageSizes([10, 20, 50]);
})
.Sortable()
.Filterable()
.NoRecords()
.DataSource(dataSource => dataSource
.Ajax()
.Batch(true)
.PageSize(10)
.ServerOperation(true)
.Events(events => events.Error("error_handler").RequestEnd("onRequestEnd('staticNotifications')"))
.Model(model =>
{
model.Id(p => p.Id);
model.Field(p => p.Contracts).DefaultValue(ViewBag.ContractId as List<ContractModel> ?? new List<ContractModel>());
model.Field(p => p.BenefitValue).DefaultValue(ViewBag.BenefitId);
model.Field(p => p.Comments);
model.Field(p => p.LastUpdatedBy).DefaultValue(ViewData[Literals.EmailKey]);
model.Field(p => p.Active).Editable(false).DefaultValue(Literals.Yes);
model.Field(p => p.Deleted).Editable(false).DefaultValue(Literals.No);
})
.Filter(f =>
{
f.Add(x => x.Active).IsEqualTo(Literals.Yes);
f.Add(x => x.Deleted).IsEqualTo(Literals.No);
})
.Read(read => read.Action("GetContractBenefits", "ContractBenefit").Data("forgeryToken"))
.Create(create => create.Action("CreateContractBenefits", "ContractBenefit").Data("forgeryToken"))
.Update(update => update.Action("UpdateContractBenefits", "ContractBenefit").Data("forgeryToken"))
.Destroy(destroy => destroy.Action("DeleteContractBenefits", "ContractBenefit").Data("forgeryToken"))
.Sort(sort =>
{
sort.Add(x => x.BenefitValue);
})
)
)
Here's the JavaScript
<script type="text/javascript">
function displayContracts(data) {
window.console.log('displayContracts()');
return $.map(data.Contracts, function (e) { return e.DisplayName; }).join(", ");
}
</script>
Here's my model
public class ContractBenefitViewModel
{
public long Id { get; set; }
public long BenefitId { get; set; }
[Display(Name = "Benefit")]
public string BenefitValue
{
get
{
var sb = new StringBuilder();
if (Benefit is not null)
{
sb.Append(Benefit.Value);
if (!string.IsNullOrWhiteSpace(Benefit.Gl))
{
sb.Append(" (");
sb.Append(Benefit.Gl);
sb.Append(")");
}
}
return sb.ToString();
}
}
[Display(Name = "GL Code")]
public string Gl => Benefit?.Gl ?? string.Empty;
[Display(Name = "Comments")]
[StringLength(ModelLiterals.CommentsLength)]
[MaxLength(ModelLiterals.CommentsLength)]
public string? Comments { get; set; }
public string Active { get; set; } = ModelLiterals.Yes;
public string Deleted { get; set; } = ModelLiterals.No;
public string LastUpdatedBy { get; set; } = string.Empty;
public BenefitModel? Benefit { get; set; } = null!;
[UIHint("Contracts")]
public IList<ContractModel> Contracts { get; set; } = new List<ContractModel>();
}
Here's my Editor Template (named Contracts.cshtml, in the EditorTemplates folder)
@model Lookups.ContractModel
<div style="width: 99%">
@(Html.Kendo().MultiSelectFor(m => m)
.DataValueField("Id")
.DataTextField("DisplayName")
.DownArrow()
.AutoClose(false)
.BindTo((IEnumerable<Lookups.ContractModel>)ViewData["contracts"])
)
</div>
Here's the Index and Get data methods in my controller
[HttpGet]
public async Task<IActionResult> IndexAsync()
{
Logger.LogDebug("{NameOf}()", nameof(IndexAsync));
var contracts = await Client.GetContractsAsync(); //Client is my API client library - it fetches data from my API
var benefits = await Client.GetBenefitsAsync();
ViewBag.Contracts = contracts;
ViewData["contracts"] = contracts;
ViewBag.Benefits = benefitFinancialSupports;
ViewBag.DefaultContractId = contracts?.FirstOrDefault()?.Id ?? -1;
ViewBag.DefaultBenefitId = benefits?.FirstOrDefault()?.Id ?? -1;
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<JsonResult> GetContractBenefitsAsync([DataSourceRequest] DataSourceRequest request)
{
Logger.LogDebug("{NameOf}()", nameof(GetContractBenefitsAsync));
var stopwatch = Stopwatch.StartNew();
try
{
if (!ModelState.IsValid) return Json(InvalidModelStateMessage);
var models = await Client.GetContractBenefitsAsync();
var viewModels = models
.Where(x => x is { Benefit: not null, Contract: not null })
.GroupBy(x => x.BenefitId)
.Select(g => new ContractBenefitViewModel
{
BenefitId = g.Key,
Benefit = g.First().Benefit,
Contracts = g.Select(x => x.Contract!).Distinct().ToList(),
Id = g.First().Id,
Comments = g.First().Comments,
Active = g.First().Active,
Deleted = g.First().Deleted,
LastUpdatedBy = g.First().LastUpdatedBy
})
.ToList();
return Json(await viewModels.ToDataSourceResultAsync(request));
}
catch (Exception ex)
{
Logger.LogError(ex, "{NameOf}()", nameof(GetContractBenefitsAsync));
return Json(new { success = false, message = ex.Message });
}
finally
{
stopwatch.Stop();
Logger.LogInformation("**** {NameOf} took [{Elapsed}]", nameof(GetContractBenefitsAsync),
stopwatch.Elapsed);
}
}
I should mention that I store the data one ContractId with one BenefitId (in a Mapping table), so when I return data via the `Client.GetContractBenefitsAsync()` call, it returns a List of 1-to-1 records, and I group them so I get a single Benefit with all Contracts that are associated with it (hence the Linq GroupBy call to build my ViewModel). This is how I want to present it to my users, so they can select as many Contracts as they want in the UI, and behind the scenes I'll turn into a 1:1 association. I'm just having trouble getting my data (the Contracts Multi Select) to display data. Am I missing something or is there another way to do this?
Hello,
create/update posts an empty model to the controller.
It was working in a very old asp.net mvc project. I setup a new asp.net core project. I spent some hours to fix this, but no luck.
Reading the data source is ok and the data is shown in column 'valuestream'. At the moment for 'Agenda' two values are shown, editable with a mutiselect.
Controller:
public ActionResult ManagerQuestionnaireCreate([DataSourceRequest] DataSourceRequest request, QuestionnaireViewModel rs)
{
....
return Json(new[] { rs }.ToDataSourceResult(request, ModelState));
}
Model:
View:
I have a grid with custom editors and they are bound to the grid as such.
columns.Bound(x => x.Parent).EditorTemplateName("ParentEditor").ClientTemplate("#= Parent === undefined || Parent === null ? '' : parentTemplate(Parent)#");
columns.Bound(x => x.Child).EditorTemplateName("ChildEditor").ClientTemplate("#= Child === undefined || Child === null ? '' : childTemplate(Child)#");
The two editor templates look like this:
@model List<ParentViewModel>
@(Html.Kendo()
.MultiSelectFor(m => m)
.DataTextField("Name")
.DataValueField("Id")
.Placeholder("Select one or more parents")
.AutoBind(true)
.TagMode(MultiSelectTagMode.Multiple)
.DataSource(source =>
{
source
.Read(read =>
{
read.Action("GetParent", "Lookup");
});
})
.Events(events => events.Change("onParentChange"))
)
@model List<ChildViewModel>
@(Html.Kendo()
.MultiSelectFor(m => m)
.DataTextField("Name")
.DataValueField("Id")
.Placeholder("Select one or more children")
.AutoBind(true)
.TagMode(MultiSelectTagMode.Multiple)
.DataSource(source =>
{
source
.Read(read =>
{
read.Action("GetChild", "Lookup").Data("getCurrentParents");
})
;
})
)
The UI is properly populating when the grid loads and looks like this:
Coumn Parent|Column Child
A |A1
B |B1, B2
When the user edits the row and removes item B from Column Parent, this code is invoked (which I got from Kendo UI Snippet | Kendo UI Dojo)
function onParentChange(e) {
var selectedonParentChange = this.value();
let dataItems = e.sender.dataItems();
var multiSelect = $("#Child").data("kendoMultiSelect");
var value = multiSelect.value();
multiSelect.dataSource.filter(getFilterObj(dataItems));
multiSelect.dataSource.filter({}); // Adding or removing has no effect
multiSelect.refresh();
multiSelect.value(value);
console.log("Second value: " + multiSelect.value());
var dataSource = multiSelect.dataSource;
dataSource.read();
}
function getFilterObj(dataItems){
let filtObj = {
logic: "or",
filters: [],
};
if(dataItems.length > 0){
for (var i = 0; i < dataItems.length; i++) {
filtObj.filters.push({
field: "ParentId",
operator: "eq",
value: dataItems[i].Id
});
}
} else {
filtObj.filters.push({
field: "ParentId",
operator: "eq",
value: ""
});
}
return filtObj;
}
After the code runs, the UI looks like this:
Coumn Parent|Column Child
A |A1
So far so good. The problem is that when the user hits save this ends up on the Network in the form data:
Parent[0].Id: ParentIdA
Parent[0].Name: A
Child[0].Id: ChildId1
Child[0].Name: A1
Child[0].ParentId: ParentIdA
Child[1].Id: ChildId2
Child[1].Name: B1
Child[1].ParentId: ParentIdB
Child[2].Id: ChildId3
Child[2].Name: B2
Child[2].ParentId: ParentIdB
Hi,
I have been trying to implement the multiSelect with no avail.
I have been trying to implement the multiselect and although in the pop up is working, meaning I am able to see the possible values as well as I am able to select the values, when I press save. The model that is used to create the whole object contains null instead of the selected values.
When I check in the WebApp the payload the MultiSelect looks like this:
ActivitiesUuids[0].Disabled: false
ActivitiesUuids[0].Group:
ActivitiesUuids[0].Selected: false
ActivitiesUuids[0].Text: name of the activity (I have changed the name for security)
ActivitiesUuids[0].Value: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (I have changed the Guid number into x, the value is actually a proper Guid)
My Models:
public class CourseContentViewModel
{public List<Guid>? RelatedCourseContensUuids { get; set; } //contains a list of values that has been/should be chosen from the multiselec}
public class CombinedCourseContentViewModel
{public SelectList RelatedCourseContens { get; set; } //contains a list of the values that multiSelect has to choose from}
Controller:
This is inside the Index and List methods.
ICollection<CourseContentResponseDTO> responseDTOs = await _akdService.CourseContentsAllAsync();
combinedCourseContentViewModel.RelatedCourseContentViewModel = new SelectList(responseDTOs, "Uuid", "Name");
Grid implementation:
@(Html.Kendo().Grid<WebApp.Models.CourseContentViewModel>().Name("grid")
.Columns(columns =>
{
columns.Bound(n => n.Name).Filterable(false).Title("Username").Width(250);
columns.Command(command => { command.Edit().Text("Edit"); command.Destroy().Text("Delete"); }).Width(200);
})
.ToolBar(toolBar =>
{
toolBar.Create().Text("Create");
})
.Editable(editable => editable.Mode(GridEditMode.PopUp).TemplateName("CourseContentCreateUpdate"))
.Pageable()
.Sortable()
.Scrollable()
.Filterable()
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(30)
.Model(model =>
{
model.Id(p => p.Uuid);
})
.Read(read => read.Action("List", "CourseContent"))
.Update("Update", "CourseContent")
.Create(create => create.Action("Create", "CourseContent"))
.Destroy(destroy => destroy.Action("Delete", "CourseContent"))
)
)
This is my pop-up template multiselect (other components likes comboBoxes are working fine):
<label class="Headings">Related Course Content</label>
<kendo-multiselect for="RelatedCourseContensUuids"
name="RelatedCourseContensUuids"
datatextfield="Text"
datavaluefield="Value"
placeholder="No items selected..."
bind-to="combinedCourseContentViewModel.RelatedCourseContentViewModel">
</kendo-multiselect>
My implementation of comboBoxes is almost the same except difference in naming of the tag helpers.
Hope you can help!
Juste
Hello,
I have this code on my index.cshtml:
@(Html.Kendo().MultiSelect()
.Name("selectedmembers")
.Placeholder("Please select the members on the job...")
.DownArrow(true)
.AutoClose(false)
.DataTextField("MemberName")
.DataValueField("MemberId")
.Filter("contains")
.DataSource(source =>
{
source.Read(read =>
{
read.Action("GetPeople", "Combo");
});
})
)
And my GetPeople code looks like this:
public JsonResult GetPeople(string text)
{
using (var db = new chilternModel())
{
var members = db.Members.Select(member => new Member()
{
MemberName = member.MemberName,
MemberId = member.MemberId
});
return Json(members.ToList());
}
}
When I run the code I get undefined as the text value. The database is a mySql running locally.
I have checked the members variable and it has the appropriate information for a jsonResult to be returned, but the control seems to ignore the text field (and I assume the MemberId as well)
Thanks in advance.
Michael
I have a multiselect whose items cascade from the the selected value of a drop down list.
When the page is loaded, the drop down list selection defaults to the first item in the list, which results in two items in the multiselect and the multiselect popup height shows the two items.
If a different drop down list option is selected, there are 15 items in the multiselect but the popup height still shows two items at a time. The scroll buttons work, but I would like to increase the height so that more options are visible. I have tried setting the height of the multiselect, but it has no effect.
Is there a way to resize the popup height when the items in the multiselect changes or to specify a minimum height of say 4 lines?
Thank you
I want to use this demo: https://demos.telerik.com/aspnet-core/multiselect and have a checkbox on left side of the list.
Upon selecting the tag I want to see the count of items selected instead of showing them as chips with names as selected.
I want to use this demo : https://demos.telerik.com/kendo-ui/dropdownlist/index and have a checkbox and show count for no of tags selected as in this demo: Kendo UI Snippet | Kendo UI Dojo (telerik.com).
I see a jquery approach but I want to implement a html tag helper approach in both cases for asp net core
I have attached a image that I have as UI
Hello,
How do I disable popup if no DropDownList/Multiselect contains no data or filtering does not find any items using html helpers:
@(
Html.Kendo().DropDownList()
.Name("Testi")
.DataTextField("Text")
.DataValueField("Value")
.BindTo(Source)
)
If I instantate controls using jQuery I would get this done setting noDataTemplate to false:
$("#Testi").kendoDropDownList({ noDataTemplate: false, ..
On html helpers there is no option to set NoDataTemplate boolean value and setting string value empty or null does not work.
Mikko
After converting my project from .NET 4.7.2 to .NET 6.0 I am facing below JS error for Kendo UI multiselect
readyException.js:9 Uncaught TypeError: Cannot read properties of undefined (reading 'off')
at init._editable (kendo.multiselect.js:537:40)
at init.readonly (kendo.list.js:201:18)
at new init (kendo.ts:796:30)
at HTMLSelectElement.eval (kendo.core.js:3469:32)
at Function.each (core.js:260:19)
at jQuery.fn.init.each (core.js:82:17)
at $.fn.<computed> [as kendoMultiSelect] (kendo.core.js:3468:26)
at HTMLDocument.<anonymous> (List:668:63)
at mightThrow (deferred.js:124:29)
at process (deferred.js:192:12)