Partial View models - asp.net-mvc

Within my layout page, I want to either show a Login box, OR the details about the logged in person.
<div>
#if (Request.IsAuthenticated)
{
#Html.ViewBag.UserDisplay
#Html.ActionLink("[Logout]", "LogoutUser", "User")
}
else
{
#Html.Partial("_Login")
}
<hr />
#RenderBody()
</div>
My login partial view:
#model BasicFinanceUI.Models.LoginModel
#using (Html.BeginForm("LoginUser", "User"))
{
#Html.ValidationSummary()
<p>
Username: #Html.TextBoxFor(x => x.Username)
Password: #Html.TextBoxFor(x => x.Password)
Remember Me: #Html.DropDownListFor(x => x.RememberMe, new[]
{
new SelectListItem() { Text = "Yes", Value = "true"},
new SelectListItem() {Text = "No", Value = "false"}
}, "Select")
<input type="submit" value="Login" />
#Html.ActionLink("[Register]", "Register", "User")
</p>
}
When I load the screen, and login, it works fine.
However, when I click 'Register', it loads the register screen. The login box is still visible in the Layout.
My registration screen is displayed:
#model BasicFinanceUI.Models.RegisterationModel
#{
ViewBag.Title = "Register";
Layout = "~/Views/Shared/SiteLayout.cshtml";
}
<h2>Register</h2>
#using (Html.BeginForm("Register", "User"))
{
<p>#Html.ValidationSummary()</p>
<p>Username: #Html.TextBoxFor(x => x.Username)</p>
<p>Password: #Html.PasswordFor(x => x.Password1)</p>
<p>Retype Password: #Html.PasswordFor(x => x.Password2)</p>
<p>Firstname: #Html.TextBoxFor(x => x.Firstname)</p>
<p>Surname: #Html.TextBoxFor(x => x.Surname)</p>
<p>Email: #Html.TextBoxFor(x => x.Email)</p>
<p><input type="submit" value="Register"/></p>
}
When I click the Register button, things go wrong.
I get the error:
The model item passed into the dictionary is of type
'BasicFinanceUI.Models.RegisterationModel', but this dictionary
requires a model item of type 'BasicFinanceUI.Models.LoginModel'.
It seems because there are two forms on the screen, they're getting mixed up. What am I doing wrong?

Related

How to validate a duplicate post request from a form in MVC

I am having an form which insert a record whenever a post request is made,but the problem is that if someone clicks submit button more than 1 times than duplicate post request are made and at the end same records are getting inserted. I dont want to check that the record is already present or not because the record would be different always. I tried using ValidateAntiForgeryToken filter in controller but it is failing to validate the requests, Below is my View Code.
#using (Html.BeginForm("Create", "Home",FormMethod.Post,new { onkeydown = "return event.keyCode!=13" }))
{
#Html.AntiForgeryToken()
<div class="right-col">
#Html.TextBoxFor(model => model.Name, new { placeholder = "Name", #class = "small-box" })
</div>
<div class="left-col">Email Id :</div>
<div class="right-col">
#Html.TextBoxFor(model => model.EmailId, new { placeholder = "Email Id", #class = "small-box",id="resumeemailid" })
#Html.ValidationMessageFor(model => model.EmailId)
</div>
<div class="left-col">Address :</div>
<div class="right-col">
#Html.TextAreaFor(model => model.Address, new { placeholder = "Address", #class = "small-box" })
</div>
<div class="buttons resume-threebutton">
<input type="submit" id="register-button" class="gradient-btn" value="#T("Account.Passport.Register.Button.Upload", "Upload")" name="Command" />
<input type="submit" id="register-button" class="gradient-btn" value="#T("Account.Passport.Button.UploadandAdd", "Upload And Add New")" name="Command" />
<input type="button" id="register-button" class="gradient-btn" value="#T("Account.Passport.Register.Button.Cancel", "cancel")" name="register-button" onclick="location.href='#Url.Action("SelectTemplate", "CustomerTemplate")'" />
</div>
}
and below is my controller Post method
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ProductModel model)
{
//To do add the product here...
}

Send single value through RouteValueDictionary

One of the properties of my ViewModel is an array which, unfortunately, is null every time I post back to the controller. I figured a simple hack where I place the values into a coma-delimited string.
This works great for our paging plugin, which posts back to our Index method, using a RouteValueDictionary. However, it is not working in the Html.BeginForm helper which posts back to a different controller action (the Update method).
View
#*Since we can't send arrays or complex objects break array down into string for RouteValueDictionary*#
var channelCodes = "";
for (int i = 0; i < Model.searchChannelCode.Length; i++)
{
channelCodes += Model.searchChannelCode[i];
if (i + 1 < Model.searchChannelCode.Length)
{
channelCodes += ",";
}
}
#*The 'searchChannelCodesPagin' variable from this RouteValueDictionary always posts back as null
using (Html.BeginForm("Update", "ZipCodeTerritory", new RouteValueDictionary()
{
{"searchChannelCodesPaging", channelCodes }
}, FormMethod.Post, new {id = "UpdateForm"}))
{
#Html.HiddenFor(model => model.searchZip)
#Html.HiddenFor(model => model.searchTerritory)
#Html.HiddenFor(model => model.searchState)
#Html.HiddenFor(model => model.searchActiveOnly)
#Html.HiddenFor(model => model.zipCodeTerritory)
<div id="cloneBox">
<div id="rw1">
#Html.LabelFor(model => model.newTerritory)
#Html.TextBoxFor(model => model.newTerritory, new { style = "width: 30px;padding-left:10px;", maxLength = 3 })
#Html.LabelFor(model => model.newDescription)
#Html.TextBoxFor(model => model.newDescription, new { style = "width: 250px;padding-left:10px;", maxLength = 30 })
#Html.LabelFor(model => model.newEffectiveDate)
#Html.TextBoxFor(model => model.newEffectiveDate, new { style = "width: 80px;padding-left:10px;" })
<div id="rw2" style="padding-top: 10px;">
#Html.LabelFor(model => model.newChannelCode)
#Html.DropDownListFor(model => model.newChannelCode, Model.ChannelCodes, " ")
#Html.LabelFor(model => model.newStateCode)
#Html.DropDownListFor(model => model.newStateCode, Model.StateCodes, " ")
#Html.LabelFor(model => model.newEndDate)
#Html.TextBoxFor(model => model.newEndDate, new { style = "width: 80px;" })
</div>
</div>
</div>
<br/>
<div id="buttonDiv">
<button type="submit" id="CloneButton" name="button" value="clone">Apply New Data</button>
<button type="submit" id="deleteButton" name="button" value="delete">Delete Selected Items</button>
<button type="submit" id="removeButton" name="button" value="removeErrors">Remove Selected Errors</button>
</div>
}
Controller
The forma above posts to this controller action. The searchChannelCodePaging variable is null each time.
[HttpPost]
public ActionResult Update(ZipCodeIndex updateZip, string button, string searchChannelCodesPaging)
{
Since you are doing a post, the simplest way to get it to the backend would be add a hidden field:
#Html.HiddenFor("searchChannelCodesPaging", searchChannelCodesPaging);
As a routing value, you may need to get it explicitly within the control via one of the two following approaches. These objects are directly accessible within the Controller class.
RouteData.Values("searchChannelCodesPaging")
Request.QueryString.Get("searchChannelCodesPaging");
You don't have to serialize a array type model parameter to a CSV string to get it to post to your controller. You can do this instead:
#for (var i = 0; i < Model.searchChannelCode.Length; i++)
{
#Html.HiddenFor(m => m.searchChannelCode[i]);
}

MVC4 - Login box within Layout page using Partial

I have a layout page, and want it to have a Login Box, OR display the logged in user, along with a Logout link.
At the moment, my SiteLayout.cshtml file has this:
<body>
<h1>Basic Finance</h1>
<div>
#if (Request.IsAuthenticated)
{
#Html.ViewBag.UserDisplay
#Html.ActionLink("[Logout]", "LogoutUser", "User")
}
else
{
#Html.Partial("_Login")
}
<hr />
#RenderBody()
</div>
</body>
Then, i my Views/Shared/, I have the _Login.cshtml file:
#model BasicFinanceUI.Models.LoginModel
#using (Html.BeginForm())
{
#Html.ValidationSummary()
<p>
Username: #Html.TextBoxFor(x => x.Username)
Password: #Html.TextBoxFor(x => x.Password)
Remember Me: #Html.DropDownListFor(x => x.RememberMe, new[]
{
new SelectListItem() { Text = "Yes", Value = "true"},
new SelectListItem() {Text = "No", Value = "false"}
}, "Select")
<input type="submit" value="Login" />
</p>
}
This code was moved from a standard view. I had it then there was simply a 'Login' link, instead of the login box in the Layout page. I want to display a login box instead.
My login code is still in a controller I created called 'UserController'. But, I have no idea how to tell my login box to use that controller. Is this the right way to do what I want (or should there be a controller for my login partial). Is it OK to use my 'UserController'? And if so, how?
Change:
#using (Html.BeginForm())
{
#Html.ValidationSummary()
<p>
Username: #Html.TextBoxFor(x => x.Username)
Password: #Html.TextBoxFor(x => x.Password)
Remember Me: #Html.DropDownListFor(x => x.RememberMe, new[]
{
new SelectListItem() { Text = "Yes", Value = "true"},
new SelectListItem() {Text = "No", Value = "false"}
}, "Select")
<input type="submit" value="Login" />
</p>
}
To:
#using (Html.BeginForm("Login", "User"))
{
#Html.ValidationSummary()
<p>
Username: #Html.TextBoxFor(x => x.Username)
Password: #Html.TextBoxFor(x => x.Password)
Remember Me: #Html.DropDownListFor(x => x.RememberMe, new[]
{
new SelectListItem() { Text = "Yes", Value = "true"},
new SelectListItem() {Text = "No", Value = "false"}
}, "Select")
<input type="submit" value="Login" />
</p>
}
Where "User" is your controller and "Login" is your action method that handles the Login's POST.
http://msdn.microsoft.com/en-us/library/dd492590(v=vs.118).aspx

ASP.NET MVC: Multiple submit buttons using Ajax.BeginForm

I want to create a page that has a next button and previous button that switches the image displayed.
For that purpose I created an Ajax.BeginForm and inserted into it, an image and two submit buttons.
Can I (should I) have multiple submit buttons inside an Ajax.BeginForm?
How would the controller handle each submit separately?
Try this,
View
#model TwoModelInSinglePageModel.RegisterModel
#using (Ajax.BeginForm("DYmanicControllerPage", "Test", FormMethod.Post,null, new { id = "frmSignUp" }))
{
<div>
<input type="hidden" id="" name="hidden2" id="hdasd" />
#Html.HiddenFor(m => m.hidden1)
#Html.LabelFor(m => m.Name)
#Html.TextBoxFor(m => m.Name)
#Html.ValidationMessageFor(m => m.Name)
</div>
<br />
<div>
#Html.LabelFor(m => m.Address)
#Html.TextBoxFor(m => m.Address)
#Html.ValidationMessageFor(m => m.Address)
</div>
<br />
<div>
#Html.LabelFor(m => m.PhoneNo)
#Html.TextBoxFor(m => m.PhoneNo)
#Html.ValidationMessageFor(m => m.PhoneNo)
</div>
<input type="submit" value="Save" id="btnSave" name="ButtonType"/>
<input type="submit" value="Next" id="btnNext" name="ButtonType" />
}
Controller
[HttpPost]
public ActionResult DYmanicControllerPage(RegisterModel model, string ButtonType)
{
if(ButtonType == "Next")
{
// Do Next Here
}
if (ButtonType == "Save")
{
//Do save here
}
return JavaScript("REturn anything()");
}
I would recommend that you have two buttons and then depending on what button was clicked you could set the action on the form:
Razor
$(function (){
$("#btn-prev").click(function() {
$("#form").attr
(
"action",
"#Url.Action("Action", "Controller", new {area="Area" })",
).submit();
});
$("#btn-next").click(function() {
$("#form").attr
(
"action",
"#Url.Action("Action", "Controller", new {area="Area" })",
).submit();
});
});
I am using jQuery here to do this, but I think you can get the idea.
I had the same requirement/issue and tried both solutions here and they both work for me. I LIKE the idea of setting the action via jquery when clicking so I can keep my actions separate so they can be used by other views.
HOWEVER, I've found that when I do this while I debug, it posts TWICE and BOTH the OnSuccess and OnFailure are triggered. It only happens when debugging though. Keep this in mind when picking.

MVC 4 binding nested list of lists result

I've done some research on this and seem to find posts that are either outdated or don't quite work in my situation. I may be using wrong keywords when searching though... =/
On my web page I have Tabs containing Group boxes that contain Lines which in turn contain Items.
So it is lists of lists 4 levels.
The problem:
When posting back, ViewModel.Tabs is null and I can't save anything.
Everything displays quite nicely, but nothing is posted back.
The code
Views/Shared/EditorTemplates/Tab.cshtml
#model AWMCCRM.Web.ViewModels.Tab
#Html.HiddenFor(vm => vm.Name)
<div id="tab-#Model.Name.Replace(" ", string.Empty)" class="tab-content two">
#Html.EditorFor(vm => vm.Groups)
</div>
Views/Shared/EditorTemplates/Group.cshtml
#model AWMCCRM.Web.ViewModels.Group
#Html.HiddenFor(vm => vm.Name)
<fieldset>
<legend>#Model.Name</legend>
#Html.EditorFor(vm => vm.Lines)
</fieldset>
Views/Shared/EditorTemplates/Line.cshtml
#model AWMCCRM.Web.ViewModels.Line
<div class="_100Max">
#Html.HiddenFor(vm => vm.Name)
#Html.EditorFor(vm => vm.Items)
</div>
Views/Shared/EditorTemplates/Item.cshtml
#model AWMCCRM.Web.ViewModels.Item
<div class="_#Model.DisplaySize" title="#Model.Description">
<p>
#Html.HiddenFor(x => x.DataType)
#Html.HiddenFor(x => x.Description)
#Html.HiddenFor(x => x.DisplaySize)
#Html.HiddenFor(x => x.DisplayType)
#Html.HiddenFor(x => x.IDsPiped)
#Html.HiddenFor(x => x.ItemType)
#Html.HiddenFor(x => x.Name)
#Html.LabelFor(vm => vm.Value, Model.Name)
#switch (Model.DisplayType.ToLower().Replace(" ", string.Empty))
{
case "checkbox":
#Html.CheckBoxFor(vm => Convert.ToBoolean(vm.Value))
break;
case "dropdownlist":
#Html.DropDownListFor(vm => vm.Value, Model.ValueOptionListItems)
break;
case "multiselectlist":
#Html.ListBoxFor(
x => x.SelectedValueList,
Model.ValueOptionListItems,
new { id = "itemValuesMultiSelect", multiple = "multiple", Size = 15 })
break;
case "radiobutton":
#Html.RadioButtonFor(vm => vm.Value, Model.Value)
break;
case "textarea":
#Html.TextAreaFor(vm => vm.Value)
break;
default:
#Html.TextBoxFor(vm => vm.Value)
break;
}
</p>
</div>
The ViewModel (cut down version)
namespace AWMCCRM.Web.ViewModels
{
public class PersonEditViewModel
{
public List<Tab> Tabs { get; set; }
//Other properties
//...
}
}
The View (cut down version)
#using (Html.BeginForm("Edit", "Person", FormMethod.Post, new { id = "validate-form", #class = "block-content form" }))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(x => x.PersonID)
#foreach (var tab in Model.Tabs)
{
#Html.EditorFor(vm => tab)
}
<input class="close-toolbox button" type="submit" value="Save">
}
Any suggestions?
Thanks
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
It's an old article, but it still applies.
I had this exact problem, but basically due to the model binding system you need to use an explicit for loop instead of a foreach loop, and reference your elements by their index.
#using (Html.BeginForm("Edit", "Person", FormMethod.Post, new { id = "validate-form", #class = "block-content form" }))
{
Html.AntiForgeryToken()
Html.HiddenFor(x => x.PersonID)
for (int i = 0; i<Model.Tabs.Count; i++)
{
Html.EditorFor(x => Model.Tabs[i])
}
<input class="close-toolbox button" type="submit" value="Save">
}

Resources