#Html.DisplayFor() value not change after send data. I read a article about this issue and say it like this; only send data what such as EditorFor, TextBoxFor, TextAreaFor and change state. Is it true? How can I change this value after the postback?
View
#model HRProj.Model.Person
#using(Html.BeginForm("Skills", "Home", FormMethod.Post, new { enctype = "multipart/form-data" })){
#Html.HiddenFor(m => m.SkillDoc.Filename)
<span class="file-upload">
<span>Choose a file</span>
<input type="file" name="file" />
</span>
File name : #Html.DisplayFor(m => m.SkillDoc.Filename)
<button>Upload</button>
}
Controller
public ActionResult Skills(int? id)
{
Others oparations...
var model = new Person { SkillDoc = db.GetSkillDoc().FirstOrDefault(m => m.PersonId == id) };
return View(model);
}
[HttpPost]
public ActionResult Skills(Person model, HttpPostedFileBase file)
{
Others oparations...
if (ModelState.IsValid)
{
SkillDoc doc = new SkillDoc();
doc.Id = model.SkillDoc.Id;
doc.PersonId = model.SkillDoc.PersonId;
doc.CvDoc = (file != null) ? file.FileName : model.SkillDoc.CvDoc;
db.SkillDocCRUD(doc, "I");
TempData["eState"] = "The record adding successfully";
if (file != null)
{
file.SaveAs(Server.MapPath("~/Files/" + file.FileName));
}
}
return View(model);
}
Please add the following line inside the if block:
model.SkillDoc=doc;
Or rather redirect to Skills action:
return RedirectToAction("Skills", new{id= model.PersonId});
Related
This is my first post to StackOverflow. I hope this is useful.
I have a Razor view that is intended to allow editing of the displayable properties of a model containing either pre-defined values or null values. The view should not change the contents of the model's properties unless the user changes them intentionally by editing them on in the UI based on the view. The view behaves correctly, except with regard to a property of type byte[] that contains image data: Model.ImageData
#using (Html.BeginForm("Edit", "Admin", FormMethod.Post, new { enctype = "multipart/form-data" } ))
{
#Html.EditorForModel()
<div class="editor-label">Image</div>
<div class="editor-field">
#if (Model.ImageData == null)
{
#: No image has been assigned in the database.
}
else
{
<img width="150" height="150" src="#Url.Action("GetImage", "Product", new { Model.ID} )" />
}
<div>Upload new image: <input type="file" name="Image" /></div>
</div>
<input type="submit" value="Save" />
}
The above view works as intended for all properties in the model except Model.ImageData. In this case, posting causes any previously set Model.ImageData to be set to null. I have confirmed that the Model.ImageData is set to null during post by verifying that prior to posting, Model.ImageData contains a valid byte array (with the expected image).
The controller code for the above view is:
public ViewResult Edit(int id)
{
Product product = repository.Products.FirstOrDefault(p => p.ID == id);
// breakpoint here shows that all model properties including product.ImageData are populated and valid.
return View(product);
}
[HttpPost]
public ActionResult Edit(Product product, HttpPostedFileBase Image)
{
if (ModelState.IsValid)
{
// breakpoint here shows that product.ImageData is null (but all other model properties are still populated with data).
if (Image != null)
{
product.ImageMimeType = Image.ContentType;
product.ImageData = new byte[Image.ContentLength];
Image.InputStream.Read(product.ImageData, 0, Image.ContentLength);
}
repository.SaveProduct(product);
TempData["Message"] = string.Format("{0} has been saved", product.Name);
return RedirectToAction("Index");
}
else
{
return View(product);
}
}
And here is the respository code that updates the model (though the model is changing before this code is called):
public void SaveProduct(Product product)
{
if (product.ID == 0)
{
context.Products.Add(product); // why doesn't this try to save ID = 0 to the ID field of the product in the database??
}
else
{
Product dbEntry = context.Products.Find(product.ID);
if (dbEntry != null)
{
dbEntry.Name = product.Name;
dbEntry.Description = product.Description;
dbEntry.Category = product.Category;
dbEntry.Price = product.Price;
dbEntry.ImageData = product.ImageData;
dbEntry.ImageMimeType = product.ImageMimeType;
}
}
context.SaveChanges();
}
What am I doing wrong?
This is really where you should consider using a ViewModel class instead. But to keep your current ways of doing things with minimal changes, try modifying the SaveProduct() method to the following:
public void SaveProduct(Product product, bool updateImage = false)
{
context.Products.Attach(product);
// mark product as modified
var entry = context.Entry(product);
entry.State = EntityState.Modified;
entry.Property(e => e.ImageData).IsModified = updateImage;
entry.Property(e => e.ImageMimeType).IsModified = updateImage;
context.SaveChanges();
}
and modify the controller code to:
...
var updateImage = false;
if (Image != null)
{
updateImage = true;
product.ImageMimeType = Image.ContentType;
product.ImageData = new byte[Image.ContentLength];
Image.InputStream.Read(product.ImageData, 0, Image.ContentLength);
}
repository.SaveProduct(product, updateImage);
....
My Controller
public ActionResult Edit(int id)
{
return this.EditDefault(id);
}
[HttpPost]
public ActionResult Edit(int id, Models.Company model)
{
return this.EditDefault(id, model);
}
My Model
pulbic class Company
{
... Many other Propeties
public HttpPostedFileBase File { get; set; }
}
My View
#using (Html.BeginForm(new { enctype = "multipart/form-data" }))
{
... Many other Properties
#Html.TextBoxFor(m => m.File, new
{
type = "file", style = "display:none"
})
... Submit
}
So my problem now is when I submit the page the infos in the model are right, but the File Property is still null.
I found some solutions where people added HttpPostedFileBase as parmeter in the controller (tried it doesn't work too), but I would like to avoid that anyway because the model and the controller are generated with T4. So has someone an idea why the File Property is always null?
Would be really happy about some help :)
Update: Found the solution thx to Matt Tabor.
For me the solution looks like this because I use a shared Edit page.
The javascript part is to hide the actual file upload element and use a span instead, because the file upload isn't style able.
//Shared Part
#{
RouteData routeData = this.ViewContext.RouteData;
string currentController = routeData.GetRequiredString("controller");
}
#using (Html.BeginForm("Edit", currentController, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
//Special Part
... Many other Properties
//File upload which is hidden
#Html.TextBoxFor(m => m.File, new
{
type = "file", style = "display:none"
})
//Span which forwards the clicks to the file upload
<span id="fake-file-name">Kein Bild</span>
... Submit
}
<script type="text/javascript">
$(function () {
//forward the click from the span to the file upload
$("#fake-file-name").click(function () {
$("#File").click();
});
//display the chosen file name to the user with the styled span
$("#File").bind('change', function () {
//we don't want the C:\fakepath to show
var displayFileName = this.value.replace("C:\\fakepath\\", "");
$("#fake-file-name").text(displayFileName);
});
});
</script>
you need to specify your form method as post
#using (Html.BeginForm("Edit", "CONTROLLER", null,FormMethod.Post, new { enctype = "multipart/form-data" }))
#Html.TextBoxFor(m => m.File, new
{
type = "file", style = "display:none"
})
Instead have a Input type file as shown below -
<input type="file" name="File" id="File"/>
PS: Name should match to Model property name.
UPDATE
Remove display:none from your code and it should work fine.
I am trying to add file upload to my asp.net mvc4, however, since I am just learning C# I am not sure on how where to add it:
This is the controller:
public ActionResult Create()
{
ViewBag.c_id = new SelectList(db.Cities.OrderBy(o => o.name), "c_id", "name");
ViewBag.m_id = new SelectList(db.Schools, "m_id", "name");
return View();
}
//
// POST: /Create
[HttpPost]
public ActionResult Create(TotalReport treport)
{
if (ModelState.IsValid)
{
treport.created = DateTime.Now;
db.TotalReports.Add(treport);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.c_id = new SelectList(db.Cities.OrderBy(o => o.name), "c_id", "name");
ViewBag.m_id = new SelectList(db.Schools, "m_id", "name");
return View(treport);
}
the view is here:
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.ValidationSummary(true)
<fieldset>
<div class="mycss">
<input type="file" name="file" />
</div>
</fieldset>
ok here is the part that saves the file:
if (file != null && file.ContentLength > 0)
{
// extract only the fielname
var fileName = System.IO.Path.GetFileName(file.FileName);
// store the file inside ~/App_Data/uploads folder
var path = System.IO.Path.Combine(Server.MapPath("~/myfolder"), fileName);
file.SaveAs(path);
}
Suppose if your markup is like,
<input type="file" name="file" />
and then your action should look like,
[HttpPost]
public ActionResult(HttpPostedFileBase file)
{
string filename=file.FileName;
filename=DateTime.Now.ToString("YYYY_MM_dddd_hh_mm_ss")+filename;
file.SaveAs("your path"+filename);
return View();
}
here parameter name of HttpPostedFileBase and upload control name should be same. Hope this helps
pick up the files in the controller like so
[HttpPost]
public ActionResult Create(HttpPostedFileBase fileUpload)
{
if (ModelState.IsValid)
{
treport.created = DateTime.Now;
db.TotalReports.Add(treport);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.c_id = new SelectList(db.Cities.OrderBy(o => o.name), "c_id", "name");
ViewBag.m_id = new SelectList(db.Schools, "m_id", "name");
return View(treport);
}
just add argument for the posted file to your action :
public ActionResult Create(TotalReport treport, System.Web.HttpPostedFileBase file)
and do whatever you want to do with it - read stream, save it somewhere...
Basically I have an Image Upload controller, that I am inserting in pages as follows :-
<div id='imageList'>
<h2>Upload Image(s)</h2>
#{
if (Model != null)
{
Html.RenderPartial("~/Views/File/ImageUpload.cshtml", new MvcCommons.ViewModels.ImageModel(Model.Project.ProjectID));
}
else
{
Html.RenderPartial("~/Views/File/ImageUpload.cshtml", new MvcCommons.ViewModels.ImageModel(0));
}
}
</div>
So I am passing an ID to the ImageUpload, in this case the ProjectID, so that I can include it in my insert.
Now this is piece of code is populating an ImageModel(id), in my case its ProjectID :-
public ImageModel(int projectId)
{
if (projectId > 0)
{
ProjectID = projectId;
var imageList = unitOfWork.ImageRepository.Get(d => d.ItemID == projectId && d.PageID == 2);
this.AddRange(imageList);
}
}
and this in turn leads to the ImageUploadView.cshtml :-
<table>
#if (Model != null)
{
foreach (var item in Model)
{
<tr>
<td>
<img src= "#Url.Content("/Uploads/" + item.FileName)" />
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
</tr>
}
}
</table>
#using (Html.BeginForm("Save", "File", new { ProjectID = Model.ProjectID },
FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<input type="file" name="file" />
<input type="submit" value="submit" /> <br />
<input type="text" name="description" />
}
So far so good, however my problem is that the first time
new { ProjectID = Model.ProjectID }
is correctly populated with the ProjectID, however, when I upload an image, the ProjectID is lost, and becomes zero. Is there a way I can persist the ProjectID for the second time?
Thansk for your help and time.
********* UPDATE *************************
After the upload, the Action is as follows inside the FileController :-
public ActionResult Save(int ProjectID)
{
foreach (string name in Request.Files)
{
var file = Request.Files[name];
string fileName = System.IO.Path.GetFileName(file.FileName);
Image image = new Image(fileName, Request["description"]);
ImageModel model = new ImageModel();
model.Populate();
model.Add(image, file);
}
return RedirectToAction("ImageUpload");
}
You can pass the projectId as a route value from the RedirectToAction. You should change the ImageUpload action to accept the projectId.
public ActionResult Save(int projectId)
{
....
return RedirectToAction("ImageUpload", new { projectId = projectId });
}
public ActionResult ImageUpload(int projectId)
{
var model = .. get the model from db based on projectId
return View("view name", model);
}
I have a partial view:
<% using (Html.BeginForm("add", "home", FormMethod.Post,
new { enctype = "multipart/form-data" })){%>
<input name="IncomingFiles" type="file" />
<div class="editor-field"><%: Html.TextBox("TagsInput") %></div>
<p><input type="submit" value="Create" /></p><% } %>
And this in the controller:
[HttpPost]
public ActionResult add(HttpFileCollection IncomingFiles, string TagsInput)
{
return View();
}
It will simply not match up my uploaded file to the HttpFileCollection, they come out as HttpFileCollectionBase.
How can i get the view to pass me a HttpFileCollection?
Do i need any specific BeginForm args?
Thank you!
Do something like this instead on your action side. You don't pass the files as parameters:
[HttpPost]
public ActionResult add(string TagsInput) {
if (Request.Files.Count > 0) {
// for this example; processing just the first file
HttpPostedFileBase file = Request.Files[0];
if (file.ContentLength == 0) {
// throw an error here if content length is not > 0
// you'll probably want to do something with file.ContentType and file.FileName
byte[] fileContent = new byte[file.ContentLength];
file.InputStream.Read(fileContent, 0, file.ContentLength);
// fileContent now contains the byte[] of your attachment...
}
}
return View();
}