I have a page which displays fields from 2 different models. To ensure this works I am using Tuple<> to display them both and have tested to ensure data shows which it does. The issue I had was when I submitted the form to update the database, nothing was passed and the record was wiped clean. Please advise me on how to proceed. I have shortened fields shown to make the post shorter.
View
#model Tuple<CommunityParkletDashboard.Models.COMMUNITY_PARKLET_APPLICATION, CommunityParkletDashboard.ViewModels.CPDashboardModel>
<input type="button" value="Back" onclick="location.href='#Url.Action("CPDashboard", "CPDashboard")'" />
#using (Html.BeginForm("EditCP", "CPDashboard", FormMethod.Post))
{
<p>
Reference Number:
#Html.DisplayFor(i => i.Item1.REF_NUMBER)
</p>
<p>
Name:
#Html.DisplayFor(i => i.Item1.NAME)
</p>
<p>
Notes:
#Html.TextAreaFor(i => i.Item1.NOTES)
</p>
foreach (var item in Model.Item2.lParkletApplicationDtos)
{
<p>
Title:
#Html.DisplayFor(i => item.ParkletTitle)
</p>
<br />
<input type="submit" value="Save" />
}
}
Controller
[HttpPost]
public ActionResult EditCP(COMMUNITY_PARKLET_APPLICATION cpa, int id)
{
_context.UpdateParkletApplication(cpa, id);
return RedirectToAction("CPDashboard");
}
UpdateParkletApplication() is just a method which runs some SQL script to update the data using the model and ID so associate the record with, all this was working fine before I introduced second model. Thanks in advance.
Because you create one form and use foreach form item2 it pass all item in item2.
You can use foreach and inside of it use begin form .
foreach (var item in Model.Item2.lParkletApplicationDtos)
{
#using (Html.BeginForm("EditCP", "CPDashboard", FormMethod.Post))
{
<p>
Reference Number:
#Html.DisplayFor(i => i.Item1.REF_NUMBER)
</p>
<p>
Name:
#Html.DisplayFor(i => i.Item1.NAME)
</p>
<p>
Notes:
#Html.TextAreaFor(i => i.Item1.NOTES)
</p>
<p>
Title:
#Html.DisplayFor(i => item.ParkletTitle)
</p>
<br />
<input type="submit" value="Save" />
}
}
Does it pass id ???
#Html.DisplayFor(i => item.ParkletTitle)
As your view contains model as
#model MyCustomModel
Following method should like this and then access cpa.Item1 just you did in view.
[HttpPost]
public ActionResult EditCP(MyCustomModel cpa, int id)
{
_context.UpdateParkletApplication(cpa, id);
return RedirectToAction("CPDashboard");
}
Your Model Class
public class MyCustomModel
{
public CommunityParkletDashboard.Models.COMMUNITY_PARKLET_APPLICATION Item1 {get;set;}
public CommunityParkletDashboard.ViewModels.CPDashboardModel Item2 {get;set;}
}
Ultimately you have to think like how is model constructed. Even if in View you keep Tuple then you can use above answer.
Related
What I want to achieve is to present a set of checkboxes to a user with the <span> ProductName and a boolean value if the ProductName is in stock or not. I'd also like these checkboxes to be updatable in the same view. If there has been an update I want to update a table in SQL using the primary key ProductKey.
I have a class Products containing this information:
public class Products
{
[Key]
public string ProductKey {get;set;}
public string ProductName {get;set;}
public Boolean IsAvailable {get;set;}
}
In practice this issue is a bit complicated as different products can have different attributes. To combat this I've found a solution which creates a IEnumerable model. This is generated prior to the view of interest for this question.
My issue is that I want to be able to loop through my IEnumerable model, see whether the product name is in stock or not (indicated whether the checkbox is checked or not) and be able to update the availability from the same view.
#model IEnumerable<Products>
<form asp-controller="Product" asp-action="FindIfProductInStock">
<div class="row">
#foreach (var item in Model)
{
<div class="form-group">
<label class="label-checkbox">
<input asp-for=#item.IsAvailable type="checkbox" checked=#item.IsAvailable />
<span>
#Html.DisplayFor(modelItem => item.ProductName)
</span>
</label>
</div>
<div class="form-group">
<input asp-for=#item.ProductKey type="hidden" value=#item.ProductKey />
</div>
}
</div>
<div class="form-group">
<input type="submit" value="Update" class="btn btn-primary" />
</div>
</form>
Where my FindIfProductInStock controller looks like this:
[HttpPost]
public IEnumerable<Products> FindIfProductInStock(IEnumerable<Products> productList)
{
return productList;
}
(I use this controller as of right now to be able to make a break point to see what the actual values of productList is).
The view presented to the user works as intended. The checkboxes are checked if the product is in stock. My issue is that productList contains no data. It has 0 rows and IsAvailable is false and the rest of the values of Products are null. Does anyone know why?
I am using the type="hidden" for ProductKey as I don't want the user to see the field, but I need it to connect the value of IsAvailable to the correct product in my SQL table later on.
Replace foreach by for loop
#for (int i = 0; i < Model.Count; i+=1)
{
.....
<input asp-for=#Model[i].IsAvailable type="checkbox" checked=#Model[i].IsAvailable />
....
#Html.DisplayFor(m => m[i].ProductName)
.... and so on
}
My application searches for the files that contains a string the user is looking for. So far it does that perfectly. The last thing I need to do is to export it to an excel file so I added a method inside of my controller that is called by the Result page after I press a button.
The results are stored in a List of type Result which is a class with four variables.
The method ExportToExcel currently returns string so that I can test if the list of results is null. Every single time it has come out as "No Data", therefore it is null. It perfectly prints out a table with the information in the Results page but does not have the information when i want to export it.
Why is my model not passing from view to method?
At first I wanted to pass my model so that I can access the information in the List but now I am wondering if it would be better to save the List data in the controller so that I can directly pass it to my method.
Either way would be fine and I am open to any other ways to do this.
Model
namespace Final.Models
{
public class InputModel:Result
{
public List<Result> Results { get; set; }
}
}
Controller
This controller is just showing how I am passing the InputModel between the views and method. Maybe I am doing something wrong here?
public ActionResult Index()
{
var input = new InputModel();
return View(input);
}
[HttpPost]
public ActionResult Index(InputModel input)
{
//Finds files that contain string.
//send model to Result
return View("Result", input);
}
public ActionResult Result(InputModel input)
{
return View(input);
}
[HttpPost]
public string Result(InputModel input,string export)
{
return ExportToExcel(input);
}
public string ExportToExcel(InputModel input)
{
if (input.Results!=null)
{
//Run excel code here
return "Excel Exported";
}
else
{
return "No Data";
}
}
View for Result
This is part of the view, not the whole thing. I didn't think the full view was necessary but I posted it in the bottom just in case.
#foreach(var result in Model.Results)
{
<tr>
//Return graph of information received
</tr>
}
</table>
<form action="Find/Result" method="POST">
<input type="submit" value="Export" name="export" class="btn btn-default">
</form>
Output
Occurs after pressing the "Export" Button
"No Data"
This is my first MVC applications so once again please let me know if there is any other area I can improve in.
Full View For Result
Changed the form to enclose the entire view as suggested by Wubbly but I get the same output.
#model Final.Models.InputModel
#{
ViewBag.Title = "Result";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<br />
<h4>Result</h4>
<hr />
#using (Html.BeginForm("Result", "Find", FormMethod.Post))
{
<p>The <b>#Model.SelectedText</b> files that contain <b>"#Model.Find"</b> are: </p>
<div>
<table class="table table-bordered table-responsive table-hover">
<tr>
//HEADERS
</tr>
#foreach (var result in Model.Results)
{
// int i = 1;
<tr>
<td>#result.SelectedText</td>
<td>#result.FileName</td>
<td>#result.Line</td>
<td>#result.LineCode</td>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 ">
<input type="submit" value="Export" name="export" class="btn btn-default">
</div>
</div>
</div>
}
<p>
#Html.ActionLink("Back to List", "Index")
</p>
Use for loop with hidden property which takes your property value to model.
#using (Html.BeginForm("Result", "Find", FormMethod.Post))
{
<p>The <b>#Model.SelectedText</b> files that contain <b>"#Model.Find"</b> are: </p>
<div>
<table class="table table-bordered table-responsive table-hover">
<tr>
//HEADERS
</tr>
#for (int i = 0; i < Model.Results.Count; i++)
{
// int i = 1;
<tr>
<td>
#Html.HiddenFor(r => r.Model.Results[i].SelectedText)
#Html.HiddenFor(r => r.Model.Results[i].FileName)
#Html.HiddenFor(r => r.Model.Results[i].Line)
#Html.HiddenFor(r => r.Model.Results[i].LineCode)
#Html.DisplayFor(r => r.Model.Results[i].SelectedText)
</td>
<td>#Html.DisplayFor(r => r.Model.Results[i].FileName)</td>
<td>#Html.DisplayFor(r => r.Model.Results[i].Line)</td>
<td>#Html.DisplayFor(r => r.Model.Results[i].LineCode)</td>
</tr>
}
</table>
<div class="form-group">
<div class="col-md-offset-2 ">
<input type="submit" value="Export" name="export" class="btn btn-default">
</div>
</div>
</div>
}
<p>
#Html.ActionLink("Back to List", "Index")
</p>
To anyone who might need an answer and is in a similar situation I figured my problem out. Many of you might not think it is the right way to fix it but it is what worked for me. Either way any feedback would be appreciated to improve my abilities.
First, I did change my foreach to a for loop as recommended by StephenMuecke and ShailendraKumar.
The way I transferred the data from my HTTPGet to my HTTPPostis with TempData. I used it to store my model with the user's input inside my HTTPPost Index and called it in my HTTPPost Result.
Here's how I changed my controller.
public ActionResult Index()
{
var input = new InputModel();
input.Type = input.FillType(input.Type);
return View(input);
}
[HttpPost]
public ActionResult Index(InputModel input)
{
input.FileType = input.ValueConvert();
input.FileFind();
TempData["model"] = input
return View("Result", input);
}
public ActionResult Result(InputModel input)
{
return View(input);
}
[HttpPost]
public void Result()
{
InputModel model = new InputModel();
model = (InputModel)TempData["model"];
model.ExportToExcel();
}
When the button is pressed it states that the model is invalid as fields are empty.
when looking at the model all fields are empty or null. anyone know how to solve this issue, thanks
Home Controller
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> createOrderLine(Product models)
{
if (ModelState.IsValid)
{
db.OrderLines.Add(new OrderLine
{
productId = models.productId,
productColour = models.productColour,
productDescription = models.productDescription,
productName = models.productName,
productPrice = models.productPrice,
productStock = models.productStock,
//QuantityOrdered = quantityOrdered
});
db.SaveChanges();
return RedirectToAction("Index", "OrderLines");
}
else return RedirectToAction("Index", "Home");
}
Home Index - Displays the product information to the screen
#foreach (var product in Model)
{
using (Html.BeginForm("createOrderLine", "Home", FormMethod.Post, new {
#class = "form-horizontal", role = "form" }))
{
<div class="row">
<div class="blog col-md-6">
<div>
<h1>Product Name</h1>
<h2>#product.productName</h2>
<div>
<h3>Product Description</h3>
<h6><i>#product.productDescription</i></h6>
<br />
<h3>Product Price</h3>
<td>#product.productPrice </td>
<br />
<h1>Product colour</h1>
<td>#product.productColour</td>
<div></div>
<br />
<br />
#Html.AntiForgeryToken()
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-buy" value="Add to cart" onclick="btnbuy_Click" />
</div>
</div>
</div>
</div>
<br />
</div>
</div>
}
}
the problem is that none of your field are linked to the model they only display it so to fix this
you can simply add a hiddenfor field that links the product id in your page like so for each of your product :
#Html.HiddenFor(product.Id)
then fill the rest of the informations you need with the id that is gonna be passed to your controller as a parameter
exemple :
public ActionResult createorderline(Product ProductToAdd)
{
// add your product or do your treatment
}
I do not quite understand what are you trying to do, but Louis is right. If you are trying to pass values back, you have to #Html.EditorFor() or some other ways to pass back information to the server.
You could also use #Html.HiddenFor(product.Id) then inside your Actionmethod call the database to get the rest of the info regarding your product or you could use JQuery as well.
In this case I would only use the #Html.HiddenFor(product.Id) and have a static helper method that will map your orderline class from the DB.
public static Product GetProductById(int productId){
return db.Products.FirstOrDefault(x => x.Id == productId);
}
But again you could just add the FirstOrDefault() line inside your controller as you not mapping from a ViewModel to a business Model.
I wanna update a list of item in edit page,
a user pass to edit page and update request for a list of question,
a model for request is ready to use,
edit.cshtml is like
#using (Html.BeginForm()){
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
#foreach (var item in Model.Requesttables)
{
<div class="editor-label">
#Html.LabelFor(modelItem => item.request)
</div>
<div class="editor-field">
#Html.EditorFor(modelItem => item.request)
#Html.ValidationMessageFor(modelItem => item.request)
</div>
<p>
<input type="submit" value="Save" />
</p>
}
</fieldset>
}
how the controler will be??
public ActionResult Edit(List <Requesttable> requestlist)
{//some logic here!}
If I understand it right, you want to see the controller. First I think that there is something wrong with your controller signature. It should be like this:
public ActionResult Edit(int id)
{
//search the object, no matter what it is - as long as it is form database by id
var db = new DbContext();
var yourRequestedList=db.Find(id); //or something like that, see linq for the correct sintax
yourRequestedList = objectThatWasEdited;
}
I hope this helps you out, and don't forget to refactor. My cod is not a good practice, you do not instatiate the db inside of a controller method.
I work on one of my first ASP MVC-programs at the moment.
The program should show me a list of product, and with a link beneath the name of the product it should be possible to edit the product. No problem so far.
#model MVC3Demo.Product
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm("Save", "Product"))
{
<div>
<input type="hidden" id="ID" name="ID" value="#Model.ID" />
ProduktID #Model.ID
</div>
<div>
Produktname <input id="Name" name="Name" type="text" value=#Model.Name />
</div>
<div>
Preis <input id="Price" name="Price" type="text" value=#Model.Price />
</div>
<div>
<input type="submit" value="Speichern"/>
</div>
}
Now I have written a Save action method that should update my data:
public ActionResult Save(Product p)
{
ProductRepository rep = new ProductRepository();
rep.Update(p);
return RedirectToAction("List");
}
The "List"-View is where I can see all products with the edit-link.
The problem is, that if I press the save-button, it redirects me to the old list, not to the updated one. I debugged my project and I´m sure that the update-method works correct and updates the product.
My List action is:
#model IEnumerable<MVC3Demo.Product>
#{
ViewBag.Title = "List";
}
<h2>List</h2>
<ul>
#foreach (MVC3Demo.Product p in Model)
{
<li>#p.Name #Html.ActionLink("bearbeiten", "Edit", "Product", p, null)</li> //new{ ID = p.id}
}
</ul>
Because you asked, here is the List Action:
public ActionResult List()
{
ProductRepository rep = new ProductRepository();
return View(rep.GetAll());
}
So where could be my mistake?
It looks like you're calling the update, but you're not actually submitting the transaction itself, does your repository have a SubmitChanges, AcceptChanges or Commit or something similar? As with DataTables, your changes won't actually take effect (save to the database) until you call AcceptChanges.
Try include an HttpPost attribute at Save controller method.
[HttpPost]
public ActionResult Save(Product p)
{
ProductRepository rep = new ProductRepository();
rep.Update(p);
return RedirectToAction("List");
}