Kendo UI Grid with Cascading DropDownList - grid

I have a Kendo UI Grid on my Razor Layout which fetches data from the controller.
In this grid I wish to have a set of 3 DropDownLists which are:
ProductGroups, Products, Services
The behaviour I wish to implement is, when I Add a row to the Grid, I choose ProductGroups first, and the Products DropDown is updated with products list filtered by GroupId (value). Then select Product and like the first one, update the Services DropDown with services filtered by productId (value).
I don't quite know how to achieve this, can anyone please help me?
Thank you all for your help.
Best Regards.

Here is what I've done for GridEditMode.InCell. I have Client and Fund, each client have own list of Funds, so when user select client I need to only show Funds specific to this client
View:
Kendo Grid UI setup
c.ForeignKey(p => p.ClientId, Model.Clients, "Id", "ClientName")
.Title("Client")
.Width(100);
c.ForeignKey(p => p.FundId, Model.Funds, "Id", "Description")
.EditorViewData(new {funds = Model.Funds})
.EditorTemplateName("FundForeignKeyEditor")
.Title("Fund")
.Width(100);
})
.Editable(x => x.Mode(GridEditMode.InCell))
.Events(e => e.Edit("gridEdit"))
Now when user click on Fund you need to perform filtering of the datasource for funds, you do it on "gridEdit" event using JavaScript. You put this code in the same view/file as your code above
<script type="text/javascript">
function gridEdit(e) {
var fundDropDown = e.container.find("#FundId").data("kendoDropDownList");
if (fundDropDown) {
fundDropDown.dataSource.filter({ field: "ClientId", operator: "eq", value: e.model.ClientId });
</script>
Fund has "FundForeighKeyEditor" editor template, which you have to add in to Views\Shares\EditorTemplate folder. You can use any name, just make sure name of the file template matches name of the EditorTemplateName. In my case I used "FundForeignKeyEditor" as EditorTemplate and FundForeighKeyEditor.cshtml file
FundForeighKeyEditor.cshtml
#model FundViewModel
#(
Html.Kendo().DropDownListFor(m => m)
.BindTo((System.Collections.IEnumerable)ViewData["funds"])
.DataTextField("Description")
.DataValueField("Id")
)
Here is a FundViewModel, it contains ClientId so I can perform filtering on it
public class FundViewModel
{
public string Id { get; set; }
public string ClientId { get; set; }
public string Description { get; set; }
}

The easiest way would be to use the cascading dropdownlists:
http://demos.kendoui.com/web/dropdownlist/cascadingdropdownlist.html
inside of the editor templates for each of these columns.
If you are using popup editing you might consider customizing the popup menu like here:
http://www.kendoui.com/code-library/mvc/grid/custom-popup-editor.aspx
If you are using InLine editing you should use this approach to customize the editor templates:
http://docs.kendoui.com/documentation/getting-started/using-kendo-with/aspnet-mvc/helpers/grid/editor-templates
If you are using InCell - lets just say its not possible.

This works with Inline edit mode. I haven't tried any others yet.
Tie into the change event of the first drop down, find the target drop down, and change its datasource. data-selector-type is an attribute I add to each drop down in the cascade chain to make the select easy.
function clientDropDownEditor(container, options) {
var clientCombo = $('<input id="client' + options.uid + '" data-selector-type="client" data-text-field="Name" data-value-field="Name" data-bind="value:' + options.field + '"/>')
.appendTo(container)
.kendoComboBox({
dataTextField: "Name",
dataValueField: "Name",
dataSource: {
transport: {
read: "json/data/getClients"
}
},
change: function (e) {
// Find the element with the required selector type in the same TR
var kendoComboSites = e.sender.element.closest("tr").find("[data-selector-type=site]").data("kendoComboBox");
kendoComboSites.dataSource.transport.options.read.url = "json/data/GetClientSites/" + e.sender.element.val() + "/" + $("#Id").val();
kendoComboSites.dataSource.read();
kendoComboSites.refresh();
}
}).data("kendoAutoComplete");
}

Related

Building ASP.NET with MVC, trying to create a filter from Dropdown list

First post here so be gentle please :)
I am creating an ASP.NET with MVC web app that shows a list of items of the same class (Laptop)
I want to create a Dropdown list in the main view below each title that will allow me to filter the results OnChange - hence the selection is empty, but the user can click and select the value in the DropDown list, and the main view items list will update immediately according to the selection.
This is how the list looks now:
Snapshot of the list
I want to implement a dropdown, but I can't seem to get the selected value from the dropdown: (The DropDownlist is populated properly, and working)
<select class="form-control" asp-items="Html.GetEnumSelectList<purpose>()"
onchange="#{Model = Model.Where(m=>m.Purpose == /*HERE SHOULD BE THE VALUE SELECTED*/)}">
<option selected="selected" value="">-Select one-</option>
</select>
And then refreshing the page... but - how do I get the selected value from inside the selection?
If it was in JavaScript I would have done:
html.document.getElementById("The id of the selection").value
but I don't want JavaScript since this is all ASP.NET
To be clear, I have 5 different dropdown lists to filter by, and they can be selected or not.
You're mixing up client-side vs. server-side code. The example below uses only MVC and a full client-server architecture. Each request requires a round-trip to the server.
You have 3 components in this scenario.
ProductsViewModel.cs
public class ProductsViewModel
{
public IList<Laptop> Laptops { get; set; }
public PurposeEnum Purpose { get; set; }
}
ProductsController.cs
public class ProductsController : Controller
{
[HttpGet]
public IActionResult Index()
{
// Retrieve all records without a filter
var unfiltered = db.Laptops.ToList();
var viewModel = new ProductsViewModel() { AvailableLaptops = unfiltered };
return View(viewModel);
}
[HttpPost]
public IActionResult Index(ProductsViewModel viewModel)
{
// Use viewModel.Purpose & viewModel.Maker to filter records from database
var filtered = db.Laptops.Where(l => l.Purpose == viewModel.Purpose).ToList();
var filteredViewModel = new ProductsViewModel()
{
AvailableLaptops = filtered,
Purpose = viewModel.Purpose
};
return filteredViewModel;
}
}
Products\Index.cshtml
#model MyNameSpace.ViewModel
using (BeginForm())
{
#Html.DropDownList(Html.GetEnumSelectList<PurposeEnum>())
foreach (var l in Model.AvailableLaptops)
{
// Loop through Model.AvailableLaptops and generate table
}
<input type="submit" value="Search" />
}
When you visit the URL /Products/Index for the first time, the GET action handler will be triggered. It will generate an unfiltered list of your products along with the dropdown list required for filtering.
When you make your selection and submit the form, the POST action handler will be triggered, and use the selected values in the Purpose and Maker properties of the view model to filter the records. The same view is generated, but with a filtered down list of products.
This is very basic code that ignores validation, error handling and security.

Kendo Tabstrip with dynamic content in a Partial View

I am new to ASP.NET MVC. I am facing one or other issues to design the layout for below scenario. Could you someone help me with a solution and I will definitely appreciate your help.
The requirement is:
This is an existing application. While loading view there is a Master View and inside few partial views already defined.
In one of the Partial view, I need to have a same layout multiple times on demand. It is depends on the user how many required. may be 1 or 2 or more. We are using Telerik Kendo controls extensively in our UI and in existing View we strongly typed Model object with View.
I would like to go with Kendo Tabstrips control and add Tab dynamically when required by the user. Also, the layout is exactly same, So, would like to design (Html table with many controls like textbox, dropdown etc.) each tab layout as Partial View so that I can reuse the design. Please let me know whether this approach is best or any better approach is available.
I need to get the entire data when the user Submit the master view . Each main partial View contains and the parent of the Tabstrips Partial view also contains a but not defined for each tabstrip partial view as I need data as collection of objects in one of the property in Parent Partial View Model Object.
Can you please let me know how to design model object for each tabs(Partial View) as well as Parent Partial View. it could be good, if you could show a small example code.
The below are the issues faced during designing this
Unable to add inside another as getting below error
Inline markup blocks (#Content) cannot be nested. Only one level of inline markup is allowed.
#(Html.Kendo().PanelBar().Name("panelBar_" + panelName).Items(pb => pb.Add().Text("PCG").Expanded(Expanded).Selected(true)
.Content(#<text>
<form id="frm_#(panelName)" onsubmit="DisableEvent(event)">
<div style="width:100%; height:auto;">
<button class="k-button">Add new PCG</button>
#(Html.Kendo().TabStrip()
.Name("TabPCG").HtmlAttributes(new { style = "width:100%;" })
.Items(items =>
{
items.Add()
.Text("PCG 1 <button data-type='remove' class='k-button k-button-icon' onclick='deleteMe(this)'><span class='k-icon k-i-close'></span></button>")
.Encoded(false)
.Selected(true)
.HtmlAttributes(new { style = "width:12%", id = "tabPCG1" })
//.LoadContentFrom("_PCGTab", "Home", new { tabId ="tab1"});
.Content(#<text>#(Html.Partial("_PCGTab"))</text>);
})
)
</div>
</form>
</text>)))
2.Then Changed the design as shown below. defined partial view in Parent View
#helper RenderPCGTab()
{
<div style="width:100%; height:auto;">
<button class="k-button">Add new PCG</button>
#(Html.Kendo().TabStrip()
.Name("TabPCG").HtmlAttributes(new { style = "width:100%;" })
.Items(items =>
{
items.Add()
.Text("PCG 1 <button data-type='remove' class='k-button k-button-icon' onclick='deleteMe(this)'><span class='k-icon k-i-close'></span></button>")
.Encoded(false)
.Selected(true)
.HtmlAttributes(new { style = "width:12%", id = "tabPCG1" })
//.LoadContentFrom("_PCGTab", "Home", new { tabId ="tab1"});
.Content(#<text>#(Html.Partial("_PCGTab"))</text>);
})
)
</div>
}
and designed Kendo panel as shown below the Parent Partial View
#(Html.Kendo().PanelBar().Name("panelBar_" + panelName).Items(pb => pb.Add().Text("PCG").Expanded(Expanded).Selected(true)
.Content(#<text>
<form id="frm_#(panelName)" onsubmit="DisableEvent(event)">
#RenderPCGTab()
</form>
</text>)))
Since you use a strongly typed View, I would recommend using a Tuple as the model.
The Item1 would hold the required model details, while Item2 would hold the required number of tabs (it holds the names of the tabs).
#model Tuple<[Model],List<string>>
Now create a Kendo Tabstrip control, with dynamic items (based on model's Item2)
#(Html.Kendo().TabStrip()
.Name("KendoTabStrip") //You need to dynamically change the name by appending a unique parameter in case you need multiple Tabstrips
.Animation(animation =>
animation.Open(effect =>
effect.Fade(FadeDirection.In)))
.Items(tabstrip =>
{
var TabItemIndex = 0;
foreach (var TabItem in Model.Item2)
{
tabstrip.Add().Text(TabItem)
.Selected(false)
.HtmlAttributes(new { id = "TabStripButton" + TabItem + "_" + TabItemIndex, title = TabItem}) //Generate a dynamic ID for each Tab
.Content(" ");
TabItemIndex++;
}
})
)
Once you have created the structure of the Tabstrip, you need to populate each tab with its corresponding content
In the View (Parent Partial View) itself, create a Ready function for the tabstrip and serialize the object using JSON
$(("KendoTabStrip")).ready(function () {
_TBSModelName = #Html.Raw(JsonConvert.SerializeObject(this.Model.Item1))
TabStripUserControl();
});
Note: This is in case you need the Model Data in your child partial view.
Create a javascript file and place the function TabStripUserControl() in it. This function will create your content and place it into the tab.
function TabStripUserControl()
{
var _LocalTBSModel = _TBSModelName
var items = "#KendoTabStrip" + " .k-tabstrip-items";
$(items).click(function (z) {
}
);
}
Inside the function (click function), create a div and provide a dynamic ID for the same before placing it inside the tab using Javascript/JQuery.
var div = $("<div/>");
Use Ajax call to call your controller, which in turn will call your Child Partial View (which contains HTML controls) and render the partial view inside the above created div on Ajax call's success.
$.ajax({
url: 'Controller/ActionMethod',
data: JSON.stringify({ Value: "SomeValue" }),
type: 'POST',
contentType: 'application/json;',
async: false,
success: function (data) {
div = data;
}
});
Hope this helps.

Kendo : ComboBox in a grid - Sending additional data of selected combobox to combobox Read()

ASP.NET MVC5
I have a combobox in a grid (InLine Edit):
columns.Bound(x=>x.AccountID).EditorTemplateName("MyTemplate")
Where MyTemplate is in /Shared
There are millions of Accounts.
When I try to edit the combo box in a grid and choose a new value, the ID of the Account, not the name, appears. This is because of course the name of the account is not immediately present so in the Read().Data() of the ComboBox.Datasource I need to send additional data; the AccountID.
My ComboBox Template looks like this:
.DataSource(source=>
source.Read(read =>
read.Action("ReadAccounts".....)
.Data("HERE IS WHERE I NEED TO SEND THE ACCOUNT ID AS EXTRA DATA
WHEN THIS CBO TEMPLATE IS IN A GRID")
Thank you
Here's the Combo Box defined in a partial view in ~/Views/Shared/EditorTemplates/ComboBoxTemplate
#(Html.Kendo().ComboBox()
.Name("AcctName")//must match Field Name that is being edited
.HtmlAttributes(new { style = "width:250px" })
.DataTextField("AcctName")
.DataValueField("AcctCd")
.Filter(FilterType.StartsWith)
.AutoBind(true)
.MinLength(3)
.DataSource(source =>
{
source.Read(read =>
{
read.Action("GetCombo", "GridPost").Data("OnAdditionalData");
})
.ServerFiltering(true);
})
)
Here's the view and controller action
columns.Bound(x => x.AcctName).Title("Acct Name").EditorTemplateName("ComboBoxTemplate");
function OnAdditionalData() {
var entityGrid = $("#ProposalGridX").data("kendoGrid");
var selected = entityGrid.dataItem(entityGrid.select());
//if the id is off the Grid Row and not the ComboBox
//select the row and pull the fields
//selected.PropertyName
return {
text : $("#AcctName").data("kendoComboBox").text(),
val : $("#AcctName").data("kendoComboBox").value()
};
}
public JsonResult GetCombo(string text, string val)
{
List<PortfolioViewModel> model = new AUMBusiness().GetAum(DateTime.Now);
if (!string.IsNullOrEmpty(text))
{
model = model.Where(x => x.AcctName.StartsWith(text)).ToList();
}
return Json(model, JsonRequestBehavior.AllowGet);
}
Like with any Ajax calls, placing break points in the code might prevent the widget from performing as intended. For ex. using incell editing while clicking the Field to edit, if you place a breakpoint in GetCombo the ComboBox editor template will not default correctly to that value.

Vary type of input control based on drop down selection?

I'm building an edit screen where a use can edit rows of data. One of the fields is represented by a drop down, and another is an input field named 'value'. Now, depending on the value in the dropdown, I need to have different kinds of input controls for the value input control. Sometimes it should be a text box, others a datetime control (html5 and / or jqUI date picker), and finally a dropdown list containing a fixed set of values ('Yes' / 'No').
So basically sometimes I need to accept any string data, sometimes a date, and sometimes a boolean (but with a select box, not a check box). What's my best option for implementing this? Ideally the value entered would not be lost moving from one kind of input to another, while the user is on this edit page. On post back, I have a single database value to store (its a sql_variant).
Also, I'm using asp.net mvc3 so an ideal solution will work with the normal Html.ValidateFor and Html.ValidationMessageFor methods.
After lot's of time in JSFiddle, I made this solution. And I think it's pretty cool. It wasn't really that hard. and you can adapt it to whatever you need. just click here.
basically I make variables to represent the possible values. then I make a variable to hold the active element.
Whenever the type selector changes, it calls the change() function which uses if() statements to check what was selected, and then it sets the active element accordingly.
And finally, it calls the hide() function which hides the inactive elements.
here is the updated version
RED ALERT: I realized this didn't work in FF (maybe it was just my browser but whatever).
so I fixed it here
The typical way I accomplish something like this is to actually store 3 different fields in the db for each of the different types of values. Then I create something like the following html:
<!-- Input type selector -->
<div class="cell variable-selector">
<select><option ...</select>
</div>
<!-- varied input -->
<div class="cell variable show-text">
<div class="text"><input type="textbox"></div>
<div class="date-picker"><input type="textbox" class="datepicker"></div>
<div class="drop-down-bool"><select><option ...</select>
</div>
Then I have css that hides or shows the correct input element based on which class the cell has:
div.variable div { display:none }
div.show-text div.text { display: inline }
div.show-date-picker div.date-picker {display: inline }
div.show-drop-down-bool div.drop-down-bool {display: inline}
lastly you can setup some javascript so that when you change your variable-selector you change the class of your variable cell. Which jquery one might do this as so:
$(document).ready(function() {
var variableSelector = $("div.variable-selector > select");
variableSelector.change(function() {
var type = $(this).text();
var class = "cell variable show-" + type;
var variableCell = $(this).parent().parent().find("variable");
variableCell.attr("class", class);
})
});
As a quick warning I wrote the above code on the fly in the stack overflow editor window so there might be a couple of syntax errors or a minor bug somewhere but the basic idea should work. Hope it helps.
--Adam
In case you want to make full use of mvc3 validations, consider this approach.
Model
public class MultiValueViewModel
{
[Required]
public string TextValue { get; set; }
[Required]
public bool? BooleanValue { get; set; }
public MultiValueType ValueType { get; set; }
}
public enum MultiValueType
{
Text,
Boolean
}
View
#model MultiValueViewModel
#Html.DropDownListFor(m => m.ValueType, new SelectList(new[]
{
MultiValueType.Text,
MultiValueType.Boolean
}), new { #id = "multi_value_dropdown" })
<p>
<div data-type="#MultiValueType.Text" class="multi-value-pane">
#Html.EditorFor(m => m.TextValue)
</div>
<div style="display: none" data-type="#MultiValueType.Boolean" class="multi-value-pane">
#Html.DropDownListFor(m => m.BooleanValue, new SelectList
(new [] {
new SelectListItem { Text = "Yes", Value = "true"},
new SelectListItem { Text = "No", Value = "false"}
}, "Value", "Text"), optionLabel: "[Not Set]")
</div>
</p>
Javascript:
<script type="text/javascript">
$(function () {
$("#multi_value_dropdown").change(function () {
var value = $(this).val();
$(".multi-value-pane").each(function () {
$(this).css("display", value == $(this).attr("data-type") ? "block" : "none");
});
});
})
Inside your controller, receive MultiValueViewModel value (alone or inside parent model), and based on selected ValueType save to database. Please note that you will need jquery.validate version 1.9 if you need to skip validation on hidden fields (e.g. :hidden).

ASP.NET mvc: How to automatic fill the date field with today's date?

Assume that a model has a datetime datatype.
So in view there will be a blank field ask you to input datetime.
Is there a way to fill this HTML field with today's date/datetime as default value?
model.SomeDateField = DateTime.Now();
return View(model);
Simple
<%: Html.TextBox("date", DateTime.Now.ToShortDateString()) %>
Or use javascript to get the client's browser date. Better yet use jquery's datepicker for nice UI for selecting dates. With it you can also prepopulate the default date:
/**
Enable jquery UI datepickers
**/
$(document).ready(function () {
$(function () {
$(".date-select").datepicker({ dateFormat: 'dd.mm.yy' });
$(".date-select").datepicker($.datepicker.regional['sl']);
});
$(function () {
$("#someDateField").datepicker('setDate', new Date());
});
});
Wanted to add what I found that I needed for filling a datepicker with today's date that was being generated from the DateTime data type in HTML5 (via razor at the view in the value attribute) which is this:
#Html.TextBoxFor(model => model.YourDateInModel, new { #id = "YourDateId", #type = "date",
#Value = DateTime.Now.ToString("yyyy'-'MM'-'dd") })
Note that the ToString() format was necessary to get it to show the date in the datepicker field (it requires a database format).
Source: https://forums.asp.net/p/2154640/6320474.aspx?p=True&t=637376668781782563
Edit: This ended up leaving the database blank/null by itself, so I also had to set the model with the initial value of today's date (as mentioned above).
public DateTime YourDateInModel { get; set; } = DateTime.Now;
In my case I needed both. It may also be helpful to use something like:
$("#YourDateId").prop("disabled", true);
If you are using JQuery/Javascript to prevent changes to the field where applicable.

Resources