MVC .net textbox to texbox area (Razor) - c#

I have a text box that I want to expand and add a few lines to make it a text area instead of a small box.
original code
#Html.EditorFor(x => x.emailBody)
<input type="submit" value="Send Mail" class="btn btn-default" />
I have found this on SOF, but it is not working and the field looks the same
#Html.EditorFor(x => x.emailBody, new { #class = "form-control", #rows = 5 })
<input type="submit" value="Send Mail" class="btn btn-default" />
Can anyone suggest a simple solution

#Html.TextAreaFor(model => model.emailBody, new { #rows = 3 })
you can use textarea for and set rows according to your choice its better way then increasing line of a normal textbox....

Add a DataType.MultilineText attribute to the property and #Html.EditorFor() will render a <textarea> instead of <input type="text" ../>
[DataType(DataType.MultilineText)]
public string emailBody { get; set; }
Note also (assuming your using MVC-5.1+), then to add html attributes, it should be
#Html.EditorFor(x => x.emailBody, new { htmlAttributes = new { #class = "form-control", #rows = 5 } })
If you have only MVC-5, then use #Html.TextAreaFor() or create a custom EditorTemplate and pass the attributes as AdditionalViewData (example here)

Why cant you use something like this
#Html.TextAreaFor(model => model.emailBody, new { #rows = 3 })
instead of EditorFor, you can use TextAreaFor to get multiple lines in a textbox

Related

Adding bootstrap classes with html helper methods not working?

Is there any way to add bootstrap classes like form-control to asp.net mvc html helper methods like #html.Textbox("searchString");?
this is the code I have in the view
#this is the html output for the search bar taking searchstring param from controller in htmlhelper#
#using (Html.BeginForm())
{<div class="jumbotron">
<div class="row">
<div class="col-md-12">
<p>
Search: #Html.TextBox("SearchString")
<input class="btn btn-info" type="submit" value="Search" />
</p>
</div>
</div>
</div>
}
It seems like everything is playing nice with bootstrap except the #Html.Textbox and its messing up my UI.
is there a way to affect the #Html.Textbox with bootstrap form-control class?
ps the "SearchString" variable comes from my controller.
Yes.
You have the possibility to add HtmlAttributes:
#Html.TextBox("SearchString", null, new { #class = "form-control" })
And if you're using a property from your #model:
#Html.TextBoxFor(m => m.Property, new { #class = "form-control" })
#Html.TextBox("SearchString", null, new {#class="form-control"});
One variation of the #Html.TextBox helper takes an anonymous object that you can use to pass in HTML attributes. You would use it like:
#Html.TextBox("SearchString", null, new { #class = "form-control" })

MVC Filename property mapped to <input> tag so Filename property = selected filename

MVC5 EF6 C#
I have a Create view for an Image class. The Image class contains the string Property "ImagePathLocal".
I have the following on the view, to let the user select a file to upload:
<div class="form-group">
#Html.Label("Select Image", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<input type="file" name="photo" id="photo" class="btn btn-default" />
#Html.ValidationMessageFor(model => model.ImagePathLocal, "", new { #class = "text-danger" })
</div>
</div>
I have to use
<input type="file" name="photo" id="photo" class="btn btn-default" />
as there isn't an equivalent #Html.InputFor(model => model.ImagePathLocal... etc. to use - My Googling has lead me to believe I have to do it this way (Correct me if I'm wrong)
When I click submit, the ModelState isn't valid, as the ImagePathLocal isn't populated. The file is successfully sent and I can save to the server (if I step over the validation).
So the question is, how can I allow the user to select a file and then populate the ImagePathLocal Property with the name of the file they've selected so that when the form is submitted, the Model.ImagePathLocal is populated and therefore the ModelState will be valid?
Edit:
Thanks to #Ehsan, the main answer was that I needed to use:
#Html.TextBoxFor(m => m.ImagePathLocal, new { type="file", #class="btn btn-default"})
However, I was wanted to populate 2 Properties once a user selected a file. My string property, which was the filename, and my HttpPostedFileBase property, which was the file the user selected. I changed the form so that the user was selecting the HttpPostedFileBase property:
#Html.TextBoxFor(m => m.Image, new { type="file", #class="btn btn-default"})
and I hanged that property so that whenever it gets set, it automatically sets the string property:
private HttpPostedFileBase _image;
public HttpPostedFileBase Image
{
get
{
return _image;
}
set
{
_image = value;
ImagePathLocal = _image.FileName;
}
}
This achieves exactly what I require.
You have to use overload of TextBoxFor() which takes htmlAttributes as parameter :
#Html.TextBoxFor(m => m.ImagePathLocal, new { type="file", #class="btn btn-default"})
you can also use input but then it's name should be your Model property name which is ImageLocalPath like this:
<input type="file" name="ImageLocalPath" id="ImageLocalPath" class="btn btn-default" />

Issue displaying money on view

I have the following
public decimal? Price {get;set;}
When I enter 3000.00 in to the textbox on the view (textbox below)
<div class="form-group">
<label class="col-lg-3 control-label no-padding-right">Price</label>
<div class="col-lg-5">
#Html.ValidationMessageFor(m => m.Price)
<div class="input-group">
<span class="input-group-addon">£</span>
#Html.TextBoxFor(m => m.Price, new { #class = "form-control", type = "text", id = "txtPrice", onkeypress = "return isNumberKey(event)" })
</div>
</div>
<label class="col-lg-4 control-label" style="text-align: left">Decimal format</label>
So it would look like this
It saves in the database as 3000.00 which is expected, but when I return back to the view to edit it the value in the textbox is 3000.0000
I have tried some of the solutions on here
Remove trailing zeros of decimal
I think the issue I have is the field on the view is of type decimal not a string, so I'm uncertain on how to format this decimal to remove the trailing zeros so it looks like picture above
You need to use the oveload of TextBoxFor that accepts a format string
#Html.TextBoxFor(m => m.Price, "{0:0.00}", new { #class = "form-control", type = "text", id = "txtPrice", onkeypress = "return isNumberKey(event)"})
Side notes:
Remove type="text". The html helper already adds this for you (add
is there a reason why you dont just use the default id rendered by
the helper, which would be id="Price"?).
Use Unobtrusive Javascript rather that polluting your markup
with behavior - e.g. $('#txtPrice').keypress(...

Passing value of an input control to controller

I have the following:
#Html.BeginForm("AddComment", "Shared", new { guid = Model.VideoGuid, content="", userid = authenticatedUserId.ToString()}){
<div class="input-group">
<input name="txtCommentContent" type="text" class="form-control"><span class="input-group-btn">
<button class="btn btn-default">
<img src="~/Content/images/Message-Add.png" /></button>
</span>
</div>
}
I need to pass the text input inside the input control to the control as part of the routeValues. The above content="" is going to do that.
How can this be done?
You should be using the HTML Helpers:
#Html.EditorFor(m => m.CommentContent);
This will model bind the value in CommentContent (Which should be a property in your model) and pass it back up to the server when the form is POSTED.
As an alternative solution, you can also serialize the form and pass it up via AJAX.
As #Ic has pointed out you could also use:
#Html.TextBoxFor(m => m.CommentContent, new { #class = "form-control" });
Which will change the input type to text and also add your CSS class form-control.

How to produce non-sequential prefix collection indices with MVC HTML Editor templates?

The following code has been stripped down a lot, but basically what I'm looking to achieve is as follows:
I'd like to able to edit Questions and their containing Answer Choices, while being able to dynamically add/remove Questions/Answer Choices from the page. Ideally, the HtmlFieldPrefix for my items would be non-sequential, but Html.EditorFor() uses a sequential index.
I have a Question ViewModel that contains an IEnumerable of Answer Choices:
public class QuestionViewModel
{
public int QuestionId { get; set; }
public IEnumerable<AnswerChoiceViewModel> AnswerChoices { get; set; }
}
In my Question partial view (Question.ascx), I have this:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Models.QuestionViewModel>" %>
<%=Html.HiddenFor(m => m.QuestionId)%>
<%=Html.EditorFor(m => m.AnswerChoices) %>
And the Answer Choice editor template (AnswerChoiceViewModel.ascx):
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Models.AnswerChoiceViewModel>" %>
<%=Html.HiddenFor(m => m.AnswerChoiceId)%>
<%=Html.TextBoxFor(m => m.Name)%>
When I render Question.ascx, the output will look as follows:
<input type="hidden" id="QuestionId" value="1" />
<input type="hidden" id="Question.AnswerChoices[0].AnswerChoiceId" value="1" />
<input type="hidden" id="Question.AnswerChoices[0].Name" value="Answer Choice 1" />
<input type="hidden" id="QuestionId" value="2" />
<input type="hidden" id="Question.AnswerChoices[1].AnswerChoiceId" value="2" />
<input type="hidden" id="Question.AnswerChoices[1].Name" value="Answer Choice 2" />
What I want to know is how I can provide EditorFor a custom GUID index so that the page would render like this:
<input type="hidden" id="QuestionId" value="1" />
<input type="hidden" id="Question.AnswerChoices[e1424d5e-5585-413c-a1b0-595f39747876].AnswerChoiceId" value="1" />
<input type="hidden" id="Question.AnswerChoices[e1424d5e-5585-413c-a1b0-595f39747876].Name" value="Answer Choice 1" />
<input type="hidden" id="QuestionId" value="2" />
<input type="hidden" id="Question.AnswerChoices[633db1c3-f1e6-470b-9c7f-c138f2d9fa71].AnswerChoiceId" value="2" />
<input type="hidden" id="Question.AnswerChoices[633db1c3-f1e6-470b-9c7f-c138f2d9fa71].Name" value="Answer Choice 2" />
I have already written a helper method that will get the prefix index of the current context and store it in a hidden ".Index" field so that non-sequential indices can be bound correctly. Just want to know how EditorFor is assigning the indexes so that I can override it (or any other working solution).
While ago I tackled with this problem and ran into a post from S. Sanderson(creator of Knockoutjs) where he described and solved similar problem. I used portions of his code and tried to modify it to suit my needs. I put the code below in some class (exapmle: Helpers.cs), add the namespace in web.config.
#region CollectionItem helper
private const string idsToReuseKey = "__htmlPrefixScopeExtensions_IdsToReuse_";
public static IDisposable BeginCollectionItem(this HtmlHelper html, string collectionName)
{
var idsToReuse = GetIdsToReuse(html.ViewContext.HttpContext, collectionName);
string itemIndex = idsToReuse.Count > 0 ? idsToReuse.Dequeue() : Guid.NewGuid().ToString();
// autocomplete="off" is needed to work around a very annoying Chrome behaviour whereby it reuses old values after the user clicks "Back", which causes the xyz.index and xyz[...] values to get out of sync.
html.ViewContext.Writer.WriteLine(string.Format("<input type=\"hidden\" name=\"{0}.index\" autocomplete=\"off\" value=\"{1}\" />", collectionName, itemIndex));
return BeginHtmlFieldPrefixScope(html, string.Format("{0}[{1}]", collectionName, itemIndex));
}
public static IDisposable BeginHtmlFieldPrefixScope(this HtmlHelper html, string htmlFieldPrefix)
{
return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix);
}
private static Queue<string> GetIdsToReuse(HttpContextBase httpContext, string collectionName)
{
// We need to use the same sequence of IDs following a server-side validation failure,
// otherwise the framework won't render the validation error messages next to each item.
string key = idsToReuseKey + collectionName;
var queue = (Queue<string>)httpContext.Items[key];
if (queue == null)
{
httpContext.Items[key] = queue = new Queue<string>();
var previouslyUsedIds = httpContext.Request[collectionName + ".index"];
if (!string.IsNullOrEmpty(previouslyUsedIds))
foreach (string previouslyUsedId in previouslyUsedIds.Split(','))
queue.Enqueue(previouslyUsedId);
}
return queue;
}
private class HtmlFieldPrefixScope : IDisposable
{
private readonly TemplateInfo templateInfo;
private readonly string previousHtmlFieldPrefix;
public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix)
{
this.templateInfo = templateInfo;
previousHtmlFieldPrefix = templateInfo.HtmlFieldPrefix;
templateInfo.HtmlFieldPrefix = htmlFieldPrefix;
}
public void Dispose()
{
templateInfo.HtmlFieldPrefix = previousHtmlFieldPrefix;
}
}
#endregion
After you can have EditorTemplate or partial like this
#using (Html.BeginCollectionItem("AnswerChoices"))
{
#Html.HiddenFor(m => m.AnswerChoiceId)
#Html.TextBoxFor(m => m.Name)
}
And enumerate through your list rendering template(partial).
It took me way longer than it should to figure this out. Everyone is working way too hard to do this. The secret sauce is these four lines of code:
#{
var index = Guid.NewGuid();
var prefix = Regex.Match(ViewData.TemplateInfo.HtmlFieldPrefix, #"^(.+)\[\d+\]$").Groups[1].Captures[0].Value;
//TODO add a ton of error checking and pull this out into a reusable class!!!!
ViewData.TemplateInfo.HtmlFieldPrefix = prefix + "[" + index + "]";
}
<input type="hidden" name="#(prefix).Index" value="#index"/>
Now, what is this doing? We get a new guid, this is our new index to replace the integer one that is automagically assigned. Next we get the get the the default field prefix and we strip off that int index we don't want. After acknowledging we've created some technical debt, we then update the viewdata so that all of the editorfor calls now use that as the new prefix. Finally, we add an input that gets posted back to the model binder specifying the index it should use to bind these fields together.
Where does this magic need to happen? Inside your editor template: /Views/Shared/EditorTemplates/Phone.cshtml
#using TestMVC.Models
#using System.Text.RegularExpressions
#model Phone
<div class="form-horizontal">
<hr />
#{
var index = Guid.NewGuid();
var prefix = Regex.Match(ViewData.TemplateInfo.HtmlFieldPrefix, #"^(.+)\[\d+\]$").Groups[1].Captures[0].Value;
//TODO add a ton of error checking and pull this out into a reusable class!!!!
ViewData.TemplateInfo.HtmlFieldPrefix = prefix + "[" + index + "]";
}
<input type="hidden" name="#(prefix).Index" value="#index"/>
<div class="form-group">
#Html.LabelFor(model => model.Number, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Number, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Number, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.IsEnabled, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<div class="checkbox">
#Html.EditorFor(model => model.IsEnabled)
#Html.ValidationMessageFor(model => model.IsEnabled, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Details, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextAreaFor(model => model.Details, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Details, "", new { #class = "text-danger" })
</div>
</div>
</div>
EditorTemplate? What?! How?! Just put it in the directory mentioned above using the object name for the filename. Let MVC convention work its magic. From your main view just add the editor for that IEnumerable property:
<div class="form-group">
#Html.LabelFor(model => model.Phones, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Phones, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
Now, back in your controller, make sure you update your method signature to accept that ienumerable (Bind include Phones):
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ContactId,FirstName,LastName,Phones")] Contact contact)
{
if (ModelState.IsValid)
{
db.Contacts.Add(contact);
db.SaveChanges();
//TODO need to update this to save phone numbers
return RedirectToAction("Index");
}
return View(contact);
}
How do you add and remove them on the page? Add some buttons, bind some JavaScript, add a method to the controller that will return a view for that model. Ajax back to grab it and insert it into the page. I'll let you work out those details, as it's just busy work at this point.
Html.EditorFor is nothing else as a so called Html helper method, which renders input with all apropriate attributes.
The only solution which comes me to mind is to write the own one. It must be pretty simple - 5-10 lines ling. Take a look at this Creating Custom Html Helpers Mvc.
Steve Sanderson has provided a simple implementation that may do what you're looking for. I recently started using it myself; it is not perfect, but it does work. You have to do a little magic-stringing to use his BeginCollectionItem method, unfortunately; I'm trying to workaround that myself.
Another option is to override id attribute like this:
#Html.TextBoxFor(m => m.Name, new { id = #guid })

Categories