ASP.NET MVC - Ajax.BeginForm vs Ajax.ActionLink - asp.net-mvc

I've noticed when I use Ajax.BeginForm (with a submit button) that the Model is passed to the Controller but when I use an Ajax.ActionLink it is not passed - or at least I have not discovered how to tap into it.
First question: How do you determine which is the better route to take?
Now, for a little deeper dive into one sample scenario: I have a model that has a couple dozen simple data type properties and a few List properties. The Create/Edit View is rendered with a Html.BeginForm(). The submit button returns the entire view model and I can then go through it and save all the data to the DB via the DB Model. Like I said I have a few List pieces as well. For example, I have a List of credit cards accepted which I render as a series of check boxes, a List of Services Provided also rendered as a list of check boxes. All of these are easy to deal with on the Form Post. However, I have one List that is basically a free-form text entry and I would like to have the textbox with an Add button followed by all of the List items, each with a delete button.
My Create/Edit view looks something like:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<RainWorx.FrameWorx.MVC.ViewModels.DirectoryEdit>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
<div class="Column12">
<div class="Shadow"></div>
<h2 class="h2row"><%= Model.PageTitle%></h2>
</div>
<% using (Html.BeginForm())
{%>
<%: Html.ValidationSummary(true)%>
<fieldset>
<%--<legend>Fields</legend>--%>
<div class="editor-label">
<%: Html.LabelFor(model => model.Name)%>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Name, new { style = "width:20em;" })%>
<%: Html.ValidationMessageFor(model => model.Name)%>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.Address1)%>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Address1, new { style = "width:20em;" })%>
<%: Html.ValidationMessageFor(model => model.Address1)%>
</div>
...
<div class="editor-label">
<%: Html.LabelFor(model => model.LookupAccepts)%>
</div>
<div class="editor-field">
<hr />
<% foreach (var a in Model.MyAccepts)
{
if (a.Checked)
{ %>
<input type="checkbox" name="AcceptIDs" checked="checked" value="<%: a.ID %>" /> <%: a.Name%><br />
<% }
else
{ %>
<input type="checkbox" name="AcceptIDs" value="<%: a.ID %>" /> <%: a.Name%><br />
<% }
} %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.LookupServices)%>
</div>
<div class="editor-field">
<hr />
<% foreach (var a in Model.MyServices)
{
if (a.Checked)
{ %>
<input type="checkbox" name="ServiceIDs" checked="checked" value="<%: a.ID %>" /> <%: a.Name%><br />
<% }
else
{ %>
<input type="checkbox" name="ServiceIDs" value="<%: a.ID %>" /> <%: a.Name%><br />
<% }
} %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.MyLicenses)%>
</div>
<div class="editor-field">
<hr />
<% Html.RenderPartial("EditLicense", model: Model); %>
</div>
<div class="editor-label">
</div>
<div class="editor-field">
<input type="submit" value="Save" />
</div>
</fieldset>
<% } %>
</div>
<div>
<%: Html.ActionLink("Back to List", "Index")%>
</div>
</asp:Content>
The Partial View looks like:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%# Import namespace="RainWorx.FrameWorx.MVC" %>
<% foreach (var license in Model.MyLicenses) { %>
<% } %>
My thought train for setting it up this way is pretty straight forward (I think it is anyways). I only have something to Ajax on the License section. I want to add one string after another without losing all of the information for the main model. And after the add is done I want to update the partial view with the updated List for it.
There is probably a better way to do this than what I laid out and if so, lay it on me. The main part I am trying to figure out right now (and quickly) is whether Ajax.ActionLink (and my Extension methods for it) are the right direction to go.

An Ajax.BeginForm simply ajaxifies the given form and when you submit it the values of all input fields that it contains are sent to the server. The only difference with a normal form is that the they are sent using AJAX.
On the other hand Ajax.ActionLink generates a simple anchor tag which performs an AJAX request to the given url. It won't send any additional values to the server unless you specify it.
How do you determine which is the better route to take?
Personally I don't use any of those. I use standard Html.BeginForm and Html.ActionLink and write the code manually to AJAXify them unobtrusively with jQuery (if I need to use Ajax of course).
For your scenario of implementing dynamic list editing you may take a look at the following blog post.

Related

Html.BeginForm() does not submit if nothing is selected in a Html.Dropdownlist() helper with optional label

#using (Html.BeginForm("Boxing", "Company",FormMethod.Post))
{
<div class="box">
<div>
<div class="left">
<div class="topLabel">
Id No:</div>
<div class="input_text_65">
#Html.TextBoxFor(m => m.Id)
</div>
<div class="botLabel">
percentt:
</div>
<div>
<input type="text" style="width: 50px" name="percent" />%
</div>
</div>
<div class="lastDetailField">
<div class="topLabel">
D/C:
</div>
<div class="select_110">
#Html.DropDownListFor(m => m.cType, new SelectList((IEnumerable<Model.cType>)ViewData[ViewDataKeys.cTypes]), "----")
</div>
<div class="margin_top_45">
<input id="submit" type="submit" value="submit" />
</div>
</div>
</div>
</div>
}
If I didn't select any option in the dropdownlist (leaving the optionlabel "----" selected) and I press the submit button, the form will not be posted and the focus will be move to the dropdownlist
if I remove the optional Label, like this:
#Html.DropDownListFor(m => m.cType, new SelectList((IEnumerable)ViewData[ViewDataKeys.cTypes]))
then it will work just fine. I'm thinking if I will put an optional label, does that mean I am required to choose an item? I want the form to be submitted even if I leave the dropdownlist with the optionallabel selected.
thanks in advance!
Do you have a required attribute in your model/viewmoden on cType? I think the clientside validation is kicking in, but because you didn't set a validation helper, you're not seeing the message. The form won't submit though if you select the optional label.

Problem with error about model being passed into partial view

So I am trying to render a partial view into my page and I am getting the below error:
ERROR
The model item passed into the dictionary is of type
'System.Collections.Generic.List`1[GettingOrganized.Models.Todo]', but this
dictionary requires a model item of type 'GettingOrganized.Models.Todo'.
I don't see what is wrong with the partial view or controller.
PARTIAL VIEW
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<GettingOrganized.Models.Todo>" %>
<% using (Html.BeginForm("Create", "Todo", FormMethod.Post, new {id="CreateTodo"})) {%>
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
<%= Html.LabelFor(model => model.Title) %>
</div>
<div class="editor-field">
<%= Html.TextBoxFor(model => model.Title) %>
<%= Html.ValidationMessageFor(model => model.Title) %>
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<% } %>
Controller Index View that Renders the Partial View:
<% Html.RenderPartial("CreateElements"); %>
Any thoughts? I would like to keep it as close to this setup since is strong typed.
UPDATE
So to provide a few more details, now that problem is becoming more clear. I am rendering the view on the page in a div and hiding it the user clicks a certain link. Then I want to show the div. This same partial is used in a "Create" view where you can create a "Todo". But I am now wanting to use the partial in the Index view which shows a list of the model "Todo".
The model passed in, in the "Index" view:
Inherits="System.Web.Mvc.ViewPage<IEnumerable<GettingOrganized.Models.Todo>>" %>
So if I don't want to loop through a foreach loop, and just want to show one instance of the model, who do I do that?
Also I can use the following view for the partial and it will work which takes away the strongly typed to the model:
WORKING PARTIAL
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<% using (Html.BeginForm("Create", "Todo",
FormMethod.Post, new { id="CreateTodo"})) {%>
<fieldset>
<legend>Fields</legend>
<p>
<label for="Title">Title:</label>
<%=Html.TextBox("Title")%>
<%=Html.ValidationMessage("Title", "*")%>
</p>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<p>
<input type="submit" value="Create" />
</p>
<% } %>
Possible Answer
However, I believe I might have found an answer.
<% Html.RenderPartial("CreateElements", new Todo()); %>
Is this a proper way to handle this?
However, I believe I might have found an answer.
<% Html.RenderPartial("CreateElements", new Todo()); %>
It looks like you need to pass the model to the partial view - as in:
<% Html.RenderPartial("CreateElements", myModel); %>
I would look into how you're passing in your model into the RenderPartial:
<% Html.RenderPartial("CreateElements", model); %>
And make sure that model is of type GettingOrganized.Models.Todo.
Since you're not passing a model into your RenderPartial call, MVC is effectively trying to create one for you using the ViewDataDictionary and model from the parent page.
It looks like the parent page has a model type which is a List of ToDo items, so I guess you can just call your RenderPartial method inside of a loop; something along the lines of:
<% foreach (GettingOrganized.Models.Todo todoItem in Model) {
Html.RenderPartial("CreateElements", todoItem);
} %>

MVC 3 with Forms and Lists: default model binder and EditorFor

The Model:
public class MyObject
{
public IList<Entry> Entries;
}
public class Entry
{
public string Name { get; set; }
}
If I use the default EditorFor(model => model.Entries) the name/id values are:
<input type="text" value="" name="Entries[0].Name" id="Entries_0__Name">
If instead I want to create an EditorFor template like this:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<List<CMS.Models.MyObject>>" %>
<div class="list">
<%
for (int i = 0; i < Model.Count; i++)
{ %>
<div class="object" id="<%: i %>">
<%: Html.EditorFor(model => Model[i]) %>
<%: Html.ValidationMessageFor(model => Model[i]) %>
</div>
<% } %>
</div>
The emitted name/id values are:
<input type="text" value="" name="Entries.[0].Name" id="Entries__0__Name">
So the name has a period between the property name and [0] and the id has an extra underscore to the left of the index number. This doesn't work with the default model binder. Is there a way to do this that does work with the default model binder?
I think you can do most of this using templates without having to explicitly iterate over the list, which seems to be causing your problems with the model binder.
Try making these shared editor templates:
MyObject Editor Template
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<CMS.Models.MyObject>" %>
<div class="list">
<%: Html.EditorFor(m => m.Entries) %>
</div>
Entry Editor Template
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<CMS.Models.Entry>" %>
<div class="object" id="<%: ??? %>">
<%: Html.EditorFor(m => m.Name) %>
<%: Html.ValidationMessageFor(m => m.Name) %>
</div>
The only thing I haven't figured out yet is how to get the correct value in the id attribute of the div, hence the question marks. Final caveat is that I'm at home so I haven't actually tried this.

Looking for example of using same partial view multiple times on a webpage

Using .NET MVC, I have a partial view of an address that I need to implement multiple times on the same page because the user might have different types of addresses. Being new to .NET MVC, I would like to find an example that would guide me through all the steps necessary.
Here is some of the code. This first part of the code is the main page that includes the partial view. I knew it was the wrong thing to do, but I first tried just listing the "Html.RenderPartial" line twice (for 2 different addresses). I did that just to see what it would do.
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<JCIMS_MVC2_EF.DomainModel.Data.Models.DistrictAddressModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Create District
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% Html.RenderPartial("DistrictHeaderPartial", this.ViewData.Model.DistrictHeader); %>
<% Html.EnableClientValidation(); %>
<% using (Html.BeginForm()) { %>
<fieldset id="address">
<legend>Create new Address for <%: Model.DistrictHeader.District_Name %></legend>
<% Html.RenderPartial("AddressEditPartial", this.Model); %>
</fieldset>
<p>
<input type="submit" value="Save" />
</p>
<% } %>
<div>
<%: Html.ActionLink("Return to District Info Page", "Index", "District", new { id = Model.DistrictHeader.District_GUID }, null)%>
</div>
</asp:Content>
This is the partial view:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<JCIMS_MVC2_EF.DomainModel.Data.Models.BaseAddressModel>" %>
<script language="javascript" type="text/javascript">
$(document).ready(function () {
$("#AddressViewData_Zip_Code").mask("99999");
});
</script>
<div class="editor-label">
<%: Html.LabelFor(model => model.AddressViewData.Address_Type_Text) %>
</div>
<%: Html.DropDownList("AddressViewData.Address_Type_Code")%>
<%: Html.ValidationMessageFor(model => model.AddressViewData.Address_Type_Code) %>
<div class="editor-label">
<%: Html.LabelFor(model => model.AddressViewData.Street_Address)%>
</div>
<%: Html.TextBoxFor(model => model.AddressViewData.Street_Address, new { #class = "input-size" })%>
<%: Html.ValidationMessageFor(model => model.AddressViewData.Street_Address) %>
<div class="editor-label">
<%: Html.Label("City") %>
</div>
<%: Html.TextBoxFor(model => model.AddressViewData.City, new { #class = "input-size" })%>
<div class="editor-label">
<%: Html.Label("State") %>
</div>
<%: Html.DropDownList("AddressViewData.State")%>
<div class="editor-label">
<%: Html.LabelFor(model => model.AddressViewData.Zip_Code) %>
</div>
<%: Html.TextBoxFor(model => model.AddressViewData.Zip_Code) %>
<%: Html.ValidationMessageFor(model => model.AddressViewData.Zip_Code) %>
<div class="editor-label">
<%: Html.LabelFor(model => model.AddressViewData.Address_ATTN) %>
</div>
<%: Html.TextBoxFor(model => model.AddressViewData.Address_ATTN, new { #class = "input-size" })%>
<%: Html.ValidationMessageFor(model => model.AddressViewData.Address_ATTN) %>
Whats wrong with:
<% Html.RenderPartial("Name", model ) %>
<% Html.RenderPartial("Name", differentModel ) %>
I'm not completely sure what you need. But if I understand you correctly you have a property of type List<Address> in your model. If the problem seems to be getting this kind of data from the client to the server, then read this blog post that will help you out.
If the problem is with displaying different addresses in a parent view it's just a simple foreach statement:
<% foreach(Address a in this.Model.Addresses) %>
<% { %>
<% Html.RenderPartial("Address", a); %>
<% } %>
I may be unclear as to what you're looking for but basically you're situation is that you have a list of users each of which has a list of addresses. So you'll iterate through your list of users and for each user their list of addresses like so (this code snippet is an example of c sharp embedded in an aspx page:
<% foreach(User lvUser in Model.Users)
{ %>
<% Html.Encode(lvUser.Username) %>
<% Html.Encode(lvUser.Email) %>
<% foreach(Address lvAddress in lvUser.Addresses)
{ %>
<% Html.RenderPartial("PartialAddress", lvAddress); %> //Where you've set the view data class to Address
<% } %>
<% } %>

MVC2 application with Ckeditor "potentially dangerous Request.Form

I'm getting the "Potentially dangerous Request.Form request value was detected from the client" exception when im using my FCK editor.
How could encode before submit the form, or disable this validation without disable the Data Anotations validation?
This is the code of my view:
<% using (Html.BeginForm()) {%>
<%: Html.ValidationSummary("Please complete in a right way the fields below.") %>
<fieldset>
<legend>Fields</legend>
<div class="editor-field">
<%: Html.LabelFor(e => e.Name)%>
<%: Html.TextBoxFor(e => e.Name)%>
<%: Html.ValidationMessageFor(e => e.Name)%>
</div>
<div class="editor-field">
<%: Html.LabelFor(e => e.Teaser) %>
<%: Html.TextAreaFor(e => e.Teaser)%>
<%: Html.ValidationMessageFor(e => e.Teaser)%>
</div>
<div class="editor-field">
<%: Html.LabelFor(e => e.Description) %>
<%: Html.TextAreaFor(e => e.Description)%>
<%: Html.ValidationMessageFor(e => e.Description)%>
</div>
<p>
<input type="submit" />
</p>
</fieldset>
<% } %>
<script type="text/javascript">
//<![CDATA[
// This call can be placed at any point after the
// <textarea>, or inside a <head><script> in a
// window.onload event handler.
// Replace the <textarea id="xxxxxx"> with an CKEditor
// instance, using default configurations.
CKEDITOR.replace("Description");
//]]>
</script>
Thanks a lot in advance.
<httpRuntime requestValidationMode="2.0" />
Check: Request Validation - ASP.NET MVC 2
[script type="text/javascript" src="/ckeditor/_source/core/editor.js"][/script]
CKEDITOR.config.htmlEncodeOutput = true;
If you are working with the FCK editor or CKeditor you do not need to deal with the "requestValidationMode". As it will be applied on the entire application.
You can just do the followings:
CKEDITOR.replace('Description', { toolbar: '1', htmlEncodeOutput: true});
Then in the controller:
model.Body = System.Net.WebUtility.HtmlDecode(model.Body);
config.htmlEncodeOutput
But it might be easier to use a .Net wrapper for CKEditor.

Resources