I have a two kendo multiselect and i want my roles multiselect to sort of cascade from the other in a way that on the roles multiselect read i want to post a list of the values selected in my systems multiselect. This is what my roles multiselect looks like:
#(Html.Kendo().MultiSelect()
.Name("Roles")
.DataTextField("Name")
.DataValueField("Id")
.Placeholder("Select roles")
.DataSource(source =>
{
source.Read(read =>
{
read.Action("GetRoles", "UserAdmin").Data("additionalItemsGetRoles");
})
.ServerFiltering(true);
})
)
<script>
function additionalItemsGetRoles() {
var multiselect = $("#Systems").data("kendoMultiSelect").dataItems();
var length = multiselect.length;
var systems = [];
for (var i = 0; i < length; i++) {
systems.push({
Name: multiselect[i].Name,
SystemId: multiselect[i].SystemId,
Description: multiselect[i].Description
});
}
var json = JSON.stringify(systems);
console.log(json);
return json;
}
</script>
and here is what my action method looks like:
public ActionResult GetRoles([DataSourceRequest] DataSourceRequest request, IList<SystemViewModel> systems)
{
And here is what my console.log(json) shows in the console.
And here is my viewmodel:
public string SystemId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
I tried to set the action method as [httpPost] but then it can't find the method at all.
Everytime it posts to the controller it get null. What am i doing wrong here?
OK, if I understand correctly, what you want to do is to filter one multiselect list, based on the selected items in another multiselect list. Yes?
I am actually doing something similar, and here is what I did:
First, setup the two MultiSelect "widgets"
Set the Change event on the first MultiSelect ("regionChange")
Set the .Data parameter of DataSource to a js function ("regionfilter")
#(Html.Kendo().MultiSelect()
.Name("region")
.DataTextField("Name")
.DataValueField("Id")
.AutoBind(false)
.Events(e => e.Change("regionChange"))
.DataSource(ds => ds
.Read(read => read.Action("GetRegionsForFilter", "Authorization")
)
)
#(Html.Kendo().MultiSelect()
.Name("branch")
.DataTextField("Name")
.DataValueField("Id")
.AutoBind(false)
.DataSource(ds => ds
.Read(read => read.Action("GetBranchesForFilter", "Authorization")
.Data("regionfilter"))
.ServerFiltering(true)
)
)
Define js functions (I have an additional function to get the MultiSelect values, because I am using this on a couple of other MultiSelect "widgets" no the page, AND I am actually doing reverse filtering for some (such as Branch/Region though I snipped out the filtering being done on region)
function regionChange() {
var value = this.value(),
grid = $("#grid").data("kendoGrid");
<-- snipped logic related to filtering grid -->
$('#branch').data('kendoMultiSelect').dataSource.read();
}
function regionfilter() {
var values = getMultiSelectValues("region");
return { regions: values.toString() };
}
function getMultiSelectValues(multiSelectControl) {
var multiSelect = $("#" + multiSelectControl).data("kendoMultiSelect");
var values = multiSelect.value($("#value").val());
return values;
}
Finally, in my controller I am just returning a JsonResult (get request) that accepts a string argument (comma separated list of strings)
public JsonResult GetBranchesForFilter(string regions)
{
var list = _repository.Branches().GetAll().Select(x => new { x.Id, x.Name, x.RegionId });
if (!String.IsNullOrWhiteSpace(regions))
list = list.Where(x => regions.Contains(x.RegionId.ToString()));
return Json(list.OrderBy(o => o.Name), JsonRequestBehavior.AllowGet);
}
Related
i using ajax for reading data from my controller(ShiftReports).and i also using view model for combine two tables.
i don't now how to binding or iterate json array to kendo column.
this is my kendo mvc grid
#(Html.Kendo().Grid<PLIMO.ViewModel.MainEqpViewModel>()
.Name("MainEQP")
.DataSource(ds => ds
.Ajax()
.Read(read => read.Action("Eqp_Read", "ShiftReports"))
)
.Pageable()
.Sortable()
)
this is my controller
public ActionResult Eqp_Read([DataSourceRequest]DataSourceRequest request)
{
try {
using (var db = new DBContext())
{
db.Configuration.ProxyCreationEnabled = false;
var eqp = new MainEqpViewModel()
{
Tags = db.Tags.ToList(),
};
DataSourceResult result = new List<MainEqpViewModel>() { eqp }.ToDataSourceResult(request);
return Json(result);
}
}
catch(Exception ex)
{
return Json(ex.Message);
}
}
}
You need a dynamic column. please refer the sample code https://dotnetlearningarray.blogspot.com/2015/06/telerik-mvc-grid-with-dynamic-columns.html
There is a drop-down list with a multiselect
<select asp-for="UsersListForAssignTo" id="selectUser" class="form-control" asp-items="#ViewBag.UsersList" multiple="multiple"></select>
I prepare a list, so that some values were selected by default
List<string> allUsers = await db.Users.Select(u => u.UserName).ToListAsync();
List<string> usersAssignedTo = await db.MasterAssigmentTables.Where(mat => mat.TableId == id && mat.TableName == typeof(TestCaseDesign).Name).Include(mat => mat.User).Select(u => u.User.UserName).ToListAsync();
List<SelectListItem> userListAssigned = new List<SelectListItem>();
foreach (string user in allUsers)
{
if (usersAssignedTo.Contains(user))
{
userListAssigned.Add(new SelectListItem { Text = user, Selected = true });
}
else
{
userListAssigned.Add(new SelectListItem { Text = user, Selected = false });
}
}
ViewBag.UsersList = userListAssigned;
The value is passed to the model
public List<string> UsersListForAssignTo { get; set; }
The problem is that when I use "asp-for" all values become unselected
If I remove the binding to the model - then everything is ok
How to get around this problem?
The Selected value of a SelectListItem is meaningless in this context. The only thing that matters is what's in ModelState. That object is composed of values from the request, anything in ViewData (including ViewBag), and finally values on the model passed into the view.
Long and short, if you want some options initially selected, they need to be present in the list you are binding to. Set UsersListForAssignTo to the list of values you want initially selected before returning the view, and you'll be fine.
Consider this scenario:
app loads => fetches json from api => needs to modify json returned
In this case, I'm using moment to make some date modifications and do some grouping that I'll use in the UI. I looked on stack and found a similar question but didn't feel like it provided the clarity I am seeking.
Where should I use .map to create the new objects that contain the formatted & grouped dates? Should I manipulate the raw json in the api call or in the redux action before I dispatch? What is the best practice?
Is it OK to add properties and mutate the object as I am showing below,
service["mStartDate"] = mStartDate before I put the data into my store and treat it as immutable state?
First Approach - changing raw json in the api call
class TicketRepository extends BaseRepository {
getDataByID(postData) {
return this.post('api/lookup', postData)
.then(result => {
const groupedData = {}
return result.map(ticket => {
const mStartDate = moment(ticket.startDate)
const mEndDate = moment(ticket.endDate)
const serviceLength = mStartDate.diff(mEndDate,'hours')
const duration = moment.duration(serviceLength,"hours").humanize()
const weekOfYear = mStartDate.format('WW')
const dayOfWeek = mStartDate.format("d")
if(!groupedData.hasOwnProperty(weekOfYear)){
groupedData[weekOfYear] = {}
}
if (!groupedData[weekOfYear].hasOwnProperty(dayOfWeek)) {
groupedData[weekOfYear][dayOfWeek] = []
}
service["mStartDate"] = mStartDate
service["mEndDate"] = mEndDate
service["serviceLength"] = serviceLength
service["duration"] = duration
groupedData[weekOfYear][dayOfWeek].push(service)
})
})
}
}
2nd Approach, make a simple api call
class TicketRepository extends BaseRepository {
getDataByID(postData) {
return this.post('api/lookup', postData)
}
}
Change the json in the action before dispatching
export function getDataByID() {
return (dispatch, getState) => {
dispatch(dataLookupRequest())
const state = getState()
const groupedData = {}
return TicketRepository.getDataByID(userData)
.then(result => {
const groupedData = {}
return result.map(ticket => {
const mStartDate = moment(ticket.startDate)
const mEndDate = moment(ticket.endDate)
const serviceLength = mStartDate.diff(mEndDate,'hours')
const duration = moment.duration(serviceLength,"hours").humanize()
const weekOfYear = mStartDate.format('WW')
const dayOfWeek = mStartDate.format("d")
if(!groupedData.hasOwnProperty(weekOfYear)){
groupedData[weekOfYear] = {}
}
if (!groupedData[weekOfYear].hasOwnProperty(dayOfWeek)) {
groupedData[weekOfYear][dayOfWeek] = []
}
service["mStartDate"] = mStartDate
service["mEndDate"] = mEndDate
service["serviceLength"] = serviceLength
service["duration"] = duration
groupedData[weekOfYear][dayOfWeek].push(service)
})
return groupedData
})
.then(groupedData => {
dispatch(lookupSuccess(groupedData))
})
.catch(err => dispatch(dataLookupFailure(err.code, err.message)))
}
}
All data manipulation should be handled by your reducer. That is, the returned response data should be passed on to a reducer. This practice is common, because this way if there's a problem with your data, you will always know where to look - reducer. So neither of your approaches is "correct". Actions should just take some input and dispatch an object (no data manipulation).
When you want to manipulate data for 'view' purposes only, consider using reselect library, which makes it easier to handle "data views" that are composed of the existing data.
I am using Kendo UI chart in one of my projects today noticed a weird behaviour in different browser, I have date on x-axis, and it is auto transforming dates in different browsers with different time zone.
Like in UTC+5 it is showing date range from 3/1/2014 to 3/31/2014 while in UTC-6 it is showing date range from 2/28/2014 to 3/30/2014.
Basically this is happening due to the difference between the timezones of the client and the server and the form these dates are trasnfered and re-created on both sides into Date (JS) /DateTime (.NET) objects.
Basically the whole situtation is explained in details here. The dataSource that the Chart is using is the same as the one that the Grid uses so there is not difference.
Here is some example code from a project I have which you can use. Check the requestEnd handler
#(Html.Kendo().Grid<KendoMVCWrappers.Models.Person>().Name("persons")
.DataSource(dataSource => dataSource
.Ajax()
.Events(ev => ev.RequestEnd("convert"))
.Model(model => model.Id(m => m.PersonID))
.Read(read => read.Action("GetPersons", "Home"))
.Update(up => up.Action("UpdatePerson", "Home"))
)
.Filterable()
.Columns(columns =>
{
columns.Bound(c => c.PersonID);
columns.Bound(c => c.Name);
columns.Bound(c => c.BirthDate);
columns.Command(cmd => cmd.Edit());
})
.Pageable()
.Sortable()
)
<script type="text/javascript">
function convert(e) {
if (e.response.Data && e.response.Data.length) {
var offsetMiliseconds = new Date().getTimezoneOffset() * 60000;
var persons = e.response.Data;
for (var i = 0; i < persons.length; i++) {
persons[i].BirthDate = persons[i].BirthDate.replace(/\d+/,
function (n) { return parseInt(n) + offsetMiliseconds }
);
}
}
}
</script>
And the setter code of the ViewModel. Using a setter eases the whole situation since you have to do it in multiple places (before creating an object when it is fetched from the database and when it is created from the ModelBinder).
public class Person
{
public int PersonID { get; set; }
public string Name { get; set; }
private DateTime birthDate;
public DateTime BirthDate
{
get { return this.birthDate; }
set
{
this.birthDate = new DateTime(value.Ticks, DateTimeKind.Utc);
}
}
}
Good luck!
Got null response error when tried to parse date as mentioned in this post in onRequestEnd.
http://www.telerik.com/support/code-library/using-utc-time-on-both-client-and-server-sides
I resolve this by parsing in datasource parse method instead of requestEnd.
parse :function(data)
{
return ConvertToUTC(data);
}
function ConvertToUTC(data)
{
// iterate over all the data elements replacing the Date with a version
// that Kendo can work with.
$.each(data, function(index, item){
if(index == "data")
{
for(i =0 ;i< item.length; i++)
{
// Days is my date property in item collection
item[i].Days = item[i].Days.replace(/\d+/,
function (n) {
var time = parseInt(n);
return parseInt(time) + new Date(time).getTimezoneOffset() * 60000;
}
);
}
}
});
return data;
}
I am attempting to take the Name and ID fields from each object, but the fields are appearing undefined.
function OnHistoricalListBoxLoad(historicalListBox) {
$.getJSON('GetHistoricalReports', function (data) {
historicalListBox.trackChanges();
$.each(data, function () {
var listBoxItem = new Telerik.Web.UI.RadListBoxItem();
listBoxItem.set_text(this.Name);
listBoxItem.set_value(this.ID);
historicalListBox.get_items().add(listBoxItem);
});
historicalListBox.commitChanges();
});
}
[AcceptVerbs(HttpVerbs.Get)]
public JsonResult GetHistoricalReports()
{
List<HistoricalReport> historicalReports = DashboardSessionRepository.Instance.HistoricalReports;
var viewModel = historicalReports.Select(report => new
{
ID = report.ID,
Name = report.Name
}).ToArray();
return Json(viewModel, JsonRequestBehavior.AllowGet);
}
I know that I am returning the data successfully, and I know that there is valid data. I am new to MVC/JavaScript, though.. I checked case sensitivity to ensure that I wasn't making just an easy mistake, but it does not seem to be the issue. Am I missing something more complex?
Inspecting the HTTP Response JSON tab in Chrome I see:
0: {ID:1, Name:PUE}
1: {ID:2, Name:Weight}
2: {ID:3, Name:Power Actual vs Max}
3: {ID:4, Name:Power Actual}
No idea, but passing such behemoth domain models to views is very bad practice. This is so kinda domain polluted that it has nothing to do in a view. In a view you work with views models. View models contain only what a view needs. In this case your view needs an ID and a Name. So pass a view model with only those single simple properties to this view:
[AcceptVerbs(HttpVerbs.Get)]
public JsonResult GetHistoricalReports()
{
var reports = DashboardSessionRepository.Instance.HistoricalReports;
var reportsViewModel = reports.Select(x => new
{
ID = x.ID,
Name = x.Name
}).ToArray();
return Json(reportsViewModel, JsonRequestBehavior.AllowGet);
}
Now, not only that you will save bandwidth, but you will get some clean JSON:
[ { ID: 1, Name: 'Foo' }, { ID: 2, Name: 'Bar' }, ... ]
through which you will be able to loop using $.each.
UPDATE:
Now that you have shown your JSON data it seems that there is a Content property which represents the collection. So you need to loop through it:
$.each(data.Content, ...);
and if you follow my advice about the view models your controller action would become like this:
[AcceptVerbs(HttpVerbs.Get)]
public JsonResult GetHistoricalReports()
{
var report = DashboardSessionRepository.Instance.HistoricalReports;
var reportsViewModel = report.Content.Select(x => new
{
ID = x.ID,
Name = x.Name
}).ToArray();
return Json(reportsViewModel, JsonRequestBehavior.AllowGet);
}
and now loop directly through the returned collection:
$.each(data, ...);