HttpPostedFileBase editor for using with EditorForModel - asp.net-mvc

Okay, maybe I'm missing something, but I can't figure this out. Using ASP.NET MVC 3, Razor views.
I have a model object like this:
public class MyModel
{
public HttpPostedFileBase File { get; set; }
public string Title { get;set; }
public string Description { get; set; }
}
When, in a strongly typed view, I call #Html.EditorForModel(), it only generates the Title and Description form fields.
I created the file: Views\Shared\EditorTemplates\HttpPostedFileBase.cshtml, with dummy content, but it still doesn't get rendered.
Is it possible to get EditorForModel to generate file input fields?

I managed to get it working by creating a custom Object.cshtml editor template:
#foreach (var prop in ViewData.ModelMetadata.Properties.Where(pm => pm.ShowForEdit && !ViewData.TemplateInfo.Visited(pm)))
{
if (prop.HideSurroundingHtml)
{
#Html.Editor(prop.PropertyName)
}
else
{
<div class="editor-container">
<div class="editor-label">
#Html.Label(prop.PropertyName, prop.DisplayName)
</div>
<div class="editor-field">
#Html.Editor(prop.PropertyName, prop.TemplateHint)
#Html.ValidationMessage(prop.PropertyName, "*")
</div>
</div>
}
}
Basically it calls Html.Editor() for each property of the model. I don't know if it's a good solution, but it works for now.

I have investigated a similar problem - the editor for complex properties is not output. Your case may be different but the workaround that I found (creating an editor for your model - Model.ascx) should work for you too.

Related

MVC 4 MetaClass Markup not showing up in Partial View

I have a View that loads a few partial views at load time depending on a database. It's working great so far with one exception: because this is a database first situation, I'm using a meta class to add the appropriate markup to the model fields. It worked perfectly fine when I had all the stuff on a single page but since I broke it out into partials, it refuses to recognize the meta class anymore.
There's a lot of unrelated code everywhere so I've broken out the important stuff (some names have been changed to protect the innocent)...
In the view to load said partials:
foreach (var p in MyProject.Controllers.UtilityController.GetPViews())
{
#Html.Partial("../Partial/" + p.ViewName, new ViewDataDictionary { { "column", "column" + p.ColumnSize } })
}
The main part of the meta class:
namespace MyProject.Models
{
public class MyProjectData_Meta
{
[MetadataType(typeof(MyProjectData_Meta))]
public partial class MyProjectData
{
public class MyProjectData_Meta
{
//Gets and sets and stuff. For example:
[Required(ErrorMessage = "Please enter a first name.")]
[Display(Name = "First Name")]
[UIHint("FloatLabel")]
public string MemberFirstName { get; set; }
}
}
}
}
And finally the code that pulls the list from said database...
public static List<ModuleList> GetPViews()
{
MyProjectEntities pv = new MyProjectEntities();
List<ModuleList> lvl = pv.ModuleLists.OrderBy(s => s.PageOrder).ToList();
return lvl;
}
So is there some glaring detail I'm missing or more information you need from me in order to tell me where I'm being stupid?
EDIT: Doh! Forgot to put in my actual view code:
#model MyProject.Models.MyProjectData
<div class="#ViewBag.column">
<fieldset id="fsBasic">
<legend>Basic Information</legend>
<div class="editor-label">
#Html.LabelFor(model => model.MemberFirstName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.MemberFirstName)
#Html.ValidationMessageFor(model => model.MemberFirstName)
</div>
</div>
To answer the other question: I know it's not using the meta class because the LabelFor is showing "MemberFirstName" instead of "First Name" which is what it does actually do when I'm not using a partial class.

MVC Razor Form submit not working

I am trying to use a simple form to allow authorized users to modify content on select pages on an MVC3 Razor site that I'm building. I am unable to get the edit form to post correctly though.
My model is as follows:
public class WebContent
{
public virtual UInt32 id { get; set; }
public virtual String page { get; set; }
public virtual String section { get; set; }
[UIHint("tinymce_jquery_full"), AllowHtml]
public virtual String content { get; set; }
}
My Controller:
[Authorize]
public ActionResult Edit(String page, String section)
{
WebContent content = _WebContent.GetSection(page,section);
return View(content);
}
[Authorize]
[HttpPost]
public ActionResult Edit(WebContent content)
{
if (ModelState.IsValid)
{
_WebContent.Update(content);
return View("Index");
}
else return View("Index");
}
And my View:
#model SongbirdsStudios.Models.WebContent
#{
ViewBag.Title = "Edit '"+Model.page+"'Page Content";
}
<div>
<h2>Edit</h2>
#using (Html.BeginForm())
{
<fieldset>
<legend>Page Content</legend>
#Html.HiddenFor(m => m.id)
#Html.HiddenFor(m => m.page)
#Html.HiddenFor(m => m.section)
<div class="editor-label">
Content
</div>
<div class="editor-field">
#Html.EditorFor(m => m.content)
</div>
<p>
<input type="submit" value="Update" />
</p>
</fieldset>
}
</div>
The view renders correctly and displays the expected elements. The UIHint("tinymce_jquery_full") is getting picked up correctly and the TinyMCE editor appears on the page. But, when the form submits, I get an exception.
System.Web.HttpRequestValidationException: A potentially dangerous Request.Form value was detected from the client (content=...)
Everything I've read indicates that the AllowHTML attribute should allow this to post, but it's not for some reason.
I can get around this by adding the [ValidateInput(false)] attribute to the HttpPost controller method. If I do that, then this exception does not occur, but the model still does not get passed to the controller. It just passes null instead. Examining the HttpContext in the debugger indicates that it is passing 4 separate values - one for each property in my model instead of passing the model class back to the controller. I can't figure out what I need to change to make this work correctly.
I'm hoping it's something simple that I missed, and someone with a better eye can see what it is.
So after further investigation into how ASP MVC maps form fields to the model class and examining the HTML emitted to the browser, I found that this was an issue with the name of the property in my WebContent class.
public virtual String content { get; set; }
The TinyMCE editor uses a content variable to define certain characteristics associated with the editor interface. This was apparently causing the HTML 'content' generated by the user input in the editor to not get mapped back to the Model property.
Simply changing the name of the property in the model class (and of course fixing the corresponding database mapping and view references) immediately fixed the problem.
public virtual String web_data_content { get; set; }
Everything else being identical, this worked perfectly with the UIHint and AllowHTML attributes.
Add this attribute on your action
[ValidateInput(false)]
This should solve your problem
if you use ie7
this may has some err
<input type="submit" value="Update" />
give the button a name

Remove Prefix from ViewModel in ASP.NET MVC

I have a ViewModel class composed of several nested classes:
public class UserAccountViewModel : UserProfileViewModel
{
public UserAccountEmailViewModel UserAccountEmail { get; set; }
public UserAccountLocationViewModel UserAccountLocation { get; set; }
public UserAccountPasswordViewModel UserAccountPassword { get; set; }
}
The HTML rendered from this (pay attention to model.UserAccountEmail.Email):
<div class="editor-label">
#Html.LabelFor(model => model.UserAccountEmail.Email)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.UserAccountEmail.Email)
#Html.ValidationMessageFor(model => model.UserAccountEmail.Email)
</div>
Is this:
name="UserAccountEmail.Email"
I would prefer the name to simply be Email
Changing ViewData.TemplateInfo.HtmlFieldPrefix didn't help. Overloading the htmlFieldName in #Html.EditorFor isn't going to work because I still want the label and validation message to match the rendered HTML element (no overload for htmlFieldName in these functions).
I'd prefer to not create partials for this.
Edit:
Meh...using partials actually isn't too bad. It actually makes quite a lot of sense.
See my answer here about this design, it might make sense for this particular strategy
Many models to view with many partial views

MVC Binding Data

I have a MVC Project.
I have questions in a database that I want my users to answer. I have them in the database because they need to be able to be dynamic. The user needs to submit an answer for each question. So when the view displays, it shows the question and a textbox for each question.
What would the best way to go about doing this as I do not want to hard code in the question like #Html.textBox("Question1") etc.
Question 1 [__textbox1___]
Question 2 [__textbox2___]
... etc.
I'd probably have a ViewModel that contains a collection of a custom QuestionResponder type.
IEnumerable<IQuestionResponder> Questions{get;set;}
public interface IQuestionResponder{
Guid QuestionId{get;set;}
string Question{get;set;}
string Answer{get;set;}
}
Then you can create the display items you require for your new view model.
#for(var i = 0; i <= questionList .Count; i++)
{
#Model.Questions.ToList()[i].Question
#Html.TextBoxFor(m => m.Questions.ToList()[i].Answer)
}
Alternartively you can create an EditorTemplate to avoid looping in your view:
Insdie ~/Views/Shared/EditorTemplates/ add a new view called QuestionResponder (the name of your custom class).
Inside that template you can then add:
#model MyApp.Models.QuestionResponder
<div>
#Html.DisplayFor(m => m.Question)
#Html.TextBoxFor(m => m.Answer)
</div>
While you'll then call from your original view:
#Html.EditorFor(m => m.Questions)
There's builtin way of doing that in MVC. Very simple way, by the way. There are many alternatives, take a look at this article by Phil Haack to inspect them all. One of those is with dictionaries. Quesion.Id will be key, Answer will be value
Sample:
First, create appropriate ViewModels
public class AnswerQuestionViewModel
{
public Quesion Question { get; set; }
public string Answer { get; set; }
}
public class Quesion //
{
public int Id { get; set; }
// Maybe some other properties.
}
Inside ~/Views/Shared/EditorTemplates/, create editor that will render Editor.
#model Models.AnswerQuestionViewModel
#Html.HiddenFor(model => model.Question.Id)
#Html.EditorFor(model => model.Answer)
And ~/Views/ControllerName/ActionName.cshtml
#model IEnumerable<ControllerInspectorTest.Models.AnswerQuestionViewModel>
#using (Html.BeginForm())
{
#Html.EditorForModel();
<p>
<input type="submit" value="Create" />
</p>
}
And when you create post action, parameter will be filled in
[HttpPost]
public ActionResult AnswerQuestions(IEnumerable<AnswerQuestionViewModel> quesions)
{
// questions parameter is filled in correctly
//do save job;
}
Note that question parameter can by type of IList or List too

DRY in the MVC View

I've been working a lot with asp.net web forms and one think that I like about the is the consistency with the generated markup e.g. if you create a composite control for a TextField you can control the generated markup in a single class like and don't break the SRP:
<form:textfield id="firstName" runat="server" required="true" label="First Name" />
I you're your going to generate the markup by hand it might look like this:
<label for="firstName" id="lbl_firstName">Name <span class="required">*</span></label>
<input id="firstName" name="firstName" type="text" value="" />
The problem is when would like to change something for example add a wrapping div or move the span. In worst case you have to edit thousands of views.
That's why I really like the MVC Contrib FluentHtml.
<%= this.TextBox(x => x.Message.PostedBy).Class("required").Label("Name") %>
My question is what do you think is the best way to add a wrapping div for the code line above? I think hand writing is not an option because of the arguments above? Perhaps extending the TextBox : MvcContrib.FluentHtml.Elements.TextInput?
have you checked InputBuilder in MvcContrib project? it is used in Codecampserver as well. have a look and i think u will like it.
Honestly, I don't think the example case you've given applies to real world. A textbox is a textbox. If you need one, you render one.
If you need a more "complex" control like a textbox wrapped in a div tag, then you can have a partial view for that.
For example, Model :
public class CustomControlModel {
public string Name { get; set; }
public string Value { get; set; }
public string Class { get; set; }
public bool WrapInDivTag { get; set; }
//you get the idea
}
Custom Control :
<%# Control Inherits="System.Web.Mvc.ViewUserControl<CustomControlModel>" %>
<%if (Model.WrapInDivTag) {%> <div> <% } %>
<%=Html.TextBox(Model.Name, Model.Value, new { #class = Model.Class })%>
<%if (Model.WrapInDivTag) {%> </div> <% } %>
And when rendering :
<%Html.RenderPartial("CustomControl",
new CustomControlModel { Name = "name", WrapInDivTag = true }); %>
That's a very simple example but I hope it explains why I suggested partial views. Don't forget that you can expose another property to get which tag to render etc.
InputBuilders are one option. With FluentHtml you could create a custom element, something like this:
public class TextBoxInContainer : TextInput<TextBox>
{
public TextBoxInContainer (string name) : base(HtmlInputType.Text, name) { }
public TextBoxInContainer (string name, MemberExpression forMember, IEnumerable<IBehaviorMarker> behaviors) : base(HtmlInputType.Text, name, forMember, behaviors) { }
protected override ToString()
{
divBuilder = new TagBuilder(HtmlTag.Div);
divBuilder.InnerHtml = ToString();
return divBuilder.ToString(TagRenderMode.SelfClosing);
}
}
To use this from your view you would extend IViewModelContainer something like this:
public static MyTextBox TextBoxInContainer <T>(this IViewModelContainer<T> view, Expression<Func<T, object>> expression) where T : class
{
return new TextBoxInContainer (expression.GetNameFor(view), expression.GetMemberExpression(), view.Behaviors)
.Value(expression.GetValueFrom(view.ViewModel));
}
Then if you want to change your container to a span sitewide, you change the ToString method of TextBoxInContainer.

Resources