MVC 4 - Centralize the create action - c#

I'll try to be clear. I'm developing a web application based on MVC 4 and Entity Framework. Through this app, I can create some Products which is depending on an other table which is Product Types As you can see, in my create product View, I have a dropdownlist which contains the product types :
#model BuSIMaterial.Models.Product
#{
ViewBag.Title = "Create";
}
<h2>
Create</h2>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Product</legend>
<div class="editor-label">
Purchase date :
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.PurchaseDate, new { #class = "datepicker"})
#Html.ValidationMessageFor(model => model.PurchaseDate)
</div>
<div class="editor-label">
Serial number :
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.SerialNumber, new { maxlength = 50 })
#Html.ValidationMessageFor(model => model.SerialNumber)
</div>
<div class="editor-label">
Product type :
</div>
<div class="editor-field">
#Html.DropDownList("Id_ProductType", String.Empty)<a href="../ProductType/Create">Add
a new product type?</a>
#Html.ValidationMessageFor(model => model.Id_ProductType)
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">Create</button>
</div>
</fieldset>
}
And in my create product type View, I have a dropdownlist of existing product companies (so the same relation which exists between product and product type :
#model BuSIMaterial.Models.ProductType
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>ProductType</legend>
<div class="editor-label">
Model :
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Model, new { maxlength = 50 })
#Html.ValidationMessageFor(model => model.Model)
</div>
<div class="editor-label">
Catalog Price :
</div>
<div class="editor-field">
#Html.EditorFor(model => model.CatalogPrice)
#Html.ValidationMessageFor(model => model.CatalogPrice)
</div>
<div class="editor-label">
Company :
</div>
<div class="editor-field">
#Html.DropDownList("Id_ProductCompany", String.Empty)<a href ="../ProductCompany/Create" >Add a new company?</a>
#Html.ValidationMessageFor(model => model.Id_ProductCompany)
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">Create</button>
</div>
</fieldset>
}
What I tried is to "mix" these 2 views in one Create Product View. So I think my action will change a little bit. Also, I think I'll have to do 2 adds in my database. Is it the best way to do what I want?
UPDATE : by using a viewmodel, I got this :
My View Model :
public class ProductViewModel
{
[Required]
public Product Product { get; set; }
[Required]
public ProductType ProductType { get; set; }
}
My Create Action :
[HttpPost]
public ActionResult CreateFullProduct(ProductViewModel pvm)
{
ViewBag.Id_ProductCompany = new SelectList(db.ProductCompanies, "Id_ProductCompany", "Name", pvm.ProductType.Id_ProductCompany);
if (ModelState.IsValid)
{
Product product = new Product { PurchaseDate = pvm.Product.PurchaseDate,
SerialNumber = pvm.Product.SerialNumber,
Id_ProductType = pvm.ProductType.Id_ProductType};
ProductType productType = new ProductType {Model = pvm.ProductType.Model,
CatalogPrice = pvm.ProductType.CatalogPrice,
Id_ProductCompany = pvm.ProductType.Id_ProductCompany};
db.ProductTypes.AddObject(productType);
db.Products.AddObject(product);
db.SaveChanges();
return RedirectToAction("Index", "Person");
}
return View(pvm);
}
When I try to save the new entries, I got this issue : The INSERT statement conflicted with the FOREIGN KEY constraint "FK_bm_ProductTypes_bm_ProductCompanies".

As Products are "dependent" on ProductTypes, merging them into one is a good idea. You'll have to merge the post action too, which will have 2 inserts into your database (which is correct, one for Product, one for ProductType.
You'll have to put them both in one Model too so you can use it in your view, something like:
public class ProductViewModel
{
public Product Product { get; set; }
public ProductType ProductType { get; set; }
}
EDIT: Your problem with your saving is because the ProductComany is not being posted (as indiciated in Chat)
To fix that, first we'll put the values for the Dropdown in the model:
public class ProductViewModel
{
public Product Product { get; set; }
public ProductType ProductType { get; set; }
public List<SelectListItem> ProductCompanies { get; set; }
}
Then populate it in your HttpGet and HttpPost by doing:
model.ProductCompanies = db.ProductCompanies
.ToList()
.Select(s => new SelectListItem
{
Text = s.Name,
Value = s.Id.ToString()
})
.ToList();
Then in your view you can do:
#Html.DropDownListFor(m => m.ProductType.Id_ProductCompany, Model.ProductCompanies)

Related

User registration page, with a defined user MODEL (MVC ) [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I have worked with .Net webforms before and now working on a MVC project.
I am working on creating a user registration page. I have no idea how its MODEL should look like.
See database diagram
User registration page:
Firstname
LastName
Email adress
Subcribe newsletter ?
Password
Choose your city
.....
I think I have to do this in this task:
STEP 1:
Populate cities dropdownList (choose your city) with data from database
STEP 2:
Insert users email address in UserEmail table and set subcribe to 0 or 1
STEP 3:
Save user registration data and EmailID (get from UserEmails table), CityID into Users table
Should I make a large MODEL , like this :
Public Class Registration Model
{
Get, set:
all USEREMAIL plugs ties
all Users plugs ties
all Cities plugs ties
}
How to start with this task in MVC?
(I know how to develop this in webforms , but MVC model confuses me)
You were on the right track.You're trying to create a view which let's users complete a registertion form, this view needs to use the user model and at the same type display a list of cities in a drop down which come from a different table in the database but are related to the user.
In MVC there's a concept called viewmodel, all that it is is a simple class which combines one or more models together.
Below I created a model called SiteUser and then another model called SiteUserViewModel which inherits from the user and gives us all the user properties PLUS an additional property we can use to populate the cities.So when we display the page, we will use the SiteUserViewModel but when we post to the controller to save the user in the database we will use SiteUser:
Models:
namespace MVCTutorial.Models
{
public class SiteUser
{
[Display(Name="First Name")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
public string LastName { get; set; }
[DataType(DataType.EmailAddress)]
public string EmailAddress { get; set; }
[Display(Name = "Subscribe To Newsletter?")]
public bool SubscribeToNewsletter { get; set; }
[DataType(DataType.Password)]
public string Password { get; set; }
[Display(Name="City")]
public int CityId { get; set; }
}
public class SiteUserViewModel : SiteUser
{
public List<SelectListItem> Cities { get; set; }
}
}
Controller:
public class SiteUserController : Controller
{
[HttpGet]
public ActionResult Index()
{
var model = new SiteUserViewModel();
//Replace this with logic that reads cities from the database
var city1 = new SelectListItem { Text = "Johannesburg", Value = "1" };
var city2 = new SelectListItem { Text = "Cape Town", Value = "2" };
model.Cities = new List<SelectListItem> { city1, city2 };
return View(model);
}
[HttpPost]
public ActionResult CreateUser(SiteUser user)
{
System.Diagnostics.Debugger.Break();
//Write the code to add user to the database
return View();
}
}
View:
#model MVCTutorial.Models.SiteUserViewModel
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Create User</title>
</head>
<body>
#using (Html.BeginForm("CreateUser", "SiteUser"))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Site User</legend>
<div class="editor-label">
#Html.LabelFor(model => model.FirstName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FirstName)
#Html.ValidationMessageFor(model => model.FirstName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.LastName)
#Html.ValidationMessageFor(model => model.LastName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.EmailAddress)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.EmailAddress)
#Html.ValidationMessageFor(model => model.EmailAddress)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.SubscribeToNewsletter)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.SubscribeToNewsletter)
#Html.ValidationMessageFor(model => model.SubscribeToNewsletter)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Password)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Password)
#Html.ValidationMessageFor(model => model.Password)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.CityId)
</div>
<div class="editor-field">
#Html.DropDownList("CityId", Model.Cities, "Please select one")
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
</body>
</html>
Result:

ASP.NET Entity Manager: create more than one entity in a view

i want to to create a create-view where you have the possibility to enter a value for more than one entity - "a list of entities"
e.g. Entity Class
public class MyEntity{
public string myAttribute { get; set; }
}
For the View I created a ModelView which looks like this:
public class MoreEntites{
public List<MyEntity> MyEntities { get; set; }
}
In the View I want to use MoreEntities to give the user the possibility to enter more datasets than one in just one view (my suggestions which doesnt work of course)
#model myproject.ViewModels.MoreEntities
...
<div class="editor-label">
#Html.LabelFor(model => model.MyEntities.ElementAt(0).MyAttribute)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.MyEntities.ElementAt(0).MyAttribute)
#Html.ValidationMessageFor(model => model.MyEntities.ElementAt(0).MyAttribute)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.MyEntities.ElementAt(1).MyAttribute)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.MyEntities.ElementAt(1).MyAttribute)
#Html.ValidationMessageFor(model => model.MyEntities.ElementAt(1).MyAttribute)
</div>
...
Now in the controller I want to iterate over the list and write every item of MyEntities in the database. When I run the programm I get an exception that my List is null and I should check it for null before I use it.
Is this possible and how does it work? One "Solution" would be to create an Array but in this case my program would be scalable.
Thanks for help!
Works with Array[] of Entity!!!!
Solution:
Model:
public class MyEntity{
public string MyAttribute { get; set; }
}
ViewModel:
public class MoreEntites{
public MyEntity[] MyEntities { get; set; }
}
View:
#model myproject.ViewModels.MoreEntities
...
<div class="editor-label">
#Html.LabelFor(model => model.MyEntities[0].MyAttribute)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.MyEntities.[0].MyAttribute)
#Html.ValidationMessageFor(model => model.MyEntities[0].MyAttribute)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.MyEntities[1].MyAttribute)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.MyEntities[1].MyAttribute)
#Html.ValidationMessageFor(model => model.MyEntities[1].MyAttribute)
</div>
...
controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateMoreEntitiesAtOnce(MoreEntities set)
{
foreach(var item in set.MyEntities)
{
db.MyEntity.Add(item);
}
db.SaveChanges();
return RedirectToAction("Index");
}
in my case if have to try if its possible to add more TextBoxes to the View by JavaScript
Therefore you need to increment the name of the textboxes in plain html when adding them to the DOM

Modelstate validation always return false using a dropdownlist

NOTE: Code updated to take into account the comments and answer made below.
In my MVC app I need sometimes to make references to other objects (like a many-to-many relationship, or one-to-many relationship).
So I have this model:
public class ObjInfo
{
[Required(ErrorMessage = "Obj ID is required.")]
[Display(Name = "ObjID")]
public int m_Id { get; set; }
[Required(ErrorMessage = "Obj Name is required.")]
[Display(Name = "Obj Name")]
public string m_Name { get; set; }
[Display(Name = "Obj Number")]
public int m_Number { get; set; }
(...)
[Required(ErrorMessage = "Other Obj is required.")]
[Display(Name = "OtherObj")]
public int m_OtherObjID { get; set; }
public OtherObjInfo m_OtherObj { get; set; }
(...)
}
I have default and parameters constructors as well and can show them as needed, though I am not sure if they are at fault. Anyway.
In my controller, I have the two create methods following MVC methods:
//
// GET: /Obj/Create
public ActionResult Create()
{
ViewBag.List = new SelectList(PopulateDDLs(), "OtherObj", "Other obj ID");
return View();
}
//
// POST: /Obj/Create
[HttpPost]
public ActionResult Create(Objinfo obj)
{
if (ModelState.IsValid)
{
m_ObjManager.CreateObj(obj);
return RedirectToAction("SearchIndex");
}
ViewBag.List = new SelectList(PopulateDDLs(), "OtherObj", "Other obj ID");
return View(obj);
}
And, finally, here's how my "Create" view is coded:
#model MyApp.Models.ObjInfo
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>OBJ</legend>
<div class="editor-label">
#Html.LabelFor(model => model.m_Id)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.m_Id)
#Html.ValidationMessageFor(model => model.m_Id)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.m_Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.m_Name)
#Html.ValidationMessageFor(model => model.m_Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.m_Number)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.m_Number)
#Html.ValidationMessageFor(model => model.m_Number)
</div>
(...)
<div class="editor-label">
#Html.LabelFor(model => model.m_OtherObj , "Other Obj")
</div>
<div class="editor-field">
#Html.DropDownList(model => model.m_OtherObjID, ViewBag.List as SelectList, "--- Select Other Obj ---", new {#class = "OtherObjInfo "})
#Html.ValidationMessageFor(model => model.m_OtherObj)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Basically, the problem is that each time I click on the "create" button, the ModelState validation is always false for the OtherObj even if something is selected in the dropdownlist. Every other values are correct except this one.
I don't understand why and I would greatly appreciate your help on this one, thank you for your help!
After code edited **
Now I get a crash as I enter the "Create" view:
DataBinding: 'System.String' does not contain a property with the name 'OtherObj'.
Exactly on the line where my dropdownlistfor is located.
The datavalueField and dataTextField are supposed to refer to what exactly?
add otherObjectId to your model
public class ObjInfo
{
public int m_Id { get; set; }
...
[Required(ErrorMessage = "Other Obj is required.")]
[Display(Name = "OtherObj")]
public int otherObjectId { get; set; }
public OtherObjInfo m_OtherObj { get; set; }
...
}
controller
public ActionResult Create()
{
ViewBag.List = new SelectList(PopulateDDLs(), "Id", "Name");
return View();
}
[HttpPost]
public ActionResult Create(Objinfo obj)
{
if (ModelState.IsValid)
{
m_ObjManager.CreateObj(obj);
return RedirectToAction("SearchIndex");
}
ViewBag.List = new SelectList(PopulateDDLs(), "Id", "Name");
return View(obj);
}
view
<div class="editor-label">
#Html.LabelFor(model => model.m_OtherObj , "Other Obj")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.otherObjectId, ViewBag.List as SelectList, "--- Select Category ---", new { #class = "some_class" })
#Html.ValidationMessageFor(model => model.m_OtherObj)
</div>
better way is using strongly typed helpers. All your helpers are strongly-typed (editorfor, textboxfor, dropdownlistfor,...) except dropdownlist.
if you want to bind DDL value to your model, You should use dropdownlistfor instead of dropdownlist.
your model state is not valid, because you dont bind required value as DDL to model.

Fill in form based on dropdown selection (MVC 4)

I am using asp.net mvc 4
Is there a way to update the form based on selection made by the user?
(in this case I want to fill in address fields if something is picked from the dropdown list, otherwise a new address would need to be typed in)
My model:
public class NewCompanyModel
{
[Required]
public string CompanyName { get; set; }
public bool IsSameDayRequired { get; set; }
public int AddressID { get; set; }
public Address RegisterOfficeAddress { get; set; }
}
View:
#model ViewModels.NewCompanyModel
#using (Html.BeginForm(null, null, FormMethod.Post, new { name = "frm", id = "frm" }))
{
#Html.ValidationSummary(true)
<fieldset id="test">
<legend>Company</legend>
<h2>Register office address</h2>
<div class="editor-label">
#Html.LabelFor(model => model.AddressID)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.AddressID, (IList<SelectListItem>)ViewBag.Addresses, new {id = "address", onchange = "window.location.href='/wizard/Address?value=' + this.value;" })
</div>
<div class="editor-label">
#Html.LabelFor(model => model.RegisterOfficeAddress.BuildingNameOrNumber)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.RegisterOfficeAddress.BuildingNameOrNumber)
#Html.ValidationMessageFor(model => model.RegisterOfficeAddress.BuildingNameOrNumber)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.RegisterOfficeAddress.StreetName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.RegisterOfficeAddress.StreetName)
#Html.ValidationMessageFor(model => model.RegisterOfficeAddress.StreetName)
</div>
and controller:
public ActionResult Address(string value)
{
//get the address from db and somehow update the view
}
The question is how do you update the 'model.RegisterOfficeAddress.StreetName' etc
Just to make clear this is just part of the form so I cannot submit it just yet.
Many thanks
Thanks for your help; I have decided to take a different approach:
On dropdown change I submit the form:
<div class="editor-label">
#Html.LabelFor(model => model.ServiceAddress.AddressID)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.ServiceAddress.AddressID, (IEnumerable<SelectListItem>)ViewBag.Addresses, new { onchange = "this.form.submit();" })
#Html.ValidationMessageFor(model => model.ServiceAddress.AddressID)
</div>
and then in controller:
[HttpPost]
public ActionResult NewDirector(NewDirectorVM vm, string value)
{
ModelState.Clear();
if (vm.ServiceAddress.AddressID > 0)
{
//Updates the properties of the viewModel
vm.ServiceAddress = _Repository.GetAddress(vm.ServiceAddress.AddressID);
}
return View("NewDirector", vm);
}
Please notice ModelState.Clear(); which actually allows the view to be updated from the controller (otherwise all the changes made the the viewModel by the controller would have been overwritten by the values in the view).
Common way in such cases is to update other fields via javascript:
$('##Html.IdFor(model => model.AddressID)').on('change',function(){
$.get(...,function(data){
$('##Html.IdFor(model => model.RegisterOfficeAddress.BuildingNameOrNumber)').val(data)
})
})

Add Category Dynamically ASP.Net 4 MVC3 C#

I am working on creating a blog with ASP.Net 4, MVC 3, Razor and C#.
There are 2 seperate tables. 1 for the actual blog post and a relationship table for categories.
The categories displays as a dropdown.
I want to add the ability to add a new category using Ajax so the user does not lose what they have already entered into the form.
What would be the best way to accomplish this?
Here is what I have right now.
Controller Code
public ActionResult Create()
{
ViewBag.category_id = new SelectList(_db.Categories, "id", "category_name");
return View();
}
Razor View
#model NPP.Models.News
#{
ViewBag.Title = "Create News Item";
}
<h2>Create News Item</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>News</legend>
<div class="editor-label">
#Html.LabelFor(model => model.news_title, "Title")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.news_title)
#Html.ValidationMessageFor(model => model.news_title)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.news_content, "Content")
</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.news_content)
#Html.ValidationMessageFor(model => model.news_content)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.news_teaser, "Teaser (optional)")
</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.news_teaser)
#Html.ValidationMessageFor(model => model.news_teaser)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.category_id, "Category")
</div>
<div class="editor-field">
#Html.DropDownList("category_id", String.Empty)
#Html.ValidationMessageFor(model => model.category_id)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Thanks for any help in advance. My layout page includes jquery which I would prefer to use.
Add another controler method to return you a list of categories, something like:
public JsonResult Categories()
{
return Json(DB.GetCategorys(), JsonRequestBehavior.AllowGet);
}
Then on the client side, use ajax to get your categories and bind them to your drop down, something like:
$.ajax({
url: 'http://myserver/myapp/mycontroller/Categories',
success: function(data) {
$('#dropCategorys').html('');
$.each(data, function(i, e) {
$('#dropCategorys').append('<option value="' +
e.category_id + '">' + e.category_name + '</option>');
}
}
});
This won't save your current selected item, but you can always check that before clearing the list, and reset it afterwards.
Creating the Category separately via AJAX is not your only option. You could then have a view model like this:
public class CategoryViewModel
{
public string name { get; set; }
public int id { get; set; }
}
public class CreateNewsViewModel
{
public string news_title { get; set; }
public string news_content { get; set; }
public string news_teaser { get; set; }
public string CategoryViewModel category { get; set; }
}
Change your view at the category field:
<div class="editor-label">
#Html.LabelFor(model => model.category, "Category")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.category.id, ViewBag.category_id)
#Html.EditorFor(model => model.category.name) <!-- only show when creating a new category -->
#Html.ValidationMessageFor(model => model.category)
</div>
Then your action would look something like this:
[HttpPost, ActionName("Create")]
public ActionResult DoCreate(CreateNewsViewModel model)
{
if (ModelState.IsValid)
{
if (model.category.id == 0)
{
// create your new category using model.category.name
}
// create an entity from the model and save to your database
return RedirectToAction("Index", "News"); // do whatever you wish when you're done
}
return View("Create", model); // show Create view again if validation failed
}
This is more or less off the top of my head so let me know if I bollocks'ed any parts up.

Categories