ASP.NET Html Extension not firing? - asp.net-mvc-2

I have a couple of Extension classes, borrowed from various places, and they both work - individually. When I try to use both on the same page it appears one does not work. Here is the setup:
MVC 2 (no path for upgrading it to MVC 3 or 4)
HtmlPrefixScopeExtensions - http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
FileBoxHtmlHelperExtension -
http://forums.asp.net/p/1566760/4033836.aspx
The .ascx page code looks like:
<%# Control Language="C#" AutoEventWireup="true" Inherits="System.Web.Mvc.ViewUserControl<PB.WMATA.ApplicationServices.ViewModels.Files.CIPDocumentAndFile>" %>
<%# Import Namespace="Company.Web.Extensions"%>
<div class="editorRow">
<% using(Html.BeginCollectionItem("docs")) { %>
<%= Html.Hidden("CIPDocument.Id", (Model != null) ? Model.Id : 0) %>
<label for="CIPNumber">Document Name:</label>
<%= Html.TextBox("CIPNumber", (Model != null) ? Model.CIPNumber : "", new { #size = "50", #maxlength = "255" })%>
<%= Html.ValidationMessage("CIPNumber")%>
<% if (Model != null && Model.FileName != null && Model.FileName.Length > 0) { %>
<label>Current File:</label>
<%= Model.FileName %>
<% } else { %>
<label>
File Upload:
<%= Html.FileBoxFor(m => m.HttpPostedFileBase) %>
</label>
<% } %>
delete
<% } %>
</div>
The output for this looks like:
<div class="editorRow">
<input name="docs.index" autocomplete="off" value="1809201d-2143-4da3-ba34-e443a332c516" type="hidden">
<input id="docs_1809201d-2143-4da3-ba34-e443a332c516__CIPDocument_Id" name="docs[1809201d-2143-4da3-ba34-e443a332c516].CIPDocument.Id" value="0" type="hidden">
<label for="CIPNumber">
Document Name:
</label>
<input id="docs_1809201d-2143-4da3-ba34-e443a332c516__CIPNumber" maxlength="255" name="docs[1809201d-2143-4da3-ba34-e443a332c516].CIPNumber" size="50" value="" type="text">
<label>
File Upload:
<input id="HttpPostedFileBase" name="HttpPostedFileBase" type="file">
</label>
<a href="#" class="deleteRow">
delete
</a>
</div>
Notice the FileUpload control did not get the HtmlPrefixScope. I expected it to be:
<input id="docs_1809201d-2143-4da3-ba34-e443a332c516__HttpPostedFileBase" name="docs[1809201d-2143-4da3-ba34-e443a332c516].HttpPostedFileBase" type="file">
I am not quite savvy enough with extensions to see what may be going on. I suspect that the collection extension is being handled before it tries to handle the filebox extension. Any ideas?

After digging in it turns out that I needed to get at the TemplateInfo.HtmlFieldPrefix value as the Html.BeginCollectionItem("docs") call had altered it. Was really simple once I understood the lifecycle of the TemplateInfo object. Here is the altered code for the FileBox & FileBoxFor code pieces:
public static MvcHtmlString FileBox(this HtmlHelper htmlHelper, string name, IDictionary<String, Object> htmlAttributes)
{
// If the HtmlFieldPrefix has been altered (see HtmlPrefixScopeExtensions class!!) then this will work with it...
var htmlFieldPrefix = htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix;
name = (!string.IsNullOrEmpty(htmlFieldPrefix) ? string.Format("{0}.", htmlFieldPrefix) : "") + name;
var tagBuilder = new TagBuilder("input");
tagBuilder.MergeAttributes(htmlAttributes);
tagBuilder.MergeAttribute("type", "file", true);
tagBuilder.MergeAttribute("name", name, true);
tagBuilder.GenerateId(name);
ModelState modelState;
if (htmlHelper.ViewData.ModelState.TryGetValue(name, out modelState))
{
if (modelState.Errors.Count > 0)
{
tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
}
}
return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.SelfClosing));
}

Related

Yii - Error (and possible solution) validating form with clientValidation active, and using a custom container

I was creating a CActiveForm with the ClientValidation enabled, but stepped in an error when I wrapped a RadioButtonList inside a UL container.
Whenever I submited the form I get a "field cannot be empty" message, even if the radios were checked.
My form:
$form=$this->beginWidget('CActiveForm', array(
'id'=>'enrollment-form',
'enableClientValidation'=>true,
'clientOptions'=>array(
'inputContainer'=>'ul',
'validateOnSubmit'=>true,
),
));
And one of the RadioButtonLists I use:
<div id="drinks">
<?php echo $form->label($model,'drink_id',array('class' => 'title')); ?>
<?php echo $form->radioButtonList($model,'drink_id', CHtml::listData($drinks, 'id', 'name'), array('container'=>'ul', 'template' => '<li class="radio_row">{input}{label}</li>','separator' => '')); ?>
<?php echo $form->error($model,'drink_id'); ?>
</div>
Was I making something wrong?
I studied the code of the jquery.yiiactiveform.js and found that my problem was that in the function that gets the value of the inputs, the value was not taken correctly, because the ID used to identify the inputs was not set in a span, or in the checkboxes/radios themselves (the id was set in the UL container I defined):
var getAFValue = function (o) {
var type,
c = [];
if (!o.length) {
return undefined;
}
if (o[0].tagName.toLowerCase() === 'span') {
o.find(':checked').each(function () {
c.push(this.value);
});
return c.join(',');
}
type = o.attr('type');
if (type === 'checkbox' || type === 'radio') {
return o.filter(':checked').val();
} else {
return o.val();
}
};
So I solved this adding || o[0].tagName.toLowerCase() === 'ul':
var getAFValue = function (o) {
var type,
c = [];
if (!o.length) {
return undefined;
}
if (o[0].tagName.toLowerCase() === 'span' || o[0].tagName.toLowerCase() === 'ul') {
o.find(':checked').each(function () {
c.push(this.value);
});
return c.join(',');
}
type = o.attr('type');
if (type === 'checkbox' || type === 'radio') {
return o.filter(':checked').val();
} else {
return o.val();
}
};
Maybe I was just making some mistake and this solution is just a crappy workaround... but maybe this is just a bug ¿?
The HTML generated:
<form id="enrollment-form" action="/enrollment" method="post">
<div style="display:none"><input type="hidden" value="b06e1d1d796f838f30ba130f8d990034aa9fdad6" name="YII_CSRF_TOKEN" /></div>
<div id="enrollment-form_es_" class="errorSummary" style="display:none"><p>Please fix the following input errors:</p>
<ul><li>dummy</li></ul>
</div>
<div id="drinks">
<label class="title" for="EnrollmentForm_drink_id">Drinks</label>
<input id="ytEnrollmentForm_drink_id" type="hidden" value="" name="EnrollmentForm[drink_id]" />
<ul id="EnrollmentForm_drink_id">
<li class="radio_row">
<input id="EnrollmentForm_drink_id_0" value="2" type="radio" name="EnrollmentForm[drink_id]" />
<label for="EnrollmentForm_drink_id_0">Tea</label>
</li>
<li class="radio_row">
<input id="EnrollmentForm_drink_id_1" value="1" type="radio" name="EnrollmentForm[drink_id]" />
<label for="EnrollmentForm_drink_id_1">Juice</label>
</li>
</ul>
<div class="errorMessage" id="EnrollmentForm_drink_id_em_" style="display:none"></div>
</div>
I noticed that if I didn't use any container when generating the RadioButtonList, Yii aplies a "class=success" to a span. And with my UL container, that class is not generated. When I've changed the Javascript, the class success is generated again.
** The HTML generated without the inputContainer option and without the last array in radioButtonList:
<div id="drinks">
<label class="title" for="EnrollmentForm_drink_id">Bebidas</label>
<input id="ytEnrollmentForm_drink_id" type="hidden" value="" name="EnrollmentForm[drink_id]">
<span id="EnrollmentForm_drink_id">
<input id="EnrollmentForm_drink_id_0" value="2" type="radio" name="EnrollmentForm[drink_id]"> <label for="EnrollmentForm_drink_id_0">Tea</label><br>
<input id="EnrollmentForm_drink_id_1" value="1" type="radio" name="EnrollmentForm[drink_id]"> <label for="EnrollmentForm_drink_id_1">Juice</label>
</span>
<div class="errorMessage" id="EnrollmentForm_drink_id_em_" style="display:none"></div>
</div>
As you can see, Yii generates a strange span that controls if the form is valid or not, by aplying to it the class "success". If I use the inputContainer UL then this class="success" is not generated (with my workaround it is generated and it works).

Upload in MVC4, got 2 parameters in my action but file is empty

I'm trying to upload a file to a directory. The following code worked for me
CONTROLLER
[HttpPost]
public ActionResult Index(HttpPostedFileBase uploadFile)
{
//string path = #"C:\Users\thomas\Desktop";
if (uploadFile != null)
{
string filePath = Path.Combine(Server.MapPath("/files"), Path.GetFileName(uploadFile.FileName));
uploadFile.SaveAs(filePath);
}
return RedirectToAction("Index");
}
HTML PAGE
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<form action="/Post/Index" method="post" enctype="multipart/form-data">
<label for="uploadFile">Upload file: </label>
<input name="uploadFile" id="uploadFile" type="file" />
<input value="uploadFile" type="submit" />
</form>
Now i'm trying to implement this in a function where i create a message which is created by a model that is containing a message and an item class. When i submit the form the model is passed to my savecontroller but the file is null in my parameter controller.
HTML PAGE
Create new message
#model GeoCitytroopers.Models.MessageItemModel
#{
ViewBag.Title = "Create";
}
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Event</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Message.MessagePicture)
</div>
<div>
<label for="uploadFile">Upload file: </label>
<input name="uploadFile" id="uploadFile" type="file" />
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Message.MessagePicture)
#Html.ValidationMessageFor(model => model.Message.MessagePicture)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Item.ItemTitle)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Item.ItemTitle)
#Html.ValidationMessageFor(model => model.Item.ItemTitle)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Item.ItemDescription)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Item.ItemDescription)
#Html.ValidationMessageFor(model => model.Item.ItemDescription)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
CONTROLLER
[HttpPost]
public ActionResult Create(HttpPostedFileBase uploadFile, MessageItemModel ViewModel)
{
if (ModelState.IsValid)
{
Utility ut = new Utility();
Item viewItem = ViewModel.Item;
Message viewMessage = ViewModel.Message;
if (uploadFile != null)
{
string filePath = Path.Combine(Server.MapPath("/files"), Path.GetFileName(uploadFile.FileName));
uploadFile.SaveAs(filePath);
}
//ADD USER TO ITEM
viewItem = ut.AddUserToItem(viewItem);
//ADD ITEM
viewItem.ItemCreateddate = DateTime.Now;
//ADD DISTRICT TO ITEM
viewItem.DistrictID = ut.GetUserDistrict();
db.Items.Add(viewItem);
//ADD LINK
viewMessage.Item = viewItem;
db.Messages.Add(viewMessage);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(ViewModel);
}
How can i pass the uploading file to my controller?
Thanks in advance!
You forgot set the correct enctype to the form. You cannot upload files without that:
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" })) {
...
}
Now the upload will work and your uploadFile parameter will not be null.
My initial guess is that the you created using Html helper doesn't have the necessary encrypt on it.
try using
using(#Html.BeginForm("action-name","controller-name",
FormMethod.Post, new { enctype="multipart/form-data"}){
}
with appropriate values for action-name and controller-name

ASP.NET MVC2 +file uploading (HttpPostedFileBase class)

I have problem with uploading my file. I want to upload it from my edit view:
<%
using (Html.BeginForm("edit","profile",FormMethod.Post, new { enctype="multipart/form-data" }))
{%>
<%: Html.ValidationSummary(true) %>
<%: ViewData["ErrorMessage"] %>
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
<%: Html.LabelFor(model => model.Image) %>
</div>
<div class="editor-field">
<input type="file" id="Image" name="Image" />
<label id="LabelErrorImage" class="errorMessage" />
</div>
<p>
<input type="submit" value="Save" onclick="return Validate(); return false;"/>
</p>
</fieldset>
<% } %>
I want to use HttpPostedFileBase class. My edit action:
[Authorize]
[HttpPost]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(string id, HttpPostedFileBase file, FormCollection formValues)
{
if (ModelState.IsValid)
{
if (file != null && file.ContentLength > 0)
{
CustomHelpers.createFolder();
var tmpPath = MyConfig.UPLOAD_FILE_PATH + "/" + Membership.GetUser().ProviderUserKey.ToString();
var path = Path.Combine(Server.MapPath(MyConfig.UPLOAD_FILE_PATH), "Avatar");
var fileExtension = Path.GetExtension(file.FileName);
file.SaveAs(path);
user.Image = "Avatar";
}
adventureDB.SaveChanges();
return RedirectToAction("Index");
}
}
But I always have empty the file object, why????? Do you have any ideas, suggestions why it can work like that? Maybe there is problem how I pass on the file value to my Edit action?
EDIT:
IT IS REALLY STRANGE AS EVEN WHEN I REMOVE
using (Html.BeginForm("Index","Profile",FormMethod.Get, new { enctype="multipart/form-data" }))
The page source still has:
<body>
<form method="post" action="6111e591-b92d-4bcb-b214-ab8f664b35f9" id="form1">
I mean I can not change the tag but have no idea why :/
Try changing:-
public ActionResult Edit(string id, HttpPostedFileBase file,
FormCollection formValues)
to:-
public ActionResult Edit(string id, HttpPostedFileBase image,
FormCollection formValues)
as the name of your input is image
<input type="file" id="Image" name="Image" />
edit
To be honest something else is stopping the binding of image. Is this the whole form that you have posted?
A few things to test
You have HTTPOST decorating your method twice, although I don't believe this should make a difference.
View the source and make sure there is nothing else named name=image in the source.
Make sure you empty your cache and make sure source is correct before testing again
Try using <form action="/profile/index" method="post" enctype="multipart/form-data">
Judging by your last edit you have a problem with master pages/layout? Is this a mvc/webforms hybrid?
The solution of this problem when:
We use Master.Site,
We want to upload file in a view,
We are sure that it should work but we all the time has null,
Then:
Guys were right - I had wrong name in my view - check it!
Check source code of your view and if you have 2 < form > tags you should remove the < form > tag from Master site as then the second one is ignored!
Now it should work.
Well, in your view you named the file input 'image' but your action method accepts a parameter called 'file'. Rename one of those and it should work.

ASP.NET MVC - DropDownList Validation Problem

I've got two DropDownLists in a form on a page that contain the same values (a list of languages). I want to ensure that the user doesn't select the same value in each of the drop downs.
I've tried using JavaScript to ensure the selected values aren't the same, it works fine but the form submits anyway.
What's the best way to accomplish this?
Here's the code from my View:
<script type="text/javascript">
function CheckLanguageDDL()
{
var form = document.getElementById("form0");
var sourceLangIndex = form.SourceLanguage.selectedIndex;
var targetLangIndex = form.TargetLanguage.selectedIndex;
var strSourceLanguage = form.SourceLanguage.options[sourceLangIndex].text;
var strTargetLanguage = form.TargetLanguage.options[targetLangIndex].text;
if (strSourceLanguage == strTargetLanguage)
{
alert("Source Language and Target Language must be different!");
return;
}
}
</script>
<% Html.BeginForm("Index", "Translate", FormMethod.Post, new { enctype = "multipart/form-data" }); %>
<fieldset>
<legend>Request</legend>
<br />
<div class="editor-label">
<%: Html.LabelFor(m => m.SourceLanguage) %>:
</div>
<div class="editor-field">
<%: Html.DropDownList("SourceLanguage", (IEnumerable<SelectListItem>)ViewData["SourceLanguages"]) %>
<%: Html.ValidationMessageFor(m => m.SourceLanguage) %>
</div>
<br />
<div class="editor-label">
<%: Html.LabelFor(m => m.TargetLanguage) %>:
</div>
<div class="editor-field">
<%: Html.DropDownList("TargetLanguage", (IEnumerable<SelectListItem>)ViewData["TargetLanguages"]) %>
<%: Html.ValidationMessageFor(m => m.TargetLanguage) %>
</div>
<input type="submit" value="Submit Request" onclick="CheckLanguageDDL();" />
</p>
</fieldset>
Thanks.
Make the function return a true/false value that the form submit use that return value
function CheckLanguageDDL()
{
var form = document.getElementById("form0");
var sourceLangIndex = form.SourceLanguage.selectedIndex;
var targetLangIndex = form.TargetLanguage.selectedIndex;
var strSourceLanguage = form.SourceLanguage.options[sourceLangIndex].text;
var strTargetLanguage = form.TargetLanguage.options[targetLangIndex].text;
if (strSourceLanguage == strTargetLanguage)
{
return false;
}
return true;
}
And on the button:
onclick="return CheckLanguageDDL();"

Quick MVC2 checkbox question

In order to get my EF4 EntityCollection to bind with check box values, I have to manually create the check boxes in a loop like so:
<p>
<%: Html.Label("Platforms") %><br />
<% for(var i = 0; i < Model.AllPlatforms.Count; ++i)
{ %>
<%: Model.AllPlatforms[i].Platform.Name %> <input type="checkbox" name="PlatformIDs" value="<%: Model.AllPlatforms[i].Platform.PlatformID %>" /><br />
<% } %>
</p>
It works, but it doesn't automatically populate the group of check boxes with existing values when I'm editing a model entity. Can I fudge it with something like?
<p>
<%: Html.Label("Platforms") %><br />
<% for(var i = 0; i < Model.AllPlatforms.Count; ++i)
{ %>
<%: Model.AllPlatforms[i].Platform.Name %> <input type="checkbox" name="PlatformIDs" value="<%: Model.AllPlatforms[i].Platform.PlatformID %>" checked=<%: Model.GameData.Platforms.Any(p => PlatformID == i) ? "true" : "false" %> /><br />
<% } %>
</p>
I figure there has to be something along those lines which will work, and am just wondering if I'm on the right track.
EDIT: I'm purposely staying away from MVC's check box HTML helper methods as they're too inflexible for my needs. My check boxes use integers as their values by design.
Close. You'll actually want to modify your server-side code so that the "checked" attribute is not emitted at all unless you want the checkbox to be checked.
checked="true"
or
checked="false"
are technically both invalid HTML. Source.
If you want a checked checkbox, you want to emit:
checked="checked"
Any value in the quotes will actually check the box (including checked="false"), but checked="checked" is considered proper.
So, updating your code, just tweak the ternary operator to use checked='checked' or nothing at all.
<%: Model.AllPlatforms[i].Platform.Name %> <input type="checkbox" name="PlatformIDs" value="<%: Model.AllPlatforms[i].Platform.PlatformID %>" <%: Model.GameData.Platforms.Any(p => p.PlatformID == i) ? "checked='checked'" : "" %> /><br />
You're on the right track, but I think you need to modify the snipet to it to
<%: Model.GameData.Platforms.Any(p => PlatformID == i) ? "checked='true'" : string.Empty %>