I am creating an application in mvc4. In edit mode i want user to edit his/her details,
user have option to change his profile image by selecting from fileuploader. But if user do not select file in uploader the previous file location will be sent.
I am storing image path in table same as other details.
So, only one Stored procedure is created.
I am only 1 day old in mvc.Started working on mvc directly, without studying by seniors order.
So pls help
It is a good practice to use strongly typed models. You should create a separater ViewModel for your User:
Models/UserEditViewModel
public int Id { get; set; }
public string Name { get; set; }
// Other properties
public HttpPostedFileBase Photo { get; set; }
Controller:
[HttpGet]
public ActionResult Edit(int id)
{
// Get user from database
var user = db.Users.Find(id);
// Map user to UserEditViewModel
var userVM = new UserEditViewModel
{
Id = user.Id;
Name = user.Name;
// Other mappings
}
return View(userVM);
}
[HttpPost]
public ViewResult Edit(UserEditViewModel model)
{
if (ModelState.IsValid)
{
var existingUser = db.Users.Find(model.Id);
if (model.Photo != null && model.Photo.ContentLength > 0)
{
// Update database and copy image to server
}
}
}
View:
#model YourProject.Models.UserEditViewModel
#using (Html.BeginForm("Edit", "YourController", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
// Form stuff
#Html.HiddenFor(m => m.Id)
#Html.EditorFor(m => m.Name)
#Html.EditorFor(m => m.Photo) // Creates <input type="file" name="Photo" />
<input type="submit" value="Save" />
}
The following code is on the assumption that you save the file's name in the db table and save the file on the app server.
The code will be as follows:
Model:
public string Photo {get;set;}
View :
<input type='file' id='file'/>
#Html.HiddenFor(m=>m.Photo)
JS :
// if there is a photo update by user, change the Photo hidden element value
//get the final name of the photo saved in server
$("#Photo").val(newname);
Then simply post your form.
In case, the user doesn't change its photo, then the previous value will remain in the hidden element and updated to the db. Hence, nothing will actually change in the photo column.
I strongly recommend this tutorial for MVC beginners.
Related
I have created a dropdownlist by fetching data from database.I want to display the selected value on click of submit. In controller I am trying to store the selected value in ViewBag and display it. When I debugged the code, I came to know that viewbag stores null value.The following line stores the value in viewbag.
ViewBag.scode = emp.Service_Code;
While debugging, Service_Code shows the value but it gets stored as null in ViewBag. Please help me in solving this issue.
Model
public class Employee
{
public int Service_Code { get; set; }
public string Service_Name { get; set; }
public IEnumerable<SelectListItem> ser_code { get; set; }
}
View
#model mvclearn.Models.Employee
#{
ViewBag.Title = "menu";
}
<link href="~/Content/bootstrap.css" rel="stylesheet" />
<div class="container">
#using (Html.BeginForm("save", "Test", FormMethod.Post))
{
#Html.DropDownListFor(m => m.Service_Code, Model.ser_code, "--select-",new { #class = "form-control" })
<input type="submit" value="submit" class="btn-block" />
}
</div>
<div>You entered:#ViewBag.scode</div>
Controller
public ActionResult menu()
{
RevenueDashboardEntities rdb = new RevenueDashboardEntities();
var model = new Employee()
{
ser_code = new SelectList(db.Services, "Service_Code", "Service_Name")
};
return View(model);
}
[HttpPost]
public ActionResult save(Employee emp)
{
RevenueDashboardEntities rdb = new RevenueDashboardEntities();
ViewBag.scode = emp.Service_Code;
return View("menu");
}
The selected value is already getting post in the action via model in Service_Code property of it.
What you need here is return your model back to view and it will populate the selected value with what was selected at form post:
[HttpPost]
public ActionResult save(Employee emp)
{
RevenueDashboardEntities rdb = new RevenueDashboardEntities();
// this is needed to populate the items of dropdown list again
emp.ser_code = new SelectList(db.Services, "Service_Code", "Service_Name");
// sending model back to view
return View("menu",emp);
}
Now the value will be auto selected on page load after form is posted and you can display the value on the page inside div by writing:
<div>You entered: #Model.Service_Code</div>
I hope my title is understandable. I will try to be as clear as possible. So here's what I am planning to do. I have a Customer Info page which they can upload their documents, in the page, there is two input files which they can upload their documents and then the moment they click submit, I want to fill the Photo and PassportNo variable with the path string so I could retrieve the file later.
What I am struggle with currently is that I manage to upload the files to the system, but I am kinda stuck with updating the filePath into the Photo and PassportNo properties. I can update one of them however I dont know how to retrieve two Input Files. What I meant is that, when the Form Submit, I dont know how to retrieve which type of Input Types are coming from the form (Photo or Passport No).
I want to retrieve the "name" from the Input to the Controller, so in the Controller I can do something like "If its coming from the "name", then do certain update". Hope I am clear enough, if there are other suggestion please feel free to suggest.
Thanks!
Customer Model
public int Id { get; set; }
public string Name { get; set; }
public string Photo { get; set; }
public string PassportNo { get; set; }
View
#using (Html.BeginForm("UploadFiles", "Customer", FormMethod.Post, new {enctype= "multipart/form-data"}))
{
<input type="file" name="Photo" />
<input type="file" name="PassportNo" />
<input type="submit" name="submit" value="Upload Files" />
}
Controller
[HttpPost]
public ActionResult UploadFiles(HttpPostedFileBase Photo)
{
if (Photo.ContentLength > 0)
{
var filename = Path.GetFileName(Photo.FileName);
var path = Path.Combine(Server.MapPath("~/Files/"), filename);
Photo.SaveAs(path);
}
return RedirectToAction("FileForm");
}
just add second parameter to the action
public ActionResult UploadFiles(HttpPostedFileBase Photo, HttpPostedFileBase PassportNo)
I'm creating a form for register new users, each user is allowed to have many address. (many address to one user). I found this article but doesn't look right for me, because I have a main property which is user and the address has to be related with it. I would like to create something similar for add many address even in the create screen for new user, which doesn't exist the user primary key for these address be related.
http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
Could someone send an example how is the better way to do it?
this is something very close to my code:
public class Person{
public int PersonId { get; set; }
public string Name { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
}
public class Address{
public int AddressId { get; set; }
public string Street { get; set; }
[ScriptIgnore]
public virtual Person Person { get; set; }
}
As Klors indicated, create a view models for Address and Person (suggest List<AddressVM> is initialized in PersonVM constructor). In your PersonController
[HttpGet]
public ActionResult Create()
{
PersonVM model = new PersonVM();
model.Addresses.Add(new AddressVM()); // add an empty address assuming at least one required
return View(model, "Edit")
}
[HttpGet]
public ActionResult Edit(int ID)
{
PersonVM model = // get from database/repository
// If Address.Count == 0, add new Address
return View(model);
}
[HttpPost]
public ActionResult Edit(PersonVM model)
{
...
In your view
#model PersonVM
#using (Html.BeginForm("Edit", "Person") {
....
#Html.HiddenFor(m => m.ID)
#Html.TextBoxFor(m => m.Name)
...
#Html.EditorFor(m => m.Addresses) // you may want to create an editor template for AddressVM
<button type="button" id="AddAddress">Add address</button>
The EditorFor() will generate HTML similar to this (note the indexer)
<input type="text" name="Address[0].Street" id="Address_0__Street" ..../>
<input type="text" name="Address[0].Suburb" id="Address_0__Suburb" ..../>
To dynamically add new addresses, you need to use JavaScript to generate similar HTML, but increment the indexer. If you use an editor template, you can wrap the Address controls in a container (say <div class="address">) which make them easy to select as per the script below
Script to dynamically add a new address
$('#AddAddress').click(function() {
var addresses = $('.address');
// Get the number of existing address
var count = addresses.length;
// Get the first address and clone it
var clone = addresses.first().clone();
// Update the index of the clone
clone.html($(clone).html().replace(/\[0\]/g, '[' + count + ']'));
clone.html($(clone).html().replace(/"_0__"/g, '_' + count + '__'));
// Add to the DOM
addresses.last().after(clone);
}
Note this will also clone the values from the first address, so you may want to reset some or all of them, for example (after its been added)
clone.find('input').val('');
If you're using #Html.ValidationMessageFor() methods, note that dynamically added elements will not be validated in the browser unless you parse the form. An example of how to do that can be found here: jquery.validate.unobtrusive not working with dynamic injected elements
Something like this might be closer to what you need. You'll need to imagine the checkboxes are collections of fields for entering your address.
However, if you create a ViewModel for Person that contains a list of ViewModels for Address, you can create strongly typed editor templates and display templates named the same as the ViewModels and they'll automatically be picked up if you use #Html.EditorFor and #Html.DisplayFor which makes working with one to many's easier.
If you had files like so -
~/Models/PersonViewModel.cs
~/Models/AddressViewModel.cs
~/Views/Person/Edit.cshtml
~/Views/Shared/DisplayTemplates/AddressViewModel.cshtml
~/Views/Shared/EditorTemplates/AddressViewModel.cshtml
and a person ViewModel a bit like
public class PersonViewModel
{
public int Id { get; set; }
public string Name { get; set; }
...
public List<AddressViewModel> Addresses { get; set; }
}
If you then have an edit view for person like
#model PersonViewModel
<div>
#using (Html.BeginForm("Edit", "Person", new {id = Model.Id}, FormMethod.Post))
{
<div>
#Html.EditorForModel()
</div>
<div>
<p>#Html.DisplayNameFor(p => p.Addresses)</p>
#Html.EditorFor(p => p.Addresses)
</div>
<p>
<input type="submit" value="Save"/>
</p>
}
</div>
then the editor template should get picked up for the AddressViewModel once for each entry in the list. You'll have to add in some Ajax to allow new addresses to be created like in your example link. As long as the template contains all the fields for the AddressViewModel to work, then your Edit POST controller should just receive a PersonViewModel back as it's parameter.
There are some parts missing from my example, but you should be able to fill in the blanks from tutorials.
I have a model with two entities (linked with a foreign key) and each entity has its own tab rendered using a partial view. Each tab also has its own Ajax form. When I save the entity in the first tab I now have the ID of the entity which I want to return to the two partial views in order to enable the saving of the second entity or saving updates to the first entity. I cannot get this value back to the view.
The model:
public class Entity1
{
int ID1 { get; set; }
[Some attributes]
string field1 { get; set; }
}
public class Entity2
{
int ID2 { get; set; }
[Some attributes]
string field2 { get; set; }
}
public class MyModel
{
Entity1 entity1 = new Entity1()
Entity2 entity2 = new Entity2()
}
The controller:
public class MyController : Controller
{
[HttpGet]
public ActionResult Index()
{
var model = new MyModel();
model.entity1.ID1 = 0;
model.entity2.ID2 = 0;
return PartialView(model);
}
[HttpPost]
public ActionResult Index(MyModel model)
{
SaveMyModel(model)
// have tried ModelState.Clear(); here
return PartialView(model);
}
}
And finally one of the two partial views
#model MyModel
#using (Ajax.BeginForm("Index", "Home",
new AjaxOptions
{
HttpMethod = "POST"
}
))
{
#Html.LabelFor(m => m.Entity1.field1)
#Html.EditorFor(m => m.Entity1.field1)
#Html.HiddenFor(m => m.Entity1.ID1)
<div class="form-actions">
<button type="submit">
Next section</button>
</div>
}
My save function either inserts or updates depending on the value of ID1.
The problem is that the values of ID1 always stays at zero and the hidden field is not refreshed on the return. I have tried single stepping through the razor refresh and the correct ID is being sent to the view.
The above is a simplification but it does encapsulate the problem.
Thank you in advance.
UPDATE
I can get this to work if:
I only have a single entity in my model
I add ModelState.Clear(); before the save
I was running into the same issue on my project. The only way for me to resolve it was to not include the id when the it was 0. That way when it came back the id was replaced. So in your example you would do the following:
#model MyModel
#using (Ajax.BeginForm("Index", "Home",
new AjaxOptions
{
HttpMethod = "POST"
}
))
{
#Html.LabelFor(m => m.Entity1.field1)
#Html.EditorFor(m => m.Entity1.field1)
#if(Model.Entity1.ID1 !=0){
Html.HiddenFor(m => m.Entity1.ID1)
}
<div class="form-actions">
<button type="submit">
Next section</button>
</div>
}
You need to remove the value from the ModelState if you intend to modify it in your POST controller action:
ModelState.Remove("Entity1.ID1");
This way you don't need to clear the entire ModelState using ModelState.Clear but only the value you are actually modifying. This way the Html helper will pick the value from your model and not the one in the ModelState.
Right guys. I need your brains as I can't find a way to do this properly.
I have a view model:
public class EditUserViewModel
{
public User User;
public IQueryable<ServiceLicense> ServiceLicenses;
}
User is unimportant as I know how to deal with it.
ServiceLicenses has the following implementation:
public class ServiceLicense
{
public Guid ServiceId { get; set; }
public string ServiceName { get; set; }
public bool GotLic { get; set; }
}
Getting a checked list of users is cool. It works like a charm.
<fieldset>
<legend>Licenses</legend>
#foreach (var service in Model.ServiceLicenses)
{
<p>
#Html.CheckBoxFor(x => service.GotLic)
#service.ServiceName
</p>
}
</fieldset>
The problem I'm having is getting the updated ServiceLicenses object with new checked services back to the HttpPost in my controller. For simplicity lets say it looks like this:
[HttpPost]
public ActionResult EditUser(Guid id, FormCollection collection)
{
var userModel = new EditUserViewModel(id);
if (TryUpdateModel(userModel))
{
//This is fine and I know what to do with this
var editUser = userModel.User;
//This does not update
var serviceLicenses = userModel.ServiceLicenses;
return RedirectToAction("Details", new { id = editUser.ClientId });
}
else
{
return View(userModel);
}
}
I know I am using CheckBox the wrong way. What do I need to change to get serviceLicenses to update with the boxes checked in the form?
i understand that ServiceLicenses property is a collection and you want MVC binder to bind it to you action parameters property. for that you should have indices attached with inputs in your view e.g
<input type="checkbox" name = "ServiceLicenses[0].GotLic" value="true"/>
<input type="checkbox" name = "ServiceLicenses[1].GotLic" value="true"/>
<input type="checkbox" name = "ServiceLicenses[2].GotLic" value="true"/>
Prefix may not be mandatory but it is very handy when binding collection property of action method parameter. for that purpose i would suggest using for loop instead of foreach and using Html.CheckBox helper instead of Html.CheckBoxFor
<fieldset>
<legend>Licenses</legend>
#for (int i=0;i<Model.ServiceLicenses.Count;i++)
{
<p>
#Html.CheckBox("ServiceLicenses["+i+"].GotLic",ServiceLicenses[i].GotLic)
#Html.CheckBox("ServiceLicenses["+i+"].ServiceName",ServiceLicenses[i].ServiceName)//you would want to bind name of service in case model is invalid you can pass on same model to view
#service.ServiceName
</p>
}
</fieldset>
Not using strongly typed helper is just a personal preference here. if you do not want to index your inputs like this you can also have a look at this great post by steve senderson
Edit: i have blogged about creating master detail form on asp.net mvc3 which is relevant in case of list binding as well.