I am developing an internal project managment dashboard using MVC 2. One of the requirements is to link to already existing documents on one of our local servers, in other words, browse to server, select file, click add and the view for that project will contain the link. Here is what I have (I've left some details out for brevity):
Model:
public class AddDocumentModel
{
public HttpPostedFileBase DocumentLink { get; set; }
}
View:
<%
using (Html.BeginForm(MVC.ProjectDetails.Actions.AddDoc(this.Model.ProjectID),
FormMethod.Post, new { enctype = "multipart/form-data" }))
{%>
<%=Html.TextBoxFor(a => a.DocumentLink,
new { type = "file", style = "width:100%;"})%>
<input type="submit" value="Add Document Link" />
<%} %>
Controller:
[AcceptVerbs(HttpVerbs.Post)]
public virtual ActionResult AddDoc(AddDocumentModel docModel)
{
var model = _projectManagementService.AddDocumentLink(
docModel.DocumentLink.FileName);
}
So, as you can see I am using an html textbox for file upload but not actually uploading, just attempting to grab path and filename and use that as link. However due to security constraints this will only work in IE, as no other browser will let you get at the path. Also, if the user uses a mapped drive it won't work as the full path will not be used, so they have to navaigate directly to the server.
Can anyone think of another way to do this? I would like to be able to use the browse functionality offered by upload functionality, but not be tied by constraints.
At the moment the only (low tech) solution I can think of is for the user to explicitly paste the link into a text box. But would prefer something a lot more friendly.
Thanks in advance. First posted question too, so be kind :-)
If I were you I would allow them to either upload a new file or paste in the location of an existing file. There's no reason to try to reuse a file upload element to do what you're doing.
Form example (didn't feel like writing out the <%=Html %>
<form>
<div>
<input type="radio" name="AddDocumentType" value="New" />
<label for="NewDocument">Upload New Document</label>
<input type="file" id="NewDocument" name="NewDocument" />
</div>
<div>
<input type="radio" name="AddDocumentType" value="Link" />
<label for="LinkDocument">Link To Existing Document</label>
<input type="text" id="LinkDocument" name="LinkDocument" />
</div>
<input type="submit" value="Add Document Link" />
</form>
Model
public enum AddDocumentType
{
New,
Link
}
public class AddDocumentModel
{
public AddDocumentType AddDocumentType { get; set; }
public HttpPostedFileBase NewDocument { get; set; }
public string LinkDocument { get; set; }
}
Related
My PageModel has a single BindProperty like ?Query=, whose length should not exceed 1000. However, I also want this string to always be defined; on initial page load (or if a blank form is submitted), I want to use the default value "*" instead of null or "":
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.ComponentModel.DataAnnotations;
namespace RazorTestApp.Pages;
public class IndexModel : PageModel
{
[BindProperty(SupportsGet = true)]
[StringLength(1000)]
[Required(AllowEmptyStrings = false)]
public string Query { get; set; } = "*";
public void OnGet()
{
}
}
I have an equally simple View:
#page
#model IndexModel
<div class="text-center">
<form method="get">
#Html.EditorForModel()
<div>
<input type="submit" />
</div>
</form>
</div>
I am having 2 issues with this:
Load page with no query string (initial load): the input box correctly displays "*", but I also get a validation error "The Query field is required".
Load page with ?Query= (submit with empty query): the input box does not show the default "*" (shows empty string), and I get the same validation error "The Query field is required".
I am obviously not doing this correctly. How can I set a default value for Query to be used whenever it is empty/not provided, and have it play nicely with validation?
I tried to test the validations on my side.
Below is my model:
[BindProperty(SupportsGet = true), Required, StringLength(1000)]
public string Rating { get; set; } = "*";
View:
<div class="form-group">
<label asp-for="Movie.Rating" class="control-label"></label>
<input asp-for="Movie.Rating" class="form-control" />
<span asp-validation-for="Movie.Rating" class="text-danger"></span>
</div>
Output:
On my side, I could see that * is added to the input field and if I remove it and submit the form then I get the validation message.
For testing purposes, I would suggest you make a test with the <form method="post">. If the issue persists then creating a new model and view and trying to test it separately may help to narrow down the issue.
I have implemented an image upload child action form for an application. I have a strongly typed partial view.
public class ImageViewModel{
public long ImageId{get;set;}
public long OwnerId{get;set;}
public string ImageName{get;set;}
public string ImageDescription{get;set;}
public IEnumerable<HttpPostedFileBase> Files { get; set; }
}
Razor code looks something like this:
<form action="UploadImage" method="post" enctype="multipart/form-data">
#Html.ValidationSummary()
#Html.HiddenFor(m => m.OwnerId)
#Html.HiddenFor(m => m.ImageId)
#HtmlEditorFor(m=>m.ImageName)
<input type="file" name="Files" id="file0" />
<input type="submit" value="Upload" />
</form>
Here is the problem. When form is posted back, the model has uploaded file and ImageName value in it. But values that were bound using HiddenFor are missing.
[HttpPost]
public ActionResult UploadImage(ImageViewModel model)
{ ...}
I have checked HTML source. Hidden fields are rendered corrected with Id and names matching to property named of model. On post back I checked the raw request. Both hidden fields are carried in Form collection. But model binding is not setting the values of these fields in properties.
Is there something that I am missing about these hidden fields?
Thanks
I have a simple situation where I have a page that uploads Files, for some importing. At the moment, all I have is a file upload input on my page.
this is what my get controller looks like
public ActionResult FileUpload()
{
return View();
}
This is what my view looks like
#{
ViewBag.Title = "FileUpload";
}
<h2>FileUpload</h2>
<form action="/Home/FileUpload" method="post" enctype="multipart/form-data">
<input type="file" id="newFile" name="newFile" />
<input type="submit" id="submitButton" value="Submit" />
</form>
and this is what my post action looks like
[HttpPost]
public ActionResult FileUpload(HttpPostedFileBase newFile)
{
if (newFile.ContentLength > 0)
{
//do stuff here
}
return View("Index");
}
You will of course notice there is no mention of a model here as I cannot find a way to create a model for this situation. I would like to have some very basic validation, along the lines of 'please choose a file before you upload', thats all.
Is there a way to achieve this?!
Thanks in advance
Will
Create model class with string property newFile and put a Required on it.
In controller accept not HttpPostedFile but your model class.
You should add the client side validation manually:
<input type="file" data-val="true" data-val-required="please select a file" name="file" />
#Html.ValidationMessage("file")
I have created view page in MVC like
<%using (Html.BeginForm())
{ %>
<%=LabelHelpers.Label("firstname", "FirstName:")%>
<br/>
<%=Html.TextBox("firstname")%>
<br/><br/>
<%=LabelHelpers.Label("lastname", "Lastname:")%>
<br/>
<%=Html.TextBox("lastname")%>
<br/><br/>
<input type="Button" value="Register"/>
<%} %>
Here I want to write Buttonclick Event ...How and Where should i write?
Your input is of type button - these don't do anything without additional client side code.
If you want to handle the 'event' on the server in a similar way that you would have in ASP.NET, you should convert it to a submit button. Assuming your controller is called 'Account' and your action is called 'Register' your current code would look something like this:
public ViewResult Register()
{
return View();
}
You want to start by passing a model to the view:
public ViewResult Register()
{
var registerModel = new RegisterModel();
return View(registerModel);
}
Your current view is using loosely typed inputs. Since you're passing it a model you can use strongly typed views. Your model should look something like this:
public class RegisterMode
{
public string Firstname { get; set; }
public string Surname { get; set; }
}
To use strongly typed views, change your view to look like this:
<%using (Html.BeginForm())
{ %>
<%=Html.LabelFor(x => x.Firstname)%>
<br/>
<%=Html.TextBoxFor(x => x.Firstname)%>
<br/><br/>
<%=Html.LabelFor(x => x.Surname)%>
<br/>
<%=Html.TextBoxFor(x => x.Surname)%>
<br/><br/>
<input type="submit" value="Register"/>
<%} %>
What we've done is told the view to build labels and text boxes for your RegisterModel type. This will allow the model values to be automatically mapped when you POST the form to the controller.
Do accept the post, we need to add a new Action to the controller, with the same name, but accepting a parameter of type RegisterModel:
public ActionResult Register(RegisterModel model)
{
// do something with the model, such as inserting it into the database.
// model.Firstname will contain the value of the firstname textbox
// model.Surname will contain the value of the surnaem textbox
return RedirectToAction("Success");
}
One last thing to do, to be safe, is to add the [HttpGet] and [HttpPost] attributes to your controller actions to control the methods they accept:
[HttpGet]
public ViewResult Register()
and
[HttpPost]
public ActionResult Register(RegisterModel model)
I suggest you read up on MVC at http://www.asp.net/mvc and read the NerdDinner tutorial chapter in Professional MVC (available for free online in PDF format).
joining to the question, want to make it more concrete
i have a form, the form already has a submit button, but i need to bind an additional action to another button.
yes, i do know that MVC does not support events 'cause HTML forms doesn't support them.
so the solution i've came to is to create to hidden inputs inside the form and bind an 'onclick' event (jquery 'live' method) to every... oh, what the hell? here is the code:
html:
<input type="hidden" id="SenderControlID" name="SenderControlID" value="-1" />
<input type="hidden" id="SenderControlValue" name="SenderControlValue" value="-1" />
js:
if ($('#SenderControlID')[0]) {
$('input[type="submit"], input[type="button"], input[type="checkbox"], input[type="radio"]').live('click', function () {
$('#SenderControlID').val($(this).attr('name'));
$('#SenderControlValue').val($(this).val());
});
}
but maybe there is more elegant solution?
What's up with this? The viewmodel variable is a bool with value true.
<%= Html.HiddenFor(m => m.TheBool) %>
<%= Html.Hidden("IsTimeExpanded",Model.TheBool) %>
<input type="hidden" value="<%=Model.TheBool%>" name="TheBool" id="TheBool">
Results in:
<input id="TheBool" name="TheBool" value="False" type="hidden">
<input id="TheBool" name="TheBool" value="False" type="hidden">
<input value="True" name="TheBool" id="TheBool" type="hidden">
What am I doing wrong? Why don't the helpers work as intended?
1) use different (unique) ids
2) don't use this helper, use
<input type="hidden" name="the-name"
value="<%= Html.AttributeEncode(Model.TheBool) %>" id="TheBool_1216786" />
As answered here the problem is that HTML helpers by default use the posted values (if available) then refer to the model. Personally I don't think this makes a whole bunch of sense and now wonder how many other bugs lie in wait throughout our platform.
Anyway, the solution posted in the aforementioned answer will solve the problem, just add this line before you return from the controller:
ModelState.Remove("TheBool")
And yes, it's a bit rubbish because you can only use a string reference... but it does work.
Here's an example in razor:
html:
#Html.HiddenFor(x => Model.TheBool, new { #id = "hdnBool" })
javascript:
alert($('#hdnBool').val());
model:
public class MyModel()
{
public bool TheBool{ get; set; }
}
I had similar and ended up getting round it like this.
The situation is the user wants a Save and then confirm save scenario....
I chose to use the solution below rather than
ModelSate.Remove("OperationConfirmed");
(which does work) as I feel it is more intuative....
#{
string btnSaveCaption = "Save Changes";
if (Model.OperationConfirmed)
{
btnSaveCaption = "Confirm Save Changes";
#Html.Hidden("OperationConfirmed", true)
}
}