Validating dynamically created fields in ASP.NET MVC - asp.net

I have the following form in an ASP.NET MVC view:
<%= Html.ValidationSummary() %>
<% var fields = ViewData.Model; %>
<% using (Html.BeginForm("Dynamic", "Candidate")) { %>
<% foreach (var field in fields) { %>
<label for="<%= field.FieldName %>"><%= field.FieldName %></label>
<%= Html.TextBox(field.FieldName, field.Value, new { #class = "short" }) %>
<% } %>
<a class="button green" onclick="$('form').submit(); return false;">Submit</a>
<% } %>
I have a single controller action that loads this form as well as accepts the post, it looks like this:
public ActionResult Dynamic() {
var fields = DataProvider.Candidates.GetAllDynamicFields();
if (Request.HttpMethod == "POST") {
fields.ForEach(f => f.Value = Request[f.FieldName]);
var validation = DataProvider.Candidates.SaveDynamicFields(fields);
if (validation.IsValid)
return RedirectToAction("Index");
ViewData.ModelState.AddErrorsFromValidationResult(validation);
}
return View(fields);
}
My problem is that if any of the validators fail (i.e. the validation object contains errors) then I get an error on view rendering because ViewData.ModelState doesn't contain any keys. Where am I going wrong here? Any clues?

Figured it out. ViewData.ModelState is populated by the params in the response object. So with a dynamically created form you don't know exactly what was passed in the post. So I just recreate my ModelState on the fly:
fields.ForEach(f => ViewData.ModelState.Add(f.FieldName ...
And then we're all good...when the validation is run on the view it can find all the keys in the ModelState and no exceptions...works like a charm.

Asp.Net C# MVC Dynamic Forms (Changing Dom structure and getting data on the server)

Related

asp.net mvc2 dropdownlistfor

I want to create a dropdownlist in my asp.net MVC2 view and I am following code:
foreach (var whiteout in Model)
{
%>
<tr>
<td>
<%= whiteout.Field.NiceName%>
<% Html.DropDownListFor("anyname", Model); %>
<%
}
}
%>
but I am getting error that second parameter is not correct. Second parameter is a list. Here is how Model is declared at the top of partial view:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<EnviroTracker.Entities.Whiteout>>" %>
Please suggest how to fix this?
A DropDownListFor helper takes a SelectList as second argument and a lambda expression to a simple property as first:
<%= Html.DropDownListFor(
x => x.SomeProperty,
new SelectList(Model.SomeList, "ValueProperty", "TextProperty")
) %>
If you want to use the weakly typed DropDownList helper you could manually specify the name of the property it will be bound to but the second argument should still be a SelectList:
<%= Html.DropDownList(
"SomeProperty",
new SelectList(Model.SomeList, "ValueProperty", "TextProperty")
) %>

ActionLink routeValue from a TextBox

I'm working on the following:
1- The user enters a value inside a textBox.
2- then clicks edit to go to the edit view.
This is my code:
<%= Html.TextBox("Name") %>
<%: Html.ActionLink("Edit", "Edit")%>
The problem is I can't figure out how to take the value from the textBox and pass it to the ActionLink, can you help me?
You can't unless you use javascript. A better way to achieve this would be to use a form instead of an ActionLink:
<% using (Html.BeginForm("Edit", "SomeController")) { %>
<%= Html.TextBox("Name") %>
<input type="submit" value="Edit" />
<% } %>
which will automatically send the value entered by the user in the textbox to the controller action:
[HttpPost]
public ActionResult Edit(string name)
{
...
}
And if you wanted to use an ActionLink here's how you could setup a javascript function which will send the value:
<%= Html.TextBox("Name") %>
<%= Html.ActionLink("Edit", "Edit", null, new { id = "edit" })%>
and then:
$(function() {
$('#edit').click(function() {
var name = $('#Name').val();
this.href = this.href + '?name=' + encodeURIComponent(name);
});
});

ajax in asp.net mvc 2

I am using the microsoft ajax and the ajax form looks like this
<% using (Ajax.BeginForm("UserListing", new AjaxOptions
{
UpdateTargetId = "results",
OnComplete = "getData"
}))
{%>
<input id="Submit1" type="submit" value="submit" />
<%} %>
Now, i get the getData() js function called upon completion of the ajax request. all i want is that i need to access the response data of this ajax request inside this function and prevent that data being directed to the div with id results.
Thats because i am returning json result from the controller's action method and i need to parse it and display in the div.
script function is :
<script type="text/javascript">
function getData() {
alert("Response is : ");
}
</script>
the div tag is :
<div id="results">
</div>
I do not want to use other than microsoft ajax. kindly suggest me accordingly.
You could try rendering the response of the ajax request in a partial view containing only hidden fields. Then in your js function you can access the hidden fields using jQuery selectors.
So your action would look something like
[HttpPost]
public ActionResult UserListing()
{
List<string> data = GetUserListing();
return PartialView(data);
}
Your partial view will then only contain hidden fields that you render something like:
<% for (int i = 0; i < Model.Count(); i++)
{ %>
<input id="<%: "User" + i.ToString() %>" type="hidden" value="<%: Model[i] %>" />
<% } %>
That will render as:
<input id="User0" type="hidden" value="PeterSmith" />
Then in your javaScript function you can access each of the fields by doing something like:
function getData() {
var user = $('#User0').val();
alert(user);
}
That will show you the first field rendered. But you can enhance it a bit by looping through all the injected input fields.
Corrected this problem by myself as explained above. need some json parsers, currently looking for those..

Why can't I use an iteration variable in a LoginView?

I am building a .NET MVC app that has a page with a list of delete buttons, one for each item in a list. The problem I'm having is that the foreach variable "item" is not visible inside the LoginView, which results in the following error:
Compiler Error Message: CS0103: The name 'item' does not exist in the current context
Below is a simplified version of the view. The error occurs at the "new {id=item.Id}" in the LoggedInTemplate - the reference to "item" in the ActionLink works fine:
<% foreach (var item in Model) { %>
<%= Html.ActionLink("Item", "Details", new { id = item.Id })%>
<asp:LoginView runat="server">
<LoggedInTemplate>
<% using( Html.BeginForm( "Delete", "Items", new {id=item.Id}, FormMethod.Post))
{ %>
<input type="submit" value="Delete" runat="server" />
<% } %>
</LoggedInTemplate>
</asp:LoginView>
<% } %>
To clarify the problem is not that the Model has not been successfully passed to the View. The Model is visible from both inside and outside the LoginView. The foreach loop as no problem in iterating through the items in the Model (which is a List). The problem is that the iteration variable "item" is not accessible from within the LoginView - though the original Model is.
Is there any way to pass "item" through to the LoginView's templates? Or is building LoginViews within a foreach loops the wrong way of doing things?
Is there a scoping rule that prevents using local variables within controls - perhaps because the control is rendered at a different time to the main page?
With ASP.NET MVC you really shouldn't use user/custom controls, so if you omit the <asp:LoginView/> and write a line of code to check if the user is authenticated, you are good to go.
Instead of your current code:
<asp:LoginView runat="server">
<LoggedInTemplate>
<div>Show this to authenticated users only</div>
</LoggedInTemplate>
</asp:LoginView>
Just use an if-statement and the value of Request.IsAuthenticated:
<% if (Request.IsAuthenticated) { %>
<div>Show this to authenticated users only</div>
<% } %>
Are you passing the Model to the view and are you also inheriting from the model within that view?
So if this is a View then in your C# code you need to return the list of items like return View(listofitems);
If this is a partial view then <% Html.RenderPartial("MyPartial", listofitems) %>
And in the view you need to
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IQueryable<ListOfItems>>" %>
If all that is in place then it should work no probs.
<% foreach (var item in Model) { %>
<%= Html.ActionLink("Item", "Details", new { id = item.Id })%>
<%= if( Request.IsAuthenticated ) {
using( Html.BeginForm( "Delete", "Items", new {id=item.Id}, FormMethod.Post))
{ %>
<input type="submit" value="Delete" runat="server" />
}
} %>
<% } %>
There is no need to use the LoginView, its not really giving you anything. Use something like the above instead.
Alternatively, you can move the decision of whether to show the delete option for the specific item into the controller, so instead of doing if( Request.IsAuthenticated ) you would do if( item.ShowDelete ) ... assuming item's type is a view model. Another option is to use an extension method for the same, item.ShowDelete(). I prefer the earlier, because there might be logic associated to deciding whether to show delete for a given item, so its better to not have it in the controller or a related logic.

How to return Nested PartialViews (including their javascript) from an AJAX call in ASP.Net MVC

I have created a treeview of Categories using nested partial views:
my Index page (that displays the treeview):
<div>
Category Menu:
<input type="button" value="1" name='selectCat_btn' />
<input type="button" value="2" name='selectCat_btn' />
</div>
<!-- Treeview -->
<% Html.RenderPartial("ItemCats_UL", Model); %>
<div id="CatSelectorOutput">
</div>
ItemCats_UL:
<div>
<ul id="catsTree">
<% Html.RenderPartial("ItemCats_LI", Model); %>
</ul>
</div>
<script type="text/javascript" >
$(document).ready(function() {
$("#catsTree").treeview();
</script>
ItemCats_LI:
<%foreach (ItemCategory itemCat in Model)
{ %>
<li>
<%= itemCat.Name %>
<%if (itemCat.Children != null && itemCat.Children.Count() > 0)
{ %>
<ul>
<% Html.RenderPartial("ItemCats_LI", itemCat.Children); %>
</ul>
<%} %>
</li>
<%} %>
Now this treeview works perfectly when I return the basic View("Index", Model) from my controllers Index action on page load.
The trouble comes when I want to change the Categories Model displayed in my Treeview (the nested partialViews) from an AJAX call...
For example: I click one the 'Cats2' button and the page should display Categories with ParentID of 2 in the Treeview. I attempted this by returning a JsonResult of the html of the ItemCats_UL PartialView (using a RenderPartialToString method found here) from my Controller Action. As some of you might know Javascript won't run in your partial view when you use an AJAX form to return a PartialViewResult, and I need Javascript in my Treeview which is why I'm using the RenderPartialToString.
The category select button click handler:
<script type="text/javascript">
$("[name='selectCat_btn']").click(function() {
var CID = $(this).attr('value');
$.ajax({
type: "POST",
url: "SelectCat",
dataType: "json",
data: { "CID": CID },
success: function(result) { $("#CatSelectorOutput").html(result.output); }
});
return false;
});
</script>
My Controller Action:
[AcceptVerbs(HttpVerbs.Post)]
[UrlRoute(Name = "SelectCat", Path = "selectCat")]
public ActionResult SelectCat(int CID)
{
IQueryable<ItemCategory> cats;
cats = ItemRepo.GetItemCats().WithCID(CID);
JsonResult result = null;
result = new JsonResult
{
Data = new
{
success = true,
output =
Helpers.RenderHelper
.RenderPartialToString("~/Views/Admin/AdminItemCatsUL.ascx",
cats)
}
};
return result;
}
The result:
The ItemCats_UL partialView displays! BUT the nested PartialViews (ItemCats_LI) don't!
Error I receive when I step through the markup in the ItemCats_UL.ascx and hover over the 'Html' part of the following code:
<ul id="catsTree">
<% Html.RenderPartial("ItemCats_LI", Model); %>
</ul>
Value cannot be null.
Parameter name: viewContext
Html = 'Html' threw an exception of type 'System.ArgumentNullException'
I'm wondering if there's a clever guy out there who can extend the RenderPartialToString method to include nested partialviews? Or am I missing something simple?
You need to hook the newly returned HTML / JavaScript back into the DOM upon loading it.
I'm sure there are lots of ways to do this, but I found a nice jQuery add-on called LiveQuery (link)
that helps me do it.
To make it work in your case, you'd set up a jQuery document.ready function in the parent page that looks something like this:
$("#catsTree").livequery(function () { this.treeview(); }, function () { /* code to destroy the treeview here */ });

Resources