MVC Problems 16 - c#

I am trying to post a part of my model for a view to action in another controller. I've tried to do it this way:
<% using (Html.BeginForm("Register", "User", new {createDto = Model.UserCreateDto}, FormMethod.Post))
{%>
Form code here...
<%}%>
UserCreateDto is a part of my model and it's being filled in this form. I'm trying to pass it to "Register" action in UsersController:
[HttpPost]
public ActionResult Register(UserCreateDto createDto)
The problem is that route value in Register action is null (createDto) after form submit. Maybe it is happening because route values are being attached to url before form submit event (and before model fields are filled).
Is there any way to do what I want?
UPDATE Here is the actual HTML code:
Имя пользователя
Пароль
E-mail
And here is the updated form code:
<% using (Html.BeginForm("Register", "User", FormMethod.Post))
{%>
<%: Html.HiddenFor(userCreateDto => Model.UserCreateDto.Username) %>
<%: Html.HiddenFor(userCreateDto => Model.UserCreateDto.Password) %>
<%: Html.HiddenFor(userCreateDto => Model.UserCreateDto.Email) %>
<%: Html.HiddenFor(userCreateDto => Model.UserCreateDto.Active) %>
<%= Html.LabeledValidatedTextBoxFor(username => Model.UserCreateDto.Username, true) %>
<%= Html.LabeledValidatedTextBoxFor(password => Model.UserCreateDto.Password, true)%>
<%= Html.LabeledValidatedTextBoxFor(email => Model.UserCreateDto.Email, true)%>
<input type="submit" value="Register"/>
<%}%>
Html.LabeledValidatedTextBoxFor is just my custon HTML helper.
Here is updated action signature:
[HttpPost]
public ActionResult Register(UserCreateDto userCreateDto)
userСreateDto in this action is still null.http://www.media fire.com/file/5dcrv5zupy44c0i/A16.rar/file By the way, is there any way not to use hidden fields? They are not secure enough to pass through them user registration information.

Probably what you're looking for is ModelBinding. If your UserCreateDto Model is complex enough, and by that I mean that it contains any other complex type inside, the DefaultModelBinder is not going to know how to fill your model. So you need to tell it explicitly how to map the values in your request to the Model you are expecting in the Register post method in your UserController.
This link shows an example of how to do it in ASP.NET Core. I couldn't find the ASP.NET MVC official documentation but it's not so different than this.
Hope it helps!

Related

Html.ValidationMessageFor not showing

I have a MVC2 Project using ADO.NET Entity Data Model that I used to generate my view automatically since it has tons of fields. It gave me code like this.
<% using (Html.BeginForm()) {%>
<%= Html.ValidationSummary(true) %>
<div class="editor-label">
<%= Html.LabelFor(model => model.Position) %>
</div>
<div class="editor-field">
<%= Html.TextBoxFor(model => model.Position) %>
<%= Html.ValidationMessageFor(model => model.Position) %>
</div>
But with a ton more fields.
Here is my Controller code:
MyProjectEntities_Career_Applications dbca = new MyProjectEntities_Career_Applications();
[HttpPost]
public ActionResult Apply(Career_Applications application)
{
if (ModelState.IsValid)
{
//save form
dbca.AddToCareer_Applications(application);
dbca.SaveChanges();
}
else
{
ModelState.AddModelError("", "Application could not be submitted. Please correct the errors below.");
}
//redirect to thank you page
return View("Apply", application);
}
When the ModelState.IsValid fails I see the <%= Html.ValidationSummary(true) %> but none of the Html.ValidationMessageFor's ever show up.
What am I missing?
Turns out the problem was with the CSS. Stupid mistake, ugh!
Thanks for the help regardless.
I think you're missing a Jquery reference or error handling library that you're using.
This also happens often when you include validation script multiple times. (In shared view, and in your view)

MVC Html.ActionLink with post functionality?

I'm checking to see if anyone has written an MVC extension for Html.ActionLink that you can pass in Post parameters like such:
<% Html.ActionLink("Click me", "Index", "Home", new { MyRouteValue = "123" }, null, new { postParam1 = "a", postParam2 = "b" }); %>
That would render the link like normal but having an onClick event that submits an also rendered form with an Action url for the Action, Controller, and Route Values with additional hidden inputs from the Post Parameters like such:
Click me
<form id="theform" action="/Home/Index/123" method="post">
<input type="hidden" name="postParam1" value="a">
<input type="hidden" name="postParam2" value="b">
</form>
I'm looking to redirect users to various pages with potentially a lot of data. Not only from page to page, but from email to page also. This would be highly reusable and I think would clean up a lot of code, and would save a bunch of time writing this if its already floating around out there. I hate recreating the wheel when I don't have to.
ActionLink is just for creating an <a>. What you are asking for would blow up if it is already inside of a form. If it isn't then it is preferable to make the link the submit button inside the form and NOT use javascript (javascript and emails don't get along great).
You could create the form and appende it to the end of the DOM. This could be done through partial view or through javascript.
Honestly I suggest you don't use a POST. If you persist most of the data and just have the ids needed to retrieve said data, you should never have to pass too much data in an actionlink.
Ajax.ActionLink works perfectly fine for a post request. To refresh page, you can create a function that refreshes page (e.g. function reload(){ windows.location.reload();}).
It would look something like this.
#Ajax.ActionLink("DiaplyName", "Action", new { parameters to post }, new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, OnComplete="reload();"})
Note: You'll need to reference the appropriate scripts to use ajax or jQuery code.
This piece of code was helpful for me and saved my day.. I improved it and it helped me for Impersonated user.. here is bellow ,what i did..
<% if (Session["SessionUserImpersonate"] != null && Session["SessionUserImpersonate"] != "" && Session["SessionUserImpersonate"] == "Yes")
{
BLL.Models.User userold = new BLL.Models.User();
userold = (BLL.Models.User)Session["SessionUserOld"];
%>
<span class="FL">(Impersonated as <%=Website.Backoffice.SessionHelper.Session_User.UserName != null ? Website.Backoffice.SessionHelper.Session_User.UserName:"" %> , </span>
<form class="FL" id='frmid' action="/Index/Login?username=<%=userold.UserName%>&password=<%=userold.Password%>&IsImpersonate=No" method="post">
<a class="TxtRed" style="cursor:pointer;" onclick="$('#frmid').submit(); return false;" > - finish impersonated session </a>
</form>
)
<%} %>

Aliasing Querystring Params in BeginForm with FormMethod.Get - MVC2

The Question
Is there a way to alias the parameters generated by MVC, in my case it would be the searchTerm param? For example, in my viewmodel it would still be SearchViewModel.searchTerm but the URL would look more like /Home/Search?s=jimmy
Search Form
<% using (Html.BeginForm("Search", "Home", FormMethod.Get)) {%>
<%: Html.TextBoxFor(model => model.searchTerm) %>
<input type="submit" value="Search" />
<% } %>
View Model
public class SearchViewModel
{
public string searchTerm { get; set; }
}
The URL Generated
/Home/Search?searchTerm=jimmy
Edit: Doesn't seem possible. Looks like I'll have to do it on my own..
I think you could either change your textbox control to not use templating and use the name your want to see in the query string like this...
<%= Html.TextBox("s", Model.searchTerm)
Or you could change the template that the TextBoxFor method uses with a own custom one, but I'm not sure what convention you would use. Maybe check for modelnames of "searchTerm"?
This site has some examples of how to create a custom template.
Sorry, if the syntax was not correct in the code, but I do not have MVC on this machine.

How can I use Html.DisplayFor inside of an iterator?

I am loving MVC 2. The whole thing just fits the web so well.
There is one piece of functionality, however, that I am unable to coax out of the Html.DisplayFor() function:
<# Page Inherits="ViewPage<IEnumerable<Foo>>">
<% foreach(var item in Model) { %>
<%: Html.DisplayFor(item.BarBaz) %>
<% } %>
I need to be able to use the DisplayTemplate for this value. Is there a way to do this?
Actually, I figured it out. How stupid of me.
This works:
<# Page Inherits="ViewPage<IEnumerable<Foo>>">
<% foreach(var item in Model) { %>
<%: Html.DisplayFor(m => item.BarBaz) %>
<% } %>
However, this will not work correctly for Html.HiddenFor and Html.ValueFor. In particular, Html.HiddenFor(m => item.NullableDecimal) will render as <input name="NullableDecimal" value="0" /> and Html.ValueFor(m => item.NullableDecimal, "0.00##) will render as 0.00##. However, if you apply a [DisplayFormat(DataFormatString = "{0:0.00########}" to your view model, then it will suddenly work. For this reason, you're probably best off using Html.Display, Html.Hidden, and Html.Value extensions, since you're less likely to run into scenarios where things fail when someone makes a non-local change.
You can accomplish it by getting away from the foreach and using a regular for loop:
<% for (int i = 0; i < Model.Count(); i++) { %>
<%: Html.DisplayFor(p=> p.ElementAt(i).BarBaz) %>
<%} %>
Another option would be to create a PartialView that took an Foo object and only displayed the information that you wanted.
<% foreach (var item in Model)
{
Html.RenderPartial("FooBarBazLine", item);
} %>
This is an old question, but i guess that someone can get benefits from my solution:
Aspx view
<%# Page Inherits="ViewPage<IEnumerable<Foo>>" %>
<% foreach (var item in Model) { %>
<%: Html.DisplayFor(m => item) %>
<% } %>
Razor view
#model IEnumerable<Foo>
#foreach (var item in Model)
{
Html.DisplayFor(m => item)
}
Since DisplayFor accepts an implicit typed lambda, we can directly indicate the instance to display in the loop.
Finally, i'm agree with Anders E. Andersen's answer for the template usage
Html.DisplayFor can automatically iterate over collections, displaying a partial view for each element in the collection.
The first thing you need to do is create an actual model class, with the collection being a property of the class.
public class Bar
{
public IEnumerable<Foo> foo { get; set; }
}
Return this class from your controller instead of the raw collection.
Secondly you need a display template for the Foo class. Display templates are partial views that need to be placed in the folder Views/Shared/DisplayTemplates.
Edit: You can have them in your controller subfolder of Views as well if you want to limit the template to a particular controller. See this question for more information.
Here is an example in razor syntax:
#model YourNameSpace.Foo
<p>#Model.BarBaz</p>
Save it as Foo.cshtml in the DisplayTemplates folder given above.
This template is pretty simple because it is based on your example where you are only displaying a string, but if the collection elements where a class with its own properties you could create a more elaborate template.
Now in the original view, you can get rid of the loop entirely and just write
#Html.DisplayFor(m => m.foo)
Notice foo is the name of the property in your new model class that contains the old collection you looped over before.
DisplayFor will automatically know that the foo property is of type (collection of) Foo and pick up the Foo.cshtml template in the DisplayTemplates folder and show it once for each element in foo.
In Razor I am using
#Html.DisplayFor(model => item.CreatedDate)
Just be careful that a DisplayFor on a POST of a form doesnt actually create a field that can be referenced back in the controller. You would need to use an additional HiddenFor if you need to refer to fields in your submitted POST method.

Best way to do ListView in ASP.NET MVC?

bascially, how should i do it now that using the asp:listview control is out of the question?
i am migrating from webforms to mvc and former implementation was in <asp:ListView...> so how should i do it now in terms of best user experience for the user? ie: i will need to ajax everything.
thanks
You should use the standard list box now. If you need to return the list from your controller you return a SelectList [i think that's it].
To ajax the select list you can use say jQuery to add click and change events which will then call actionresults in your controller.
Edit
Ah right, there are a couple of ways. You could use a jQuery grid which will display the rows and allow you to have edit delet buttons. You then post back to an action, make the changes and come back. All Ajax of course.
The other way might be to write a couple of PartialViews. The first loops through your collection and the second displays the row. The last one would have say the buttons or a check box or whatever.
Then you again post back, via ajax, to an action result, make the changes and come back.
Your action result can return a fully rendered partial view so after you make your changes, return a new partial view of your data rows and replace the old with the new.
edit 2
if you want code, leave a comment and i'll provide
edit 3
There are a bunch of jQuery grids out there, just google it.
As for partial views something like this would work;
HTML Partial View 1 called BenefitList;
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IQueryable<Models.Benefit>>" %>
<% foreach(Benefit benefit in Model){ %>
<% Html.RenderPartial("Benefit", benefit); %>
<% } %>
HTML Partial View 2;
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Models.Benefit>" %>
<fieldset class="Benefit benefit" id="<%= Model.Id %>">
<legend><label class="Title"><%= Html.Encode(Model.Title) %></label> <a class="BenefitDescriptionView" href=".">View</a><a class="BenefitDescriptionEdit" href=".">Edit</a></legend>
<div class="BenefitDescription hidden">
<%= Html.Encode(Model.Description) %>
</div>
</fieldset>
Some jQuery for you to do a post to an action result;
function jQueryDeleteBenefit() {
$.post("/Home/jQueryDeleteBenefit", { Id: YOURID },
function(NEWHTML) {
$('.EditProductContainer').html(NEWHTML);
});
}
An action result;
[AcceptVerbs(HttpVerbs.Post)]
public void jQueryDeleteBenefit(int id
{
//delete item
return PartialView("BenefitList", AllMyBenefits);
}

Categories