i have two models "user" (id,username,address) and "sales" (id,descriprion,userid)
user can have multiple sales (one to many relationship)
and i want to add user information from mvc razor form and i also want to send sales information to controller from my form.
my question is what is the best way to do this task
I am using grid and a small form to add sales in a grid and want to post that grid with user information. i dont know how to do this.
please suggest me the best way to do this task. or the way that i am using how to post sales also with user information.
Here is the way i want to post this form
Click Image to see
If you need to insert multi description for each user I think you have to create a list of hidden input(I use jquery you can choose another plugin or pure javascripts)
<form>
<input id="username" name="username" />
<input type="button" value="Add" id="btnAdd" />
<table id="saleTable">
<tbody></tbody>
</table>
<input type="submit" value="submit" />
<div id="inputContainer">
</div>
</form>
<script>
var index = 0;
$("#btnAdd").on("click", function () {
var saleDesc = $("#saleDesc").val();
$("#inputContainer").append('<input type="hidden" value="' + saleDesc + '" name="Sales[' + index + '].Description" />');
$("#saleTable tbody").append('<tr><td> ' + saleDesc + '</td></tr>');
index++;
});
</script>
the code above will work with following ViewModel
public class User
{
public long Id { get; set; }
public string Username { get; set; }
public List<Sale> Sales { get; set; }
}
public class Sale
{
public long Id { get; set; }
public string Description { get; set; }
}
And after you click on Submit button you receive data in controller
public ActionResult Receive(User model)
{
//Save data...
return View()
}
Related
I am using MVC to display a simple form in a view:
ViewModel:
public class CreateSaleViewModel
{
public string OrderId { get; set; }
public decimal TotalAmount { get; set; }
public bool ShowInstoreConfirmDetails { get; set; }
}
Controller action:
[HttpGet]
public IActionResult CreateSale()
{
return View(new CreateSaleViewModel());
}
View:
#model CreateSaleViewModel
<form asp-controller="Sales" asp-action="CreateSale" method="post">
<input asp-for="OrderId" />
<input asp-for="TotalAmount" />
<button type="submit" name="CreateSale" id="CreateSale">
button
</button>
</form>
I then post to a new view, where the same details need to be entered. To do this I store the old values in hidden inputs and provide another form to re-enter the details.
ViewModel:
public class ConfirmDetailsViewModel
{
public string OrderId { get; set; }
public decimal TotalAmount { get; set; }
public string ConfirmOrderId { get; set; }
public decimal ConfirmTotalAmount { get; set; }
}
Controller:
[HttpPost("Confirmdetails")]
public IActionResult ConfirmDetails(CreateSaleViewModel model)
{
var viewModel = new ConfirmDetailsViewModel
{
ConfirmOrderId = model.OrderId,
ConfirmTotalAmount = model.TotalAmount,
OrderId = string.Empty,
TotalAmount = 0.0m
};
return View("ConfirmDetails", viewModel);
}
View:
#model ConfirmDetailsViewModel
<form asp-controller="Sales" asp-action="Summary" method="post">
<input type="hidden" value="#Model.ConfirmOrderId" id="OrderIdConfirm" />
<input type="hidden" value="#Model.ConfirmTotalAmount" id="TotalAmountConfirm" />
<input type="hidden" value="#Model.OrderId" id="banana" />
<input asp-for="OrderId" />
<input asp-for="TotalAmount" />
<button type="submit" name="CreateSale" id="CreateSale">
button
</button>
</form>
My problem is on the confirmdetails view orderId and TotalAmount retain the values that were posted from the previous page.
I have debugged the controller and can see the ConfirmOrderId and ConfirmTotalAmount properties have the correct values, and also OrderId and TotalAmount are empty strign and 0 respectively.
Even stranger is that
<input type="hidden" value="#Model.OrderId" id="banana" />
Has the correct value of "".
Does anyone know what is causing this issue?
MVC stores the posted back values in ModelState.
These values are used by default in #Html helpers - as a convenience. This allows the values of hidden form fields to be preserved through postbacks, even if they don't have properties in the view-model.
Unfortunately what is usually a convenience turns into a headache, if you try to modify the model's properties within the action. Helpers take their values from ModelState, ignoring the updated view-model.
To solve this, call ModelState.Clear()
removes all the posted back values from ModelState
the helpers will now use the values from the view-model.
Controller:
[HttpPost]
public IActionResult ConfirmDetails(CreateSaleViewModel model)
{
var viewModel = new ConfirmDetailsViewModel
{
ConfirmOrderId = model.OrderId,
...
};
ModelState.Clear(); // force asp-helpers to use the updated model's values
return View("ConfirmDetails", viewModel);
}
I'm working in asp.net core inside a MVC application. I'm using the scaffolding feature that creates the views and controller based on a model. Below is the model that i'm using:
class ShoppingList
{
public int ShoppingListId { get; set; }
public string Name { get; set; }
public List<string> ListItems { get; set; }
}
The form that displays to the user via the view only displays the field for Name. I would like the form to be able to show a field for a list item, and then if the user wants to add another list item they can hit a button to add another field to do so. They at run time decide how many shopping list items they want to add.
Here is the razor cshtml form i'm using:
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
Is there an easy way to do this? I don't want to have to hard code a number.
If you want to allow the user to add a new form element on the client side you need to use javascript to update the DOM with the new element you want to add. To list the existing items you may use editor templates. Mixing these 2 will give you a dynamic form. The below is a basic implementation.
To use editor templates, we need to create an editor template for the property type. I would not do that for string type which is more like a generic one. I would create a custom class to represent the list item.
public class Item
{
public string Name { set; get; }
}
public class ShoppingList
{
public int ShoppingListId { get; set; }
public string Name { get; set; }
public List<Item> ListItems { get; set; }
public ShoppingList()
{
this.ListItems=new List<Item>();
}
}
Now, Create a directory called EditorTemplates under ~/Views/YourControllerName or ~/Views/Shared/ and create a view called Item.cshtml which will have the below code
#model YourNameSpaceHere.Item
<input type="text" asp-for="Name" class="items" />
Now in your GET controller, create an object of the ShoppingList and send to the view.
public IActionResult ShoppingList()
{
var vm = new ShoppingList() { };
return View(vm);
}
Now in the main view, All you have to do is call the EditorFor method
#model YourNamespace.ShoppingList
<form asp-action="ShoppingList" method="post">
<input asp-for="Name" class="form-control" />
<div class="form-group" id="item-list">
Add
#Html.EditorFor(f => f.ListItems)
</div>
<input type="submit" value="Create" class="btn btn-default" />
</form>
The markup has an anchor tag for adding new items. So when user clicks on it, we need to add a new input element with the name attribute value in the format ListItems[indexValue].Name
$(function () {
$("#add").click(function (e) {
e.preventDefault();
var i = $(".items").length;
var n = '<input type="text" class="items" name="ListItems[' + i + '].Name" />';
$("#item-list").append(n);
});
});
So when user clicks it adds a new input element with the correct name to the DOM and when you click the submit button model binding will work fine as we have the correct name attribute value for the inputs.
[HttpPost]
public IActionResult ShoppingList(ShoppingList model)
{
//check model.ListItems
// to do : return something
}
If you want to preload some existing items (for edit screen etc), All you have to do is load the ListItems property and the editor template will take care of rendering the input elements for each item with correct name attribute value.
public IActionResult ShoppingList()
{
var vm = new ShoppingList();
vm.ListItems = new List<Item>() { new Item { Name = "apple" } }
return View(vm);
}
First this is you must have a public accessor to your ShoppingList class.
So, public class ShoppingList.
Next is your view will need the following changes.
#model ShoppingList
<h1>#Model.Name</h1>
<h2>#Model.ShoppingListId</h2>
foreach(var item in Model.ListItems)
{
<h3>#item</h3>
}
So, the above code is roughly what you are looking for.
In Razor you can accessor the models variables by using the #model at the top of the view. But one thing you need to note is if your model is in a subfolder you'll need to dot into that.
Here's an example: #model BethanysPieShop.Models.ShoppingCart.
Here BethanysPieShop is my project name, Models is my folder the ShoppingCart class is in.
The form is defined in Add.cshtml:
#using bendinsnMusicApp.Models
#model Album
<h2>Add Album</h2>
<form action="Add" method="post">
Title: <input type="text" name="Title" value="" autofocus />
<span asp-validation-for="Title" class="text-danger"></span><br />
Price: <input type="number" name="Price" value="" />
<span asp-validation-for="Price" class="text-danger"></span><br />
Artist: <select name="Artist">
#foreach (Artist artist in ViewBag.Artists)
{
<option value=#artist.ArtistID>#artist.Name</option>
}
</select>
<span asp-validation-for="Artist"></span><br />
Genre: <select name="Genre">
#foreach (Genre genre in ViewBag.Genres)
{
<option value=#genre.GenreID>#genre.Name</option>
}
</select>
<span asp-validation-for="Genre"></span><br />
<input type="submit" value="Submit" />
</form>
The error occurs in the following action in my controller, AlbumsController.cs:
[HttpPost]
public IActionResult Add(Album a)
{
var albums = _context.Albums;
albums.Add(a);
_context.SaveChanges();
var albums2 = _context.Genres.ToList();
ViewBag.Artists = _context.Artists.ToList();
ViewBag.Genres = _context.Genres.ToList();
return View("Index", albums2);
}
Specifically, when attempting to save the changes, the update fails because the foreign keys of the Album object, ArtistID and GenreID, are both 0 every time, and Artist and Genre are both null. For completeness, here is the Album class:
public class Album
{
public int AlbumID { get; set; }
public string Title { get; set; }
[Required]
[DataType(DataType.Currency)]
public decimal Price { get; set; }
public int ArtistID { get; set; }
public Artist Artist { get; set; }
public int GenreID { get; set; }
public Genre Genre { get; set; }
}
It seems that some error is preventing the form from properly submitting the Artist and Genre fields, both of which are select elements. Further, I expect that even if the actual content of those elements was submitted, it would not include the FK values.
Note that ViewBag.Artists and ViewBag.Genres are defined as _context.Artists.ToList() and _context.Genres.ToList() respectively in the action to render the view. Each Genre and Artist includes a Name and [class]ID, and Artist has an associated text field called Bio as well.
The Albums table includes fields for AlbumID, ArtistID, GenreID, Price, and Title. The first three are ints, the second is a decimal, and the last is nvarchar.
What needs to be done to:
A) Submit the content of the select elements along with the rest of the form?
B) Use the ArtistID and GenreID values (ints in their respective classes) as arguments for filling the Albums table?
The name of each select input needs to correspond to the field in the Model that the value will fill in when the Model Binding occurs.
Change the code:
<select name="Artist">
to
<select name="ArtistID">
and the same for the Genre select, name="GenreID"
That will allow the Model Binding to pick up those values correctly, and then you can use them in the Controller as you need.
This question already has an answer here:
Adding users to a list MVC
(1 answer)
Closed 7 years ago.
I'm working a a project where I want to Add users to a list when a button is clicked.
The user can input name and wage before clicking on the button.
The question is: How do I call the Controller method and display the list when the button is clicked?
Controller:
namespace TimeIsMoney.Controllers
{
public class HomeController : Controller
{
List<UserModel> list = new List<UserModel>();
public ActionResult Index()
{
return View();
}
public ActionResult AddUser(UserModel user)
{
var list = Session["myUsers"] as List<UserModel>;
list.Add(user);
return View(list);
}
}
}
View:
<div class="col-md-12 row">
<form >
<input type="text" placeholder="Name" />
<input type="number" placeholder="Hourly wage" />
<input type="button" value="Add" onclick="window.location.href='#Url.Action("AddUser")'" />
</form>
</div>
#*Here is where the list is supposed to be displayed*#
Model:
namespace TimeIsMoney.Models
{
public class UserModel
{
[Required]
[DataType(DataType.Text)]
[DisplayName("Username")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Text)]
[DisplayName("Wage")]
public string Wage { get; set; }
}
}
Put your fields inside a form tag and post the form to the AddUser action method
Update your Index view like this. You can see that this view is strongly typed to the UserModel
#model TimeIsMoney.Models.UserModel
#using(Html.BeginForm("AddUser","Home")
{
#Html.TextBoxFor(s=>s.UserName)
#Html.TextBoxFor(s=>s.Wage)
<input type="submit"/ >
}
If you do not want to post the entire form but show the list in the same page without refreshing the page, you may use jQuery and ajax to post the data and get the response and use it for displaying.
We need to listen for the click event of the submit button and prevent the default behavior (posting the form), serialize the form and send it via $.post method. When we get a response. Use html method to update the inner html of the div with id userList
#model TimeIsMoney.Models.UserModel
#using(Html.BeginForm("AddUser","Home")
{
#Html.TextBoxFor(s=>s.UserName)
#Html.TextBoxFor(s=>s.Wage)
<input id="btnSubmit" type="submit"/ >
}
<div id="userList" />
#section Scripts
{
<script>
$(function(){
$("#btnSubmit").click(function(e){
e.preventDefault();
var _form=$(this).closest("form");
$.post(_form.attr("action"),_form.serialize(),function(response){
$("#userList").html(response);
});
});
});
</script>
}
Make sure your AddUser method returns a view without layout.
you may go to the AddUser.cshtml view and set the Layout to null.
#{
Layout=null;
}
Note: I'm using MVC3+Razor, EF4, CF-CTP5
How can you allow the view to have the ability to add multiple Address classes per Organization dynamically on the client, and bound strongly to the model on post?
How can you have the view parse values in the model if the (ModelState.IsValid == false) such that if you enter 3 addresses and post an invalid model, it re-populates the number addresses and with their appropriate values?
Here are my models:
public class Organization
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
public virtual ICollection<PhoneNumber> PhoneNumbers { get; set; }
...
}
public class Address
{
public int Id { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
public int Type { get; set; }
}
I'm trying to figure out how you can have the Create action for Organization (/Organization/Create) handle the create like thus (such that addresses and phone numbers are part of the submitted model):
[HttpPost]
public ActionResult Create(Organization organization)
{
if (ModelState.IsValid)
{
_db.Organizations.Add(organization);
_db.SaveChanges();
return RedirectToAction("Details", organization.Id);
}
return View(organization);
}
Your question is quite vaste :)
This is just one of the way your requirement can be achieved and I am sure there are better than mine.
I am going to start from your second question:
How can you have the view parse values
in the model if the
(ModelState.IsValid == false) such
that if you enter 3 addresses and post
an invalid model, it re-populates the
number addresses and with their
appropriate values?
If I correctly understand your request it looks very simple to me. The answer is simply code your view to render a Model class content and return the invalid model to the client exactly as you are doing in your Create action.
If your form (and its fields) have been decorated with the ValidationSummary/ValidationMessage html helpers, you are going to see also validation messages.
How can you allow the view to have the ability to add multiple Address
classes per Organization dynamically
on the client, and bound strongly to
the model on post?
You can have a main view showing Organization attributes and then have another view showing related addresses. Here you can place a hyperlink or a button that open a dialog for adding a new address object and then refresh the address list when done. At the same way you can have edit and delete buttons as icons on the list.
The address list is a piece of markup completely handled at client side that, to be correctly binded to the server side Model class should adhere to some simple naming rules for it's input attributes.
To make the Default Model Binder class bind correctly your form use the following snippet for your Organization class
#using (Html.BeginForm()) {
#Html.HiddenFor(o => o.Id)
#Html.ValidationSummary( true )
<fieldset>
<legend>My Organization</legend>
<div class="editor-label">#Html.LabelFor( model => model.Name )</div>
<div class="editor-field">
#Html.EditorFor( model => model.Name )
#Html.ValidationMessageFor( model => model.Name )
</div>
<br />
<div id="container">
<div>Address List</div>
#foreach (Address a in Model.Addresses ) {
Html.EditorFor(a);
}
</div>
<div style="text-align:right;margin-top:14px;">
<input type="submit" id="btnSubmit" value="Save" />
</div>
</fieldset>
}
To be automatically bindable the resultant code for the form should look as the following
<form action="..." id="..." method="post">
<input type="hidden" name="Id" value="2">
<input type="hidden" name="Name" value="Acme Corporation">
<!-- markup for each address -->
<input type="hidden" name="Addresses[0].Id" value="1">
<input type="hidden" name="Addresses[0].Line1" value="Line 1">
<input type="hidden" name="Addresses[0].Line2" value="Line 2">
... and so on...
</form>
having it's properties named as Addresses[index].PropertyName.
If you add new addresses on the client it does'nt matter so much: as long as your code respect this rule you can have the default Model Binder do the job for you.
Hope this helps
I'm not sure if I understand your question correctly but with respect to question 1 I think you are looking for a ViewModel. Like this perhaps..
OrganizationViewModel.cs
public class OrganizationViewModel
{
public OrganizationViewModel(Organization org, IList<Address> addresses)
{
this.Organization = org;
this.Addresses = addresses
}
public Organization Organization {get;set;}
public IList<Address> Addresses {get;set;}
}
OrganizationController.cs
public class OrganizationController : Controller
{
private readonly IOrganizationService _organizationService: //or whatever method you use
public OrganizationController(IOrganizationService orgService)
{
this._organizationService = orgService;
}
public ActionResult Item(int id)
{
var org = _organizationService.GetOrganizationById(id);
var addresses = _organizationService.GetOrgAddressesByOrgId(id);
return View(new OrganizationViewModel(program, addresses));
}
}
Item.cshtml
#model OrganizationViewModel
<h1>#Model.Organization.Name</h1>
<ul>
#foreach(var a in Model.Addresses)
{
<li>#a.Line1</li>
<li>#a.Line2</li>}
</ul>
Before I try and answer number 2 maybe you should indicate whether I am correctly understanding question 1. Hope this helps.
I managed to do this using LINQ to SQL. Now I'm trying to use Entity Framework instead, but it really makes everything more complicated. So I don't have a solution for you, but perhaps my L2S solution might help?
Using models generated from my database I could in my view do this:
#for (int i = 0; i < Model.Contact.EmailAddresses.Count; ++i)
{
<li>
#Html.TextBoxFor(x => x.Contact.EmailAddresses[i].EmailAddress)
#Html.HiddenFor(x => x.Contact.EmailAddresses[i].EmailAddressID)
</li>
}
I had a view model class:
class ContactViewModel
{
Contact contact { get; set; }
}
This worked fine and in my controller action I got my Contact object with it's Contact.ContactEmailAddresses list filled just like I expected.
But with EF, I cannot use the [i] on the EmailAddresses property generated from the database anymore. The best I have come up with is:
#Html.TextBox("Contact.EmailAddresses[" + i + "].EmailAddress", Model.Contact.EmailAddresses.ElementAt(i).EmailAddress)