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.
Related
I am implementing a search filter to one of my application's views. I struggle at getting routeValues passed to controller action using #Html.BeginForm() and GET request.
The action accepts the following properties:
public ActionResult Books(int id, string type, string search)
{
//rest of the code
}
The View's search box looks like this:
#model ILookup<string, CityLibrary.Models.Library.Book>
....
#using (Html.BeginForm("Books", "Collections", new { id = Model.First().First().CollectionId, type = ViewBag.BookType }, FormMethod.Get, null))
{
<div class="input-group col-md-4">
#Html.TextBox("search", null, new { #class = "form-control form-control-fixed-width", #placeholder = "Filter title..." })
<span class="input-group-btn">
<button class="btn btn-default" type="submit">
<span class="glyphicon glyphicon-search"></span>
</button>
</span>
</div>
}
The problem occurs when I am submitting the search box. The controller action gets the id and search string, but type is always null, even though ViewBag.BookType is not null. Fiddler shows this:
GET /Collections/Books/2?search=searchterm
Which seems to be completely ignoring type parameter in the request.
Source code in browser:
<form action="/Collections/Books/2?type=available" method="get">
<div class="input-group col-md-4">
<input class="form-control form-control-fixed-width" id="search" name="search" placeholder="Filter title..." type="text" value="" />
<span class="input-group-btn">
<button class="btn btn-default" type="submit">
<span class="glyphicon glyphicon-search"></span>
</button>
</span>
</div>
</form>
Does it have something to do with GET method? I would like to avoid POSTing as I would have to write another controller action with basically the same code.
EDIT: It seems that the problem occurs when I try to use GET request. POSTing the form actually passes all the parameters to the controller action. Why is that?
This behavior is in accordance with the HTML specifications, In particular for a form with method="get", (my emphasis)
Mutate action URL
Let destination be a new URL that is equal to the action except that its <query> component is replaced by query (adding a U+003F QUESTION MARK character (?) if appropriate).
So the query string value in your form's action attribute is replaced with the query string generated by the name/value pairs of the form controls.
Two options to solve this:
Remove the new { type = ViewBag.BookType } from the BeginForm() method and add a hidden input for the parameter
<input type="hidden" name="type" value="#ViewBag.BookType" />
Create a custom route definition for the method so that type is added as a route parameter, not a query string value (note this must be before the Default route)
routes.MapRoute(
name: "Books",
url: "Collections/Books/{id}/{type}",
defaults: new { controller = "Collections", action = "Books" }
);
so that your current BeginForm() code will generate
<form action="/Collections/Books/2/available" method="get">
and the form submit will result in a url of Collections/Books/2/available?search=searchterm
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" />
I have a parameterless Index for the HttpGet which works. But when I post it the HttpPost version of Index is invoked and the viewmodel object is passed in, but there is only the value of the dropdown in it. The rest is null (products, title)
[HttpPost]
public ActionResult Index(ProductsViewModel pvm)
{
// breakpoint on line 36, shows that pvm.Title is null and Products too.
return View(pvm);
}
My compilable and running example can be downloaded from my OneDrive http://1drv.ms/1zSsMkr
My view:
#model KleinKloteProductOverzicht.Models.ProductsViewModel
#using (Html.BeginForm("Index", "Products"))
{
<h2>#Html.DisplayFor(m => m.Title)</h2>
<input type="submit" value="post dit" /><br/>
<div class="row">
<div class="col-lg-2 col-md-2">
#Html.DropDownListFor(x => x.CurrentSort, EnumHelper.GetSelectList(typeof(SortOptions)), new { #class = "multiselect"})
</div>
</div>
if (Model.Products.Count() > 0)
{
<div class="row">
#foreach (var item in Model.Products)
{
#Html.DisplayFor(i => item.Name);
}
</div>
}
}
If I have this view model:
public class ViewModel
{
public string Name {get;set;}
public string SelectedLocation {get;set;}
public IEnumerable<SelectListItem> Locations {get;set;}
}
And your actions look like this:
public ActionResult MyForm()
{
var vm = new ViewModel
{
Locations = context.Locations.ToList() // Some database call
}
return View(vm);
}
[HttpPost]
public ActionResult MyForm(ViewModel vm)
{
vm.Locations // this is null
}
It is null because the model binder can't find a form control that is setting its data.
The <form> must set some data in the view for the model binder to pick it up.
<form>
Name: <input type="text" id="name" />
</form>
This will set the Name property on the view model, because the model bind can see the id of the form control and uses that to know what to bind to.
So in terms of your view, you need to make sure you wrap any content that you want to post back to the server with #using(Html.BeginForm())
Anyway this is my guess.
Well, you seem to be confused as to how [HttpPost] and form tags interact with eachother.
You see, when .NET MVC binds your parameters in your controller actions, it tries to derive that data from the request. For [HttpGet] it does this by looking at the query string.
For [HttpPost] calls, it also looks at the Request.Form. This variable is populated with the values of all input fields that were inside the form you submitted.
Now, this is your view:
#using (Html.BeginForm("Index", "Products"))
{
<h2>#Html.DisplayFor(m => m.Title)</h2>
<input type="submit" value="post dit" /><br/>
<div class="row">
<div class="col-lg-2 col-md-2">
#Html.DropDownListFor(x => x.CurrentSort, EnumHelper.GetSelectList(typeof(SortOptions)), new { #class = "multiselect" })
</div>
</div>
if (Model.Products.Count() > 0)
{
<div class="row">
#foreach (var item in Model.Products)
{
#Html.DisplayFor(i => item.Name);
}
</div>
}
}
You only have one select tag (generated by Dropdownlistfor) but no other inputs. That's why .NET MVC cannot infer any other data for your view model.
If you change your view to this:
#model KleinKloteProductOverzicht.Models.ProductsViewModel
#using (Html.BeginForm("Index", "Products"))
{
<h2>#Html.DisplayFor(m => m.Title)</h2>
<input type="submit" value="post dit" /><br/>
<div class="row">
<div class="col-lg-2 col-md-2">
#Html.DropDownListFor(x => x.CurrentSort, EnumHelper.GetSelectList(typeof(SortOptions)), new { #class = "multiselect" })
</div>
</div>
if (Model.Products.Count() > 0)
{
<div class="row">
#for (var i = 0; i < Model.Products.Count; i++)
{
#Html.DisplayFor(model => model.Products[i].Name)
#Html.HiddenFor(model => model.Products[i].ID)
}
</div>
}
}
You'll see I've added a hidden input (<input type="hidden">) for the product id. Note that the product name still will be null.
I would suggest you follow a tutorial on .NET MVC and read up on some of the concepts behind it, because the very fact that you ask this question reveals that you have much to learn.
Best of luck!
P.S. One last tip: #Html.Blablabla writes directly to your view. You usually don't need that ";" at the end, because it will be inside your generated html.
Your property is not associated with a "postable" control, therefore it will not be submitted along with the form data. If your really want to get the value in your Title property, just set it as a hidden input.
#Html.HiddenFor(m => m.Title)
A label will not be posted when submitting a form but an input will. This is exactly what HiddenFor does; it creates a hidden input element which will be picked up by the form submit.
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
Before some people start yelling, I realize there is similar questions up, but their all dealing with single entries...
I have a long form and want to use a generic method of dealing with the re-displaying of data.
Please note : this is an asp.Net MVC 4 application using razor2 views
Example of one of the fields,
<div class="control-group">
<label class="control-label">#Html.LabelFor(m => m.Name)
<span class="required">*</span>
</label>
<div class="controls">
#Html.TextBoxFor(m => m.Name, new { #name = "nameInput" })
</div>
</div>
My think was to add an ID to each Textbox...
Example of how is might be displayed on the confirmation view..
<div class="control-group">
<label class="control-label">Name:</label>
<div class="controls">
<span class="text display-value" data-display="nameInput"></span>
</div>
</div>
Then render that value using data-display with the ID...
jQuery that I thought would deal with it...
var displayConfirm = function() {
$('.display-value', form).each(function(){
var input = $('[name="'+$(this).attr("data-display")+'"]', form);
if (input.is(":text") || input.is("textarea")) {
$(this).html(input.val());
} else if (input.is("select")) {
$(this).html(input.find('option:selected').text());
}
});
}
Unfortunately this does not appear to be working correctly....
Can anyone point out / re-solve the issue ?
Got it, I needed to use the name in the model rather then assigning names.
Got correct names from "page view source", and simply plugged that value into data-display tag.