How do you bind asynchronously to a Kendo UI control? - kendo-ui

I've got a drop-down selection box that has 2,000 items in it. I tried to bind it like this:
#(Html.Kendo().MultiSelect().Name("kendo-dropdown-manager")
.DataValueField("Value")
.DataTextField("Text")
.Placeholder("Select Entity")
.BindTo(Model.ManagersList)
In my Razor Page, I implement the 'Get' asynchronously:
public class IndexModel : PageModel
{
public IndexModel()
{
this.ManagersList = new List<SelectListItem>();
}
public List<SelectListItem> ManagersList { get; private set; }
public Task OnGetAsync()
{
ViewsController viewsController = new ViewsController();
List<ManagerViewModel> managers = await viewsController.GetManagersAsync();
this.ManagersList.AddRange(
from m in managers
select new SelectListItem
{
Value = m.Id.ToString(),
Text = m.Name
});
}
}
When the page appears, the drop down is empty. If I change 'OnGetAsync' to a completely synchronous version, the drop down is populated correctly. My page has several similar controls and I don't want to load them up synchronously. What's the proper way to start an operation and have the Kendo controls catch the results when they're returned?

Related

Xamarin Native, Binding actions to listview items

I would like to ask about bindings. What is the best approach to bind some actions in listview items in ios and android using xamarin in mvvm world. As I understand, we have few approaches.
1.
For every list item we have some Model, and to this model we have to add some Commands.
For example:
public class ItemModel
{
public string MyName { get; set; }
public ICommand RemoveCommand { get; set; }
}
Where in ViewModel we have SomeInitMethod
public class ViewModel
{
public ObservableCollection<ItemModel> Items {get;set;}
public async Task SomeInitMethod
{
Items = new ObservableCollection(await _myApiService.FetchItemsAsync());
foreach(var item in Items)
{
item.Command = new RelayCommand(RemoveItem);
}
}
public void RemoveItem(ItemModel item)
{
Items.Remove(item);
}
}
But I see a drawback in SomeInitMethod where we should set RemoveCommand. What if we should to set 2 or even more commands than we duplicate code in ListItemView(somehow we need to bind all these commands)?
Next approach is somehow handle events of remove/toggle buttons and others in Listview and then delegate this commands directly to ViewModel.
Example:
ContactsListView.ItemRemoveClicked += (ItemModel model) => ViewModel.RemoveItem
Advantages is: we no longer need to handle commands in ViewModel
Drawback is: we need every time to write custom ListView and support event handling in code-behind.
The last approach is to send ViewModel to ListItem to set Commands.
Example
somewhere we have method CreateListViewItem on the view, let's say on iOS.
private void InitTableView() {
TableView.RegisterNibForCellReuse(ItemViewCell.Nib, ItemViewCell.Key);
var source = new ObservableTableViewSource <ItemModel>
{
DataSource = ViewModel.Items,
BindCellDelegate = (cell, viewModel, index) =>
{
if (cell is ItemModel memberCell)
{
memberCell.BindViewModel(viewModel);
memberCell.RemoveItem = (item) => ViewModel.RemoveItem;
}
}
};
TableView.Source = source;
}
Advantages: we no longer need to have Commands in Model, and we don't need to setup this Commands in ViewModel.
Possibly, drawback is that we somehow need to have ViewModel reference.
In WPF or UWP you have DataContext, you can binding directly to ViewModel.
Which approach you use, maybe I miss something, and it would be perfect if you provide some examples or thoughts.
Thanks.

I can't fetch data from the db with a Html.DropDownListFor, in MVC 4 (or 5)

I have read some pages regarding DDls, but I can't get my DDLF (DropDownListFor) to work. I have a model, and a db, but I don't know how I can in the view show one DDLF.
What I get to work, is this code:
#foreach (var item in Model)
{
#Html.DropDownListFor(m => item.Id,
new SelectList(ViewBag.SjukhusDropDownList),
"Choose something")
}
But then I get several DDLs. And I don't know how to insert data into them, so the data to the user will get fetched from the db. Like sending an id with something to the action, to do something with it. But here, I can only click on anything in the list, but of course, nothing will happen since I haven't bound any data to any option in that select list.
Here's an example for DropDownListFor in MVC 4
In the model create two properties, one for the list itself and one for identifying the list entries. In this example the vu-number identifies a merchant:
public class MyModel
{
[Display(Name = "VU")]
public string vu{ get; set; }
public List<SelectListItem> merchantList{ get; set; }
}
Initialize and load model data in the Http GET method of the controller:
public ActionResult ShowMerchants()
{
MyModel model = new MyModel();
model.merchantList = GetMerchants();
model.vu = model.merchantList[0].Value;
return View(model);
}
Create the list items using some rows from database. I.e. you can use TableAdapter and DataTable to achieve it:
private List<SelectListItem> GetMerchants()
{
List<SelectListItem> merchantList = new List<SelectListItem>();
MyDataSet.viewVUDataTable myDataTable = new MyDataSet.viewVUDataTable();
MyDataTableAdapters.VUListTableAdapter myTableAdapter = new MyDataTableAdapters.VUListTableAdapter();
myTableAdapter.Fill(myDataTable);
// iterate over rows in datatable
for(int i=0; i< myDataTable.Rows.Count; i++)
{
// Text is shown in the dropdownlist
// Value is used to identify the selected item
merchantList.Add(
new SelectListItem { Text = myDataTable[i].text, Value = myDataTable[i].vu});
}
return merchantList;
}
and in the view:
#Html.DropDownListFor(
model => model.vu,
Model.merchantList,
new { #class = "form-control" })

Assigning Key from Kendo Grid row to main Model within a View

Using Kendo Grid in an MVC application.
The primary model for the View (PlanViewModel) contains a property which is a foreign key to another entity (BuildingId) For instance,
public class PlanViewModel
{
public int PlanId { get; set; }
public string PlanName { get; set; }
public int BuildingId { get; set; }
...
}
The Building ID is to be populated by the selected row of a BuildingGrid on the View.
So with the BuildingGrid, I'm using the .Selectable setting to invoke an onChange event. How in the following event would I update the model.BuildingId
function onChange(arg) {
var selected = $.map(this.select(), function (item) {
return $(item).text();
});
/// UPDATE MODEL BuildingId here
}
Thanks!
Figured it out. First, I associated an Html attribute to the model property like so:
#Html.HiddenFor(m => m.BuildingID, new {id = "Id"})
Then I added the following jquery script:
$('#BuildingGrid').click(function() {
var gview = $(this).data("kendoGrid");
var selectedItem = gview.dataItem(gview.select());
var BuildingId = selectedItem.BuildingId;
$("#Id").val(BuildingId);
});
That set the Model.BuildingId perfectly.
Thanks to a tip I got from here: set a value to model using jQuery

Single property not getting bound on HttpPost

I'm working on the first MVC3 project at our company, and I've hit a block. No one can seem to figure out what's going on.
I have a complex Model that I'm using on the page:
public class SpaceModels : List<SpaceModel> {
public bool HideValidation { get; set; }
[Required(ErrorMessage=Utilities.EffectiveDate + Utilities.NotBlank)]
public DateTime EffectiveDate { get; set; }
public bool DisplayEffectiveDate { get; set; }
}
In the Controller, I create a SpaceModels object with blank SpaceModels for when Spaces get combined (this would be the destination Space).
// Need a list of the models for the View.
SpaceModels models = new SpaceModels();
models.EffectiveDate = DateTime.Now.Date;
models.DisplayEffectiveDate = true;
models.Add(new SpaceModel { StoreID = storeID, SiteID = siteID, IsActive = true });
return View("CombineSpaces", models);
Then in the View, I am using that SpaceModels object as the Model, and in the form making a TextBox for the Effective Date:
#model Data.SpaceModels
#using (Html.BeginForm("CombineSpaces", "Space")) {
<div class="EditLine">
<span class="EditLabel LongText">
New Space Open Date
</span>
#Html.TextBoxFor(m => m.EffectiveDate, new {
size = "20",
#class = "datecontrol",
// Make this as a nullable DateTime for Display purposes so we don't start the Calendar at 1/1/0000.
#Value = Utilities.ToStringOrDefault(Model.EffectiveDate == DateTime.MinValue ? null : (DateTime?)Model.EffectiveDate, "MM/dd/yyyy", string.Empty)
})
#Html.ValidationMessageFor(m => m.EffectiveDate)
</div>
<hr />
Html.RenderPartial("_SpaceEntry", Model);
}
The Partial View that gets rendered iterates through all SpaceModels, and creates a containing the Edit fields for the individual SpaceModel objects. (I'm using the List to use the same Views for when the Spaces get Subdivided as well.)
Then on the HttpPost, the EffectiveDate is still back at it's DateTime.MinValue default:
[HttpPost]
public ActionResult CombineSpaces(SpaceModels model, long siteID, long storeID, DateTime? effectiveDate) {
// processing code
}
I added that DateTime? effectiveDate parameter to prove that the value when it gets changed does in fact come back. I even tried moving the rendering of the TextBox into the _SpaceEntry Partial View, but nothing worked there either.
I did also try using the #Html.EditorFor(m => m.EffectiveDate) in place of the #Html.TextBoxFor(), but that still returned DateTime.MinValue. (My boss doesn't like giving up the control of rendering using the #Html.EditorForModel by the way.)
There has to be something simple that I'm missing. Please let me know if you need anything else.
Looking at the source code for DefaultModelBinder, specifically BindComplexModel(), if it detects a collection type it will bind the individual elements but will not attempt to bind properties of the list object itself.
What model binding does is attempt to match the names of things or elements in the view to properties in your model or parameters in your action method. You do not have to pass all of those parameters, all you have to do is add them to your view model, then call TryUpdateModel in your action method. I am not sure what you are trying to do with SpaceModel or List but I do not see the need to inherit from the List. Im sure you have a good reason for doing it. Here is how I would do it.
The view model
public class SpacesViewModel
{
public DateTime? EffectiveDate { get; set; }
public bool DisplayEffectiveDate { get; set; }
public List<SpaceModel> SpaceModels { get; set; }
}
The GET action method
[ActionName("_SpaceEntry")]
public PartialViewResult SpaceEntry()
{
var spaceModels = new List<SpaceModel>();
spaceModels.Add(
new SpaceModel { StoreID = storeID, SiteID = siteID, IsActive = true });
var spacesVm = new SpacesViewModel
{
EffectiveDate = DateTime.Now,
DisplayEffectiveDate = true,
SpaceModels = spaceModels
};
return PartialView("_SpaceEntry", spacesVm);
}
The POST action method
[HttpPost]
public ActionResult CombineSpaces()
{
var spacesVm = new SpacesViewModel();
// this forces model binding and calls ModelState.IsValid
// and returns true if the model is Valid
if (TryUpdateModel(spacesVm))
{
// process your data here
}
return RedirectToAction("Index", "Home");
}
And the view
<label>Effective date: </label>
#Html.TextBox("EffectiveDate", Model.EffectiveDate.HasValue ?
Model.EffectiveDate.Value.ToString("MM/dd/yyyy") : string.empty,
new { #class = "datecontrol" })
Sometimes you need to explicitly bind form data using hidden fields such as
#Html.HiddenField("EffectiveDate", Model.EfectiveDate.)
In order to bind the properties of the SpaceModel object you can add individual properties such as SiteID to the view model or add a SpaceModel property for a single SpaceModel. If you want to successfully bind a complex model, add it as a Dictionary populated with key-value pairs rather than a List. You should then add the dictionary to the view model. You can even add a dictionary of dictionaries for hierarchical data.
I hope this helps :)

When using DropDownListFor how do I bind the SelectList to the Model

This page works in two steps,
Step 1 - The user hits Index() and the SelectList is populated with the applications from the databse.
Step 2 - they select an applicaiton from the list, which posts the page back, which reloads the page with the application Details added
Error: When I run this and get to step 2, I get an error back saying:
The ViewData item that has the key 'ApplicationId' is of type 'System.Int32' but must be of type 'IEnumerable'.
This appears to be because the Model.ApplicationList is now null as it hasn't bound back to the model when the form was posted, can I make it do this?
View:
#using (Html.BeginForm())
{
#Html.DropDownListFor(x => x.ApplicationId, Model.ApplicationList, "Select an Application" , new { #onchange = "this.form.submit();" })
}
Model:
public class IndexModel
{
public int ApplicationId { get; set; }
public List<SelectListItem> ApplicationList { get; set; }
public string Detail { get; set}
}
Controller:
public ActionResult Index()
{
using (var dc = new Entities())
{
var model = new IndexModel();
model.ApplicationList = new List<SelectListItem>();
var applications = dc.Applications.OrderBy(a => a.Name).ToList();
foreach (var application in applications)
{
model.ApplicationList.Add(new SelectListItem
{
Selected = false,
Text = application.Name,
Value = application.Id.ToString()
});
}
model.ApplicationId = 1;
return View(model);
}
}
[HttpPost]
public ActionResult Index(IndexModel model)
{
model.Detail = GetDetail(model.ApplicationId);
return View(model);
}
I was struggling with the same problem. It doesn't look like .net mvc3 lets you do this without the help of jquery. Drop down lists will get their selected item bound to the model when posting but not all the items in the combo box. You would have to rebuild it each time you pass the viewmodel back to the view.
Another way around losing the dropdown list is to use ajax.

Resources