Why is CascadeFrom() not doing anything? - asp.net-mvc

I'm really new to Kendo UI, and I'm having problems with CascadeFrom() not calling an action on my controller. Here's the bare bones of my problem:
// The parent dropdown
<select id="Testing">
<option value="0">Vehicle</option>
<option value="1">Driver</option>
<option value="2">Trailer</option>
</select>
// The dynamic dropdown
#(Html.Kendo().DropDownListFor(m => m.VDTId)
.DataValueField("Id")
.DataTextField("Item")
.DataSource(ds =>
{
ds.Read(c => c.Action("GetVDT", "CompanyVDTUnavailability")
.Data("getVDTSelection"))
.ServerFiltering(true);
})
.CascadeFrom("Testing"))
// Function to allow Kendo to pass a value to
// the type parameter of my GetVDT action.
function getVDTSelection() {
return {
type: parseInt($("#Testing").val())
};
}
The action is being called when the page first loads, and returns the correct data. The problem is, if I then make a selection from the Testing dropdown, the action is never invoked on the controller (I've verified this using a breakpoint on the action), meaning the dynamic dropdown never gets updated.
Looking through the official example, and other questions around SO, I can't see what I'm doing wrong. Could someone point me in the right direction, please?
Edit: I've tried Petur's solution below by changing the parent dropdown to the following:
#(Html.Kendo().DropDownListFor(m => m.Type)
.Name("Testing")
.DataValueField("Id")
.DataTextField("Text")
.BindTo(Model.UnavailabilityTypes))
This binds the parent dropdown correctly, but no longer invokes the controller action for the cascading dropdown even when the page first loads. Any suggestions?
Controller action signature as requested:
public JsonResult GetVDT(CompanyUnavailabilityType type)
Where CompanyUnavailabilityType is an enum.

Kendo DropDownList can cascade only from another Kendo DropDownList/ComboBox. Turn the first widget into kendo DropDownList and it should start working properly.

I think the problem is that getVDTSelection() is returning an int or string value not an Enum value. Change your method sig to an int if not, try a string and the method described in my comment
public JsonResult GetVDT(int type)
{
//AllowGet might be needed as well
return Json(jsonObjx,JsonRequestBehavior.AllowGet);
}
Edit:
You can also try to Manually force the ddl to cascade. Ditch CascadeFrom and do it manually.
function OnChangeOfParentDDL(e){
var parentValue = $("#ParentDDL").val();
$("#ChildDDL").val("").data("kendoDropDownList").text("");//clear it out
var child = $("#ChildDDL").data("kendoDropDownList");
child.dataSource.read({ type : parentValue });
}

Both Petur and C Sharper were on the right track with the problem.
I did need to build the dropdown using Html.Kendo.DropDownList() (I've just verified this after getting the solution to work.)
The method signature on the controller was a problem, but only because I'd had old testing methods left on there, leading to an ambiguous call.
The major difficulty for me was that nothing was being reported in the debugger, so diagnosing the problem was a pain. In the end, I used the Network tab of the Firefox web developer tools to ensure the Ajax request was indeed being sent, and it was. Inspecting that request showed it was leading to an ambiguous call on the controller.
Also, to clear up comments from C Sharper's answer:
The parseInt() call is not required.
The call will correctly map to an enum on the server-side, meaning the method signature I posted in my question is correct.

Related

How do you set the initial value of a EnumDropDownListFor built using helper extension?

I followed a tutorial for MVC5 about coding a helper extension to populate dropdownlists using enums. This works perfectly until using an edit view.
On a create page I fill the ddl and select no problem. But when I want to update the value on an edit view, using a ddl filled just as on the create, I can't default the actual value (from the database) as the displayed value.
I've searched high and low but starting to think it can't be done. I'm not sure what code will help so here goes;
My ddl on the edit view
#Html.EditorFor(model => model.ProjectStatus)
ProjectStatus is the enum that is used to retrieve the enum value in the helper extension and populate the ddl.
So my ddl may contain statuses such as:
New,
WIP,
Rejected,
Fixed,
Closed
and my current value may = New. So when I load the edit view I would like the value New to be defaulted in to the ddl, and be able to change this value by selecting another using the ddl.
The value I would like to default to is actually in the view (model.Status). I just can't work out how to include this in the code above.
I hope this makes sense and any help appreciated.
Thanks for reading.
I'm not exactly sure what your extension method looks like. If you want a more direct answer, you'll need to include more details on your specific implementation. That being said, here is some sample code from an edit.cshtml template on Autoquoter.com
<div class="control-group">
#Html.LabelFor(model => model.AddonType)
<div class="controls">
#Html.DropDownListFor(model => model.AddonType, EnumHelper.SelectListFor((AddonType)Model.AddonType))
#Html.ValidationMessageFor(model => model.AddonType)
</div>
</div>
Notice the parameter to EnumHelper.SelectListFor. We pass in the current value to the helper method. Then we add it as the final parameter to the SelectList constructor.
public static SelectList SelectListFor<T>(T selected) where T : struct
{
Type t = typeof(T);
return !t.IsEnum ? null
: new SelectList(BuildSelectListItems<T>(), "Value", "Text", selected.ToString());
}
For anyone that may be interested I fixed this using jquery.
I was getting hung up on drop downs filled by enums, razor and html helpers etc. I looked at the raw html produced and approached it from that angle.
The following code works for me but I'm no expert so there may well be better solutions
// set initial value of Status ddl
$(document).ready(function () {
var initialValue = $("##Html.IdFor(model => model.Status)").val();
$("select option").filter(function () {
return $(this).text() == initialValue;
}).prop("selected", true);
});
I hope this helps.

Telerik MVC Grid fails to bind after calling .ajaxRequest

I have something of a weird situation going on. I'm trying to build a Telerik MVC grid in a custom HTML helper which implements some other custom functionality. (Amongst other things, it renders a form to the right of the grid when a row is selected. We're not using the in-box editing features of the grid due to UI standardization. The whole requirement is that, for simple list-of-values tables in a database, we'd like a minimal-code approach. One line in the HTML, a few lines of Javascript at most, and boom, done.)
Everything works -- except rebinding the data dynamically. The grid renders, its selection works, the form displays, the form saves at blur events. The grid hits the OnDataBinding event, but nothing happens after that. It never gets to the OnDataBound event, and it never hits the internal bindTo nor bindData methods on the grid object itself.
"Enough" of the code (a lot of it can't be revealed) is thus (HTML helper):
public static void ListOfValuesEditorFor<TModel, TModelCollection>(this HtmlHelper<TModelCollection> htmlHelper, string gridName, string refreshAction, string refreshController, string loadItemUrl, IEnumerable<TModel> model) where TModel : class where TModelCollection : IEnumerable<TModel>
{
var factory = HtmlHelperExtension.Telerik<TModelCollection>(htmlHelper);
var grid = factory.Grid(model);
grid = grid.Name(gridName).Pageable(pager => pager.Enabled(false)).Selectable(select => select.Enabled(true)).Filterable(filter => filter.Enabled(false)).Scrollable().Sortable(sort => sort.Enabled(false));
grid = grid.DataBinding(binding => binding.Ajax().Select(refreshAction, refreshController));
grid = grid.ClientEvents(events =>
{
events.OnDataBound("Telerik.ListOfValues.OnDataBound");
events.OnDataBinding("Telerik.ListOfValues.OnDataBinding");
events.OnRowSelect("Telerik.ListOfValues.SelectRow");
});
var textControls = new List<string>();
int idColumn = -1;
grid = grid.Columns(columns =>
{
int cellCount = 0;
foreach (var prop in typeof(TModel).GetProperties())
{
// Populates columns, creates text entry controls in the list,
// handles some other proprietary work.
// SNIP
}
});
// Container for the form
var formDivBuilder = new TagBuilder("div");
// Build out the form
// SNIP
// Render to the response
var response = HttpContext.Current.Response;
response.Write("<input type=\"hidden\" id=\"loadItemUrl\" value=\"" + loadItemUrl + "\" />");
response.Write("<input type=\"hidden\" id=\"idColumnIndex\" value=\"" + idColumn.ToString() + "\" />");
grid.Render();
response.Write(formDivBuilder.ToString(TagRenderMode.Normal));
}
That HTML helper is called thusly:
<% using (Html.BeginForm()) {
Html.ListOfValuesEditorFor("JobTitleGrid", "RefreshJobTitles", "Home", "/Home/LoadJobTitle", Model);
} %>
On the Javascript side of the world, all OnDataBound and OnDataBinding do is display messages indicating that they've been hit. In fact, they won't even make it to the production version of the code; they're in there for debugging purposes now.
OnSelect displays and populates the form. This is happening correctly.
The form itself updates the object any time a text field's onChange event fires. This portion is validated as functional. This is done via a $.ajax() call, which again, is validated to function.
The success callback from that $.ajax() call is thus:
function onSubmitComplete(responseData, callbackData) {
// Some irrelevant junk here
$('#JobTitleGrid').data('tGrid').ajaxRequest();
}
The call to ajaxRequest functions. At the server, my grid action functions, returning an IList of the IJobTitle objects. At the client, OnDataBinding fires, displaying its message. OnDataBound never fires, and the grid display does not update.
I know this is somewhat outside the bounds of the way Telerik controls are normally used, but the sheer amount of code necessary to use them encourages my team to try to create reusable entities (such as these custom HTML helpers) wherever possible. For the simpler controls (text boxes, calendars, etc), our custom helpers have always "just worked." The grid, though, is presenting this problem.
Any ideas on why we never get to binding the data? More importantly, how to fix that?
After coming up with the solution, I'd briefly considered deleting the question -- Telerik's grid is only minimally involved here. However, I know first-hand how hard it is to troubleshoot code when you're building on top of frameworks, which are built on top of frameworks, which are further built on top of frameworks. :) So hopefully this answer will help the next guy down the line.
The actual issue turned out to be a serialization exception from the DAL call in the grid action. This seemed odd to me, since I used the exact same call to populate both the pre-loaded view in the Index action and the response from the GridAction, but sure enough, if I debugged down in the Javascript deeply enough, I eventually found it. The exception wasn't being handled (pro-tip: implement an OnError handler for the grid), and thus client-side rebinding failed, as it had no data to bind.
Once I resolved the serialization issue, everything just magically worked, and we were down to about 20 lines of code to implement an entire generic entity data entry screen.

DropDownListFor does not set selected value

I have a view that is displaying a Drop down list using the HTML helper DropDownListFor
<%: Html.DropDownListFor(Function(model) model.Manufacturer, New SelectList(Model.ManufacturerList, Model.Manufacturer))%>
My controller is passing the View a ViewModel containing the Manufacturer and the ManufacturerList
Function Search(ByVal itemSrch As ItemSearchViewModel) As ActionResult
'some code mapping and model code'
Return View(itemSrch)
End Function
My ViewModel is nothing crazy just fills the ManufacturerList with a list of string values and then the Manufacturer property is just a string value containing the selected value from the drop down list.
Public Property Manufacturer As String
Public Property ManufacturerList() As List(Of String)
I'm having an issue with the view setting the selected value on the drop down list if we are reloading the Search View. I've checked the View Model (ItemSearchViewModel) when it comes into the Search function and the Manufacturer is populated with the proper selected value and successfully passes that value back to the Search View. At some point the data passed to the view doesn't seem to populate the selected value, was wondering if anyone had some ideas on why this is happening and what I can do to fix it.
Thanks
Didn't get much for answers on this so started digging into how I could fix this somewhat easily. Also in my research this seemed to be a common problem for many people with the DropDownListFor HTML Helper. I figured there had to be a way to get this working since I knew the selected value property from my ViewModel was actually getting passed to the View. So javascript and jQuery to the rescue, I ended up trying to set the selected value using the jQuery .ready function and was able to successfully get this to work. So here's my jQuery fix:
$(document).ready(function() {
$("#Manufacturer").val("<%: Model.Manufacturer %>");
});
For sake of making this easy to follow I used the full .ready syntax, or you can just use $(function () { 'code' });
If you want to solve this without using jQuery you can just use basic javascript syntax as well, it'll look something like this:
document.getElementByID("Manufacturer").Items.FindByValue("<%: Model.Manufacturer %>").Selected = true;
If you using the plain javascript call make sure to call this when the page is done loading data to the dropdownlist.
In either case all this code is doing is setting the selected value on the drop down list on the client side from the passed in Manufacturer value from the Model.
If you have any other ways to solve this problem please let me know, but this is working for me and hopefully it'll help someone else with their problem.
Thank,
I've done a similar quick-fix in JQuery today to fix this behaviour too :
$(document).ready(function() {
$(".wrapper<%: Model.Language %> option[value=<%: Model.Language %>]").attr("selected","true");
});
Though I could share it with others if needed.
I'd still like to see a real patch to this problem so many persons seems to have in a different way, maybe with an extension method of the already existing DropDownListFor Html.helper method.

ASP.NET MVC radiobutton not working in the parent view to call partial view

I have two strongly Typed partial views (Developers list and Testers list) and the respective views are Developers.ascx and Testers.ascx
Now I want to load one partial view based on the radiobutton selected.
The below code is not working on radio button change.
Code Snippet:
$('input:radio[name=Type]').change(function() {
var url = '/Home/Developers';
if ($(this).val() === '2') {
url = '/Home/Testers';
}
$("#result").load(url);
});
I tried $("input[name=Type]").click(function()
But din't work.
I would appreciate if anyone can provide any clue why the partial view is not loading on change event. or some guidelines.
Thanks
Rita
You can try to add a line with
if ($(this).val() === '2') {
alert('hello');
just to make sure that your code actually runs at all.
You can also call a function in the onChange()-event of the html element, instead of triggering it via $('input:radio[name=Type]').change.
Do you need to compare with three equal signs? Are you certain that the value will be '2' and not 2?
As long as the document is fully loaded and, as Marwan states above, your url:s actually point to action methods that do anything, it should work.
btw: shouldn't this be tagged with jquery or something else than just mvc?

ASP.NET MVC Unbind Action Parameter

Is it possible to disable a certain action parameter from retaining its value across requests?
[HttpPost]
public ActionResult MyAction(string value1, string value2)
{
if(value1=="hi")
ModelState.AddModelError("value1", "Can't have hi");
//do stuff
if(ModelState.IsValid)
return RedirectToAction("Finish");
else
return View()
}
[HttpGet]
public ActionResult MyAction()
{
return View()
}
The view consists of a simple form with two input boxes (value1 and value2). Once submitted and validation fails, the view is returned. I want to always have the value of the textbox in the view to be empty.
The value for the textbox "value1" is retained if the the model is invalidated.
I tried to declare the textbox as <%= Html.TextBox("value1", null) %> but the value is still retained. I also tried to use [Bind(Exclude="value1")] but that dosen't work on a single variable.
Update 2:
I'm doing this for a textbox that is used for Captcha (custom solution) input. I want the textbox to be cleared any time the page is loaded, but I want validation to remain.
Try calling
ModelState["value1"].Value
= new ValueProviderResult(null, string.Empty, CultureInfo.InvariantCulture);
before you return the view from within your controller action.
What this does is keep all the errors associated with the key "value1", but replaces the value with an empty value.
What are you doing that's causing it to be retained? There isn't anything like ViewState in MVC that will persist a value over multiple requests unless you're writing code or using form fields to make it do so.
What does the view look like? Is this action method being called via GET or POST? What's the "do stuff" contained in your method?
Edit: You're still showing //do stuff in your example code. Does that stuff contain any references to ViewData? Your question is about binding, but I don't see any binding happening. Maybe this is beyond my understanding.
Edit 2: Glad Phil saw this one! The original question didn't mention the ModelState.

Resources