Binding List in View Model using ASP.NET Model Binding - list

I'm working on an application to track orders that are coming through a web application. The application won't sell products, and it's designed to track orders as they come in via phone.
I'm still new to ASP.NET MVC, so I'm still learning the ropes. I'm trying to use model binding to get this to work.
Here's my view model:
public class OrderDetailViewModel
{
public Order Order { get; set; }
public List<OrderLine> OrderLinesList { get; set; }
public OrderDetailViewModel()
{
this.Order = new Order();
List<OrderLine> OrderLines = new List<OrderLine>();
OrderLines = new List<OrderLine>();
OrderLines.Add(new OrderLine());
this.OrderLinesList = OrderLines;
}
public OrderDetailViewModel(Order order)
{
this.Order = order;
}
public string SalesRepName
{
get
{
ApplicationDbContext db = new ApplicationDbContext();
ApplicationUser Rep = db.Users.First(u => u.UserName == Order.SalesRep);
return Rep.FirstName + " " + Rep.LastName;
}
}
public IEnumerable<SelectListItem> CustomerList
{
get
{
var Db = new OrderContext();
var Customers = Db.Customers;
var AllCustomers = Customers.Select(c => new SelectListItem
{
Value = c.Id.ToString(),
Text = c.FirstName + " " + c.LastName
});
return AllCustomers;
}
}
public IEnumerable<SelectListItem> ProductList
{
get
{
var Db = new OrderContext();
var Products = Db.Products;
var AllProducts = Products.Select(p => new SelectListItem
{
Value = p.Id.ToString(),
Text = p.Name + ", " + p.Airshow + " - " + p.AirshowDate
});
return AllProducts;
}
}
}
Here's the data model for OrderLine:
public class OrderLine
{
[Key]
public int Id { get; set; }
[Required, Display(Name="Order Number")]
public int OrderId { get; set; }
[Required]
public int ProductId { get; set; }
[Required]
public int Quantity { get; set; }
[Required]
[DisplayFormat(ApplyFormatInEditMode=true, DataFormatString="{0:C}")]
public float Price { get; set; }
[Required, Display(Name="Tax Rate"), DisplayFormat(DataFormatString="{0:P}")]
public float TaxRate { get; set; }
public Nullable<float> Discount { get; set; }
public virtual Product Product { get; set; }
public virtual Order Order { get; set; }
}
And here's the View:
#model AirshowCouponsMvc.Models.OrderDetailViewModel
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>OrderDetailViewModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<table class="table table-striped table-responsive table-hover">
<tr>
<th colspan="2">Order Date: #Html.Label(String.Format("{0:MMMM d, yyyy}", DateTime.Now))</th>
<th colspan="2">Customer: #Html.DropDownListFor(model => model.Order.CustomerId, Model.CustomerList)</th>
</tr>
<tr>
<th>
#Html.LabelFor(model => model.Order.OrderLines.FirstOrDefault().Product)
</th>
<th>
#Html.LabelFor(model => model.Order.OrderLines.FirstOrDefault().Quantity)
</th>
<th>
#Html.LabelFor(model => model.Order.OrderLines.FirstOrDefault().TaxRate)
</th>
<th>
#Html.HiddenFor(model => model.OrderLinesList)
</th>
</tr>
#foreach (AirshowCouponsMvc.Models.OrderLine item in Model.OrderLinesList)
{
<tr>
<td>
#Html.DropDownListFor(model => item.ProductId, Model.ProductList)
</td>
<td>
#Html.EditorFor(model => item.Quantity)
</td>
<td>
#Html.EditorFor(model => item.TaxRate)
</td>
<td>
#Html.HiddenFor(model => item.Price)
#Html.HiddenFor(model => item.OrderId)
#Html.HiddenFor(model => item.Id)
#Html.HiddenFor(model => item.Discount)
#Html.HiddenFor(model => item.Order)
#Html.HiddenFor(model => item.Product)
</td>
</tr>
}
<tr>
<td></td>
<td>#Html.ActionLink("Add line", "AddLine")</td>
<td>#if (Model.OrderLinesList.Count > 1) { #Html.ActionLink("Remove line", "RemoveLine") }</td>
</tr>
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10 pull-right">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Finally, here's the code for the controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(OrderDetailViewModel model)
{
if (ModelState.IsValid)
{
OrderContext db = new OrderContext();
model.Order.OrderDate = DateTime.Now;
model.Order.SalesRep = User.Identity.Name;
db.Orders.Add(model.Order);
foreach (var line in model.Order.OrderLines)
{
line.Price = line.Product.Price;
line.Discount = OrderDetailViewModel.DiscountRate(line.Quantity);
line.OrderId = model.Order.Id;
db.OrderLines.Add(line);
}
return RedirectToAction("Index");
}
return View(model);
}
Yes, I know that the controller doesn't process the list. I had used the property from the data model, then I read that you need to define a List as a property in the view model. I was in the middle of changing it to this, but when I hit a break at the beginning of the Create action, I noticed that the OrderLinesList is null.
Also, I read that in older version of ASP.NET MVC (at least I haven't seen it in reference to more recent versions of ASP.NET MVC) that you have to use a for loop instead of a foreach loop. I tried going that route, but I still had the problem of a null List.
Here's the code that I tried as a for loop in the Create view. This was inserted in place of the foreach loop currently shown:
#for (int i = 0; i < Model.OrderLines.Count(); i++)
{
<tr>
<td>
#Html.DropDownListFor(model => model.OrderLines[i].ProductId, Model.ProductList)
</td>
<td>
#Html.EditorFor(model => model.OrderLines[i].Quantity)
</td>
<td>
#Html.EditorFor(model => model.OrderLines[i].TaxRate)
</td>
<td>
#Html.HiddenFor(model => model.OrderLines[i].Id)
#Html.HiddenFor(model => model.OrderLines[i].Price)
#Html.HiddenFor(model => model.OrderLines[i].Discount)
#Html.HiddenFor(model => model.OrderLines[i].OrderId)
#Html.HiddenFor(model => model.OrderLines[i].Order)
#Html.HiddenFor(model => model.OrderLines[i].Product)
</td>
</tr>
}
My end goal is to be able to track multiple line items for each order, preferably with model binding.
I've read through quite a few references to try to get this to work, including:
http://seesharpdeveloper.blogspot.com/2012/05/mvc-model-binding-to-list-of-complex.html
Model binding generic list is null in asp.net mvc
MVC3 Non-Sequential Indices and DefaultModelBinder
ASP.NET MVC 4 - for loop posts model collection properties but foreach does not
I've followed the advice in these, and it's not working for me.
I would appreciate any help on getting this fixed.

Related

SPFx - having trouble read person field from SP list

I'm trying to get data from person field in SharePoint. My code always returns 8 rows (its correct) but at items that consists of Person it returns [object Obejct].
enter image description here
export interface SPUser {
Pracovnik: String;
}
.
.
private getListData(): void {
this._getListData().then((response) => {
this._renderList(response);
});
}
private _renderList(items: SPUser[]): void {
let html: string = '<table class="TFtable" border=1 width=100% style="border-collapse: collapse;">';
html += `<th>Title</th>`;
items.forEach((item: SPUser) => {
if(item.Pracovnik != null) {
html += `
<tr> <td>${item.Pracovnik}</td> </tr>
`};
});
html += `</table>`;
const listContainer: Element = this.domElement.querySelector('#spGetListItems');
listContainer.innerHTML = html;
}
private async _getListData(): Promise<SPUser[]> {
return pnp.sp.web.lists.getByTitle("org_struktura").items.select("Pracovnik/ID").expand("Pracovnik").get().then((response) => {
return response;
});
}
public render(): void {
this.domElement.innerHTML = `
<div class="parentContainer" style="background-color: lightgrey">
<div style="background-color: lightgrey" id="spGetListItems" />
</div>
`;
this.getListData();
}
Any idea what is wrong please?
Sample demo to map SharePoint list item to a Type object.
export interface IReactItem{
Id:number,
Title:string,
Description:string,
User:{ID:number,EMail:string},
enableEdit:boolean
}
private async _getListData(): Promise<IReactItem[]> {
return pnp.sp.web.lists.getByTitle("TestList").items.select("Id,Title,Description,User/ID,User/EMail").expand("User").get().then((response:IReactItem[]) => {
return response;
});
}

Facing issues with passing list of Ids from one View to another controller

I am trying to pass a list of int Ids from one View to another on button click. I am able to hit the controller but its passing null.
Html:
#foreach (var business in Model.Businesses)
{
<tr>
<td>#business.Name</td>
<td>
<p>
#foreach (var bt in #business.BusinessTypes)
{
#bt.Name;
}
</p>
</td>
<td><button type="button" id="btnCalcVS"> Calculate Validator Score</button></td>
</tr>
}
JQUery:
<script type="text/javascript">
$(document).ready(function () {
var businessTypeIds = [];
$(document).on("click", "#btnCalcVS", function () {
$.ajax({
type: 'POST',
url: '/BusinessType/Index',
contentType: "application/json",
data: JSON.stringify( #foreach (var business in Model.Businesses)
{
#foreach (var bt in #business.BusinessTypes)
{
#bt.Id;
}
}),
success: function (data) {
alert("success");
},
error: function (e) {
alert(e);
}
});
});
});
</script>
Controller:
[HttpPost]
[Route("/BusinessType/Index")]
public IActionResult Index([FromBody] List<int> businessTypeIds)
{
//logic
}
As mentioned, I am hitting the controller but it's having null values. As can be seen from the HTML code, i have a list inside a list. (Business Types inside Businesses, so 2 many to many relationships)
Can someone tell me where i am going wrong?
You could try get the id from html and then pass it to controller.
<form method="post">
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.DenniPlan2[0].Id)
</th>
<th>
#Html.DisplayNameFor(model => model.DenniPlan2[0].CisloZakazky)
</th>
<th>
#Html.DisplayNameFor(model => model.DenniPlan2[0].CisloProduktu)
</th>
<th>
#Html.DisplayNameFor(model => model.DenniPlan2[0].Poradi)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.DenniPlan2)
{
<tr>
<td>
#Html.EditorFor(model => item.Id)
</td>
<td>
#Html.EditorFor(model => item.CisloZakazky)
</td>
<td>
#Html.EditorFor(model => item.CisloProduktu)
</td>
<td>
#Html.EditorFor(model => item.Poradi)
</td>
</tr>
}
</tbody>
</table>
<input type="button" id="btnCalcVS" value="submit" />
</form>
#section Scripts{
<script type="text/javascript">
$(document).ready(function () {
var businessTypeIds = [];
$("[id=item_Id]").each(function () {
businessTypeIds.push($(this).val());
});
console.log(businessTypeIds);
$(document).on("click", "#btnCalcVS", function () {
$.ajax({
type: 'POST',
url: '/BusinessType/Index',
contentType: "application/json",
data: JSON.stringify(businessTypeIds),
success: function (data) {
alert("success");
},
error: function (e) {
alert(e);
}
});
});
});
</script>
}
For this line $("[id=item_Id]").each(function () {, you could get the id value item_Id by check the generated html in the web browser.

Data is not displayed on the browser from the blockchain server on solidity

I am trying to make a voting app using Blockchain on truffle framework. The data from the network is not rendered on the webpage.
Only loading is displayed but the actual content is not displayed even though I have connected by Blockchain accounts from Ganache to my metamask extension.
Here is my code:
Election.sol
pragma solidity ^0.5.0;
contract Election {
// Model a Candidate for first past the post system
struct Candidatepost {
uint id;
string name;
uint voteCount;
}
// Model as candidate for proportional party system
struct Candidateparty {
uint id;
string name;
uint voteCount;
}
// Store accounts that have voted
mapping(address => bool) public voters;
// Store Candidates
// Fetch Candidate
mapping(uint => Candidatepost) public cand_post;
mapping(uint => Candidateparty) public cand_party;
// Store Candidates Count
uint public partyCount;
uint public postCount;
uint[] public candidatesCount = [postCount,partyCount];
constructor () public {
addpostCandidate("Candidate 1");
addpostCandidate("Candidate 2");
addpartyCandidate("Candidate 1");
addpartyCandidate("Candidate 2");
candidatesCount = [postCount,partyCount];
}
function addpostCandidate (string memory _name) private {
postCount ++;
cand_post[postCount] = Candidatepost(postCount, _name, 0);
}
function addpartyCandidate (string memory _name) private {
partyCount ++;
cand_party[partyCount] = Candidateparty(partyCount, _name, 0);
}
// voted event
event votedEvent (
uint indexed _candidateId1,
uint indexed _candidateId2
);
function vote (uint _candidateId1, uint _candidateId2) public {
// require that they haven't voted before
require(!voters[msg.sender]);
// require a valid candidate
require(_candidateId1 > 0 && _candidateId1 <= postCount && _candidateId2 > 0 && _candidateId2 <= partyCount);
// record that voter has voted
voters[msg.sender] = true;
// update candidate vote Count
cand_post[_candidateId1].voteCount ++;
cand_party[_candidateId2].voteCount ++;
// trigger voted event
emit votedEvent(_candidateId1, _candidateId2);
}
}
App.js
App = {
web3Provider: null,
contracts: {},
account: '0x0',
init: function() {
return App.initWeb3();
},
initWeb3: function() {
// TODO: refactor conditional
if (typeof web3 !== 'undefined') {
// If a web3 instance is already provided by Meta Mask.
App.web3Provider = web3.currentProvider;
web3 = new Web3(web3.currentProvider);
} else {
// Specify default instance if no web3 instance provided
App.web3Provider = new Web3.providers.HttpProvider('http://localhost:7545');
web3 = new Web3(App.web3Provider);
}
return App.initContract();
},
initContract: function() {
$.getJSON("Election.json", function(election) {
// Instantiate a new truffle contract from the artifact
App.contracts.Election = TruffleContract(election);
// Connect provider to interact with contract
App.contracts.Election.setProvider(App.web3Provider);
App.listenForEvents();
return App.render();
});
},
// Listen for events emitted from the contract
listenForEvents: function() {
App.contracts.Election.deployed().then(function(instance) {
// Restart Chrome if you are unable to receive this event
// This is a known issue with Metamask
// https://github.com/MetaMask/metamask-extension/issues/2393
instance.votedEvent({}, {
fromBlock: 0,
toBlock: 'latest'
}).watch(function(error, event) {
console.log("event triggered", event)
// Reload when a new vote is recorded
App.render();
});
});
},
render: function() {
var electionInstance;
var loader = $("#loader");
var content = $("#content");
loader.show();
content.hide();
// Load account data
web3.eth.getCoinbase(function(err, account) {
if (err === null) {
App.account = account;
$("#accountAddress").html("Your Account: " + account);
}
});
//load contract data
App.contracts.Election.deployed().then(function(instance) {
electionInstance = instance;
return electionInstance.candidatesCount();
}).then(function(1) {
var postcandidatesResults = $("#postcandidatesResults");
postcandidatesResults.empty();
var partycandidatesResults = $("#partycandidatesResults");
partycandidatesResults.empty();
var postcandidatesSelect = $('#postcandidatesSelect');
postcandidatesSelect.empty();
var partycandidatesSelect = $('#partycandidatesSelect');
partycandidatesSelect.empty();
for (var i = 1; i <= 2; i++) {
electionInstance.cand_post(i).then(function(candidate) {
var id = candidate[0];
var name = candidate[1];
var voteCount = candidate[2];
// Render candidate Result
var candidateTemplate = "<tr><th>" + id + "</th><td>" + name + "</td><td>" + voteCount + "</td></tr>";
postcandidatesResults.append(candidateTemplate);
// Render candidate ballot option
var candidateOption = "<option value='" + id + "' >" + name + "</ option>";
postcandidatesSelect.append(candidateOption);
});
}
for (var j = 1; j <= 2; j++) {
electionInstance.cand_party(i).then(function(candidate) {
var id2 = candidate[0];
var name2 = candidate[1];
var voteCount2 = candidate[2];
// Render candidate Result
var candidateTemplate2 = "<tr><th>" + id2 + "</th><td>" + name2 + "</td><td>" + voteCount2 + "</td></tr>";
partycandidatesResults.append(candidateTemplate2);
// Render candidate ballot option
var candidateOption2 = "<option value='" + id2 + "' >" + name2 + "</ option>";
partycandidatesSelect.append(candidateOption2);
});
}
return electionInstance.voters(App.account);
}).then(function(hasVoted) {
// Do not allow a user to vote
if(hasVoted) {
$('form').hide();
}
loader.hide();
content.show();
}).catch(function(error) {
console.warn(error);
});
},
castVote: function() {
var candidateId1 = $('#postcandidatesSelect').val();
var candidateId2 = $('#partycandidatesSelect').val();
App.contracts.Election.deployed().then(function(instance) {
return instance.vote(candidateId1, candidateId2, { from: App.account });
}).then(function(result) {
// Wait for votes to update
$("#content").hide();
$("#loader").show();
}).catch(function(err) {
console.error(err);
});
}
};
$(function() {
$(window).load(function() {
App.init();
});
});
Index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>Election Results</title>
<!-- Bootstrap -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<div class="container" style="width: 650px;">
<div class="row">
<div class="col-lg-12">
<h1 class="text-center">
<div class="row d-flex justify-content-center" style="border:none;background:white;">
<div class="col-md-1 col-3">
<img class="mx-auto d-block img-fluid" src="images/logo.png" style="" alt="">
</div>
</div></h1>
<hr/>
<br/>
<h1 class="text-center">National Election-2075</h1>
<h1 class="text-center">Election Updates</h1>
<div id="loader">
<p class="text-center">Loading...</p>
</div>
<div id="content" style="display: none;">
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Votes</th>
</tr>
</thead>
<tbody id="postcandidatesResults">
</tbody>
</table>
<hr/>
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Votes</th>
</tr>
</thead>
<tbody id="partycandidatesResults">
</tbody>
</table>
<hr/>
<form onSubmit="App.castVote(); return false;">
<div class="form-group">
<label for="postcandidatesSelect">Select Candidate</label>
<select class="form-control" id="postcandidatesSelect">
</select>
</div>
<div class="form-group">
<label for="partycandidatesSelect">Select Candidate</label>
<select class="form-control" id="partycandidatesSelect">
</select>
</div>
<button type="submit" class="btn btn-primary">Vote</button>
<hr />
</form>
<p id="accountAddress" class="text-center"></p>
</div>
</div>
</div>
</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="js/bootstrap.min.js"></script>
<script src="js/web3.min.js"></script>
<script src="js/truffle-contract.js"></script>
<script src="js/app.js"></script>
</body>
</html>
Any call to the blockchain is async(returns a promise). You need to either handle the promise with a .then() or you can use async await. Basically anything chained after electionInstance is async. For example:
electionInstance.candidatesCount();
electionInstance.cand_party
electionInstance.voters

React Search Bar , call function from another class

hello guys i am fetching data from my api and i try to set a search bar like this :
import React, { Component } from "react";
import ProductsIndex from "./search_bar";
class SearchBar extends Component {
constructor(props) {
super(props);
this.state = { term: "" };
this.onInputChange = this.onInputChange.bind(this);
this.onFormSubmit = this.onFormSubmit.bind(this);
}
onInputChange(event) {
this.setState({ term: event.target.value });
}
onFormSubmit(event) {
event.preventDefault();
ProductsIndex.renderProducts(this.state.term)
// this.props.fetchWeather(this.state.term);
this.setState({ term: "" });
}
render() {
return (
<form onSubmit={this.onFormSubmit} className="input-group">
<input
placeholder="Get a five-day forecast in your favorite cities"
className="form-control"
value={this.state.term}
onChange={this.onInputChange}
/>
<span className="input-group-btn">
<button type="submit" className="btn btn-secondary">Submit</button>
</span>
</form>
);
}
}
export default SearchBar;
In my onSubmit function try to call my function renderProducts from my class ProductsIndex here :
class ProductsIndex extends Component {
componentDidMount() {
this.props.fetchProducts();
}
renderProducts(term) {
return _.map(this.props.products, product => {
if(product.name==term) {
return (
<tr key={product.name}>
<td>{product.name}</td>
<td>{product.product_category.name}</td>
<td>{product.price}</td>
</tr>
);
}
});
}
render(){
return(
<div>
<table className="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>Name</th>
<th>Category</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{this.renderProducts()}
</tbody>
</table>
</div>
);
}
}
function mapStateToProps(state) {
return {products: state.products}
}
export default connect(mapStateToProps, {fetchProducts})
(ProductsIndex);
But that is doesn't work i get this error : Uncaught TypeError: _search_bar2.default.renderProducts is not a function
Thanks you for your help
The short answer is: ProductsIndex.prototype.renderProducts
The correct answer is: the state of terms should be owned by a component higher up in the hierarchy. SearchBar triggers a state change in the parent component and it trickles down to ProductIndex. You already probably have a component suitable for this in your hierarchy, but here is how it may look if you add one explicitly for this purpose.
export class SharedParent extends React.Component {
constructor() {
super();
this.state = {
term: ""
};
//I use this style for methods, you can also use fat arrows to ensure 'this' is properly set
this.setTerm = this.setTerm.bind(this);
}
setTerm(term) {
//beware current best practice is to pass a fucntion
//not an object to setState
this.setState({ term });
}
render() {
return [
<ProductIndex key="a" term={this.state.term} {...this.props.productProps} />,
<SearchBar key="b" setTerm={this.setTerm} {...this.props.searchProps}/>
];
}
}
PS: I would suggest also using Redux or another state management solution. You can't go wrong with Redux though.

MVC 4 Add/Update list in a partial view

This is my first program and I am quite not sure on how to complete my codes. I am trying to create a new transaction, where for a supplier there will be multiple inventory types. I am lost on how to add the inventory types to a list using Partial View. Any guidance on how to structure the code will be greatly appreciated. Here are my codes:
ViewModels:
public class InventoryTransactionParent
{
[Key]
public int InventoryTransactionID { get; set; }
[ForeignKey("InventoryTransactionType")]
[Display(Name = "Transaction Type")]
public int InventoryTransactionTypeID { get; set; }
public virtual InventoryTransactionType InventoryTransactionType { get; set; }
[Display(Name = "Supplier")]
[ForeignKey("Supplier")]
public int? SupplierID { get; set; }
public virtual Supplier Supplier { get; set; }
[Display(Name = "Transaction Date (From previous month only)")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime InventoryTransactionDate { get; set; }
[Display(Name = "Receipt/Invoice No.")]
public string InventoryTransactionReceipt { get; set; }
[Display(Name = "Transaction By")]
public string InventoryTransactionBy { get; set; }
[Display(Name = "Created On")]
public DateTime InventoryTransactionCreatedDateTime { get; set; }
[Display(Name = "Created By")]
public string InventoryTransactionCreatedBy { get; set; }
public bool InventoryTransactionCancelled { get; set; }
public int? InventoryTransactionCancelledSourceID { get; set; }
public List<InventoryTypeChild> InventoryTypeChilds { get; set; }
}
public class InventoryTypeChild
{
[ForeignKey("InventoryType")]
[Display(Name = "Inventory Type")]
public int InventoryTypeID { get; set; }
public virtual InventoryType InventoryType { get; set; }
[Display(Name = "Quantity")]
public decimal InventoryTransactionQuantity { get; set; }
[Display(Name = "Price per Item")]
public decimal InventoryTransactionPrice { get; set; }
[Display(Name = "Remarks (1000 characters)")]
[DataType(DataType.MultilineText)]
public string InventoryTransactionRemarks { get; set; }
}
View:
#model Inventory.ViewModels.InventoryTransactionParent
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>In Transaction</legend>
<div class="editor-label">
#Html.LabelFor(model => model.SupplierID, "Supplier")
</div>
<div class="editor-field">
#Html.DropDownList("SupplierID", String.Empty)
#Html.ValidationMessageFor(model => model.SupplierID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.InventoryTransactionDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.InventoryTransactionDate, "TransactionDate")
</div>
<div class="editor-label">
#Html.LabelFor(model => model.InventoryTransactionReceipt)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.InventoryTransactionReceipt)
#Html.ValidationMessageFor(model => model.InventoryTransactionReceipt)
</div>
<div id="inventorytypes">
#using (Html.BeginForm()) {
<table>
<tr>
<td>#Html.LabelFor(model => Model.InventoryTypeChilds[0].InventoryTypeID)</td>
<td>#Html.LabelFor(model => Model.InventoryTypeChilds[0].InventoryTransactionPrice)</td>
<td>#Html.LabelFor(model => Model.InventoryTypeChilds[0].InventoryTransactionQuantity)</td>
<td>#Html.LabelFor(model => Model.InventoryTypeChilds[0].InventoryTransactionRemarks)</td>
<td></td>
</tr>
#{
if (Model.InventoryTypeChilds != null)
{
for (int i = 0; i < Model.InventoryTypeChilds.Count(); i++)
{
<tr>
<td>
#Html.DropDownList("InventoryTypeID", String.Empty)
#Html.ValidationMessageFor(model => model.InventoryTypeChilds[i].InventoryTypeID)
</td>
<td>
#Html.EditorFor(model => Model.InventoryTypeChilds[i].InventoryTransactionPrice)
#Html.ValidationMessageFor(model => Model.InventoryTypeChilds[i].InventoryTransactionPrice)
</td>
<td>
#Html.EditorFor(model => Model.InventoryTypeChilds[i].InventoryTransactionQuantity)
#Html.ValidationMessageFor(model => Model.InventoryTypeChilds[i].InventoryTransactionQuantity)
</td>
<td>
#Html.EditorFor(model => Model.InventoryTypeChilds[i].InventoryTransactionRemarks)
#Html.ValidationMessageFor(model => Model.InventoryTypeChilds[i].InventoryTransactionRemarks)
</td>
<td>
<input type="submit" value="Add" />
</td>
</tr>
}
}
}
</table>
}
</div>
<p>
<input type="submit" value="In" />
</p>
</fieldset>
}
Controller (still very rough):
public ActionResult InMultipleTransaction()
{
ViewBag.InventoryTypeID = new SelectList(db.InventoryTypes, "InventoryTypeID", "InventoryTypeName");
ViewBag.SupplierID = new SelectList(db.Suppliers, "SupplierID", "SupplierName");
InventoryTransactionParent itp = new InventoryTransactionParent();
itp.InventoryTypeChilds = new List<InventoryTypeChild>();
itp.InventoryTypeChilds.Add(new InventoryTypeChild()
{
});
return View(itp);
}
You don't need to use Partial Views in one-to-many relationship. Partial Views are useful if parent and child objects are being created at the same time, mostly happens in one-to-one relationship (But even then, most people use ViewModels). But in your case, you have to have parent object created first then you add many child objects.
This Plural Sight video would be a good way to start. There is a free trial