.NET MVC 4 - Button actions in a foreach loop - c#

I've currently got a View like this:
#using (Html.BeginForm("Compare", "Carousel", FormMethod.Post))
{
#foreach (var product in Model.Products)
{
<tr>
<td>#product.Provider.Name</td>
<td>£#Math.Round(product.MonthlyPremium, 2, MidpointRounding.AwayFromZero)</td>
<td>#Html.ActionLink("More Details", "MoreDetails", new { id = product.ProductId })</td>
<td><button name="compare" value="#product.ProductId">Compare</button</td>
</tr>
}
}
Controller:
[HttpPost]
public ActionResult Compare(ProductsWithDetailModel model) // This is a guess, Model may not persist?
{
// Code here
return View("Index", model);
}
What I actually want is, when a button is clicked, the Controller is going to be called and the product that has been selected (the button clicked) will be added to a list of items.
How do I actually find out which button has been clicked in the Controller? How come I cant find any tutorials out there on the web that have done this before, surely its basic functionality? Or am I approaching it wrong?

ASP.NET MVC takes all inputs and send it by a verb to an action. The bind of these controls is made by the name attribute on your View and the value is made by depending of what control we are talking about (text, checkbox, radio, select, button etc...). You could have a a property on your ProductsWithDetailModel called compare and it would recevie the value of the button you've clicked.

How about having 1 form per item?
E.g.
#foreach (var product in Model.Products)
{
<tr>
<td>#product.Provider.Name</td>
<td>£#Math.Round(product.MonthlyPremium, 2, MidpointRounding.AwayFromZero)</td>
<td>#Html.ActionLink("More Details", "MoreDetails", new { id = product.ProductId })</td>
<td>
#using (Html.BeginForm("Compare", "Carousel", FormMethod.Post))
{
<input type="hidden" name="productId" value="#product.ProductId" />
<button name="compare">Compare</button>
}
</td>
</tr>
}

use onclick. set js hidden value in loop. in controller check.

Related

Asp.net MVC Razor #foreach + onclick the data gets listet

I'm working on a Project Tool that lists all the Projects on the Dashboard. Now I want that when I click on the Project that the Data from my Database gets listed on the right site of it.
Problem 1
Code:
#foreach (var item in Model)
{
<tbody>
<tr>
<td>Project Titel</td>
<td>#item.PrjTitel</td>
</tr>
</tbody>
}
With this all the Database Data gets listet, but i just want the Data from one Project not from Project 1,2,3 listet.
How can i make that ?
Problem 2
How can i make that the Data gets listet when the User clicks on a Project.
So if i click on Project 2 the data from Project to gets listet on the right site. See Screenshot -->
Screenshot of the front page
#foreach (var item in Model.Projects) {
<tbody>
<tr>
<td>#Ajax.ActionLink("Select","SelectProjects",new { #id=item.ProjectId},new AjaxOptions { HttpMethod="GET",OnSuccess="LoadProjectsSuccess", OnFailure="LoadProjectsFailur",InsertionMode=InsertionMode.Replace,UpdateTargetId= "Projects" },new { #class="btn btn-info btn-table"})</td>
</tr>
</tbody>}
<div class="row row-equal" id="Privileges"><div>
you can use AjaxHelper if you don't have any information about ajax helper please visit Ajax Helper
public ActionResult SelectProjects(int id)
{
DemoViewModel model = new DemoViewModel()
{
// Your ViewModel
};
return PartialView("_ProjectDetails", model);
}
_ProjectDetails Partial View with data you wont to update

How to correctly pass an object related to a jQuery ListView line to the specific controller method?

I have a jQuery view that show a list of Fix object, this one:
<ul data-role="listview" data-inset="true" data-theme="b" data-split-icon="delete">
#foreach (DataModel.Vulnerability.Fix item in Model.VulnerabilityFixes)
{
<li><a href="#Url.Action("Details", "Product", new { Title = item.Title })">
<h2>#item.Title</h2>
<table style="width: 100%">
<tr>
<th>Id</th>
<th>FixName</th>
<th>Vendor</th>
<th>Version</th>
</tr>
<tr>
<td>#MyHelper.decodeNull(item.Id)</td>
<td>#MyHelper.decodeNull(item.FixName)</td>
<td>#MyHelper.decodeNull(item.Vendor)</td>
<td>#MyHelper.decodeNull(item.Version)</td>
</tr>
</table>
</a>
Delete
</li>
}
</ul>
As you can see, this list contains a button (a link) to delete the related fix, this one:
Delete
When this link is clicked is called the DeleteFix() method of the EditingController class that take 2 paramethers:
vulnId that is a long value.
The Fix object related to the clicked link line.
So this is my DeleteFix() method,
public ActionResult DeleteFix(long vulnId, DataModel.Vulnerability.Fix currentFix)
{
DataModel.Vulnerability.Fix model = new DataModel.Vulnerability.Fix();
manager.openConnection();
try
{
model = currentFix;
}
finally
{
manager.closeConnection();
}
return View(model);
}
The problem is that the when enter into the method the vulnId parameter is correctly set but the currentFix parameter is null.
What am I missing? What can I do to correctly pass the Fix object related to the line on which I pressed the delete button?
You can't pass an "object" in a URL. You can only pass text. (Although you could serialize the object). You probably want to pass the Id of the item. Thus the link should look like:
Delete
You action should be:
public ActionResult DeleteFix(long vulnId, int currentFix)
Then you can use the Id to delete the item.

Populate table with MVC on submit

I am learning MVC by converting an old Web Form to MVC 5. The page is pretty simple, just a few text boxes, a submit button and a grid. I want similar functionality where there is no grid on page load, and after the submit takes place, the grid appears. The issue I am having is all the examples I have seen contain the data coming at page load.
Here is my View
<h2>Search Craigslist</h2>
#using (Html.BeginForm())
{
<p>
Enter Search Criteria:<br />
#Html.TextBox("Criteria", null, new { #style = "width: 450" })
#Html.DropDownList("DropDownValues")
Days:
#Html.TextBox("Days")
<br />
<input type="submit" value="Submit" />
</p>
}
#Html.Partial("~/Views/Search/_Search.cshtml")
and the partial
#model IEnumerable<CraigsListSearch.MVC.Models.SearchModel>
#{
Layout = null;
}
<table cellspacing="0" border="1" style="border-collapse:collapse;">
<tr>
<th>
#Html.DisplayNameFor(model => model.Location)
</th>
<th>
Link
</th>
<th>
#Html.DisplayNameFor(model => model.DateSubmitted)
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Location)
</td>
<td>
#Html.Hyperlink(Html.DisplayFor(modelItem => item.URL).ToString(), Html.DisplayFor(modelItem => item.Title).ToString())
</td>
<td>
#Html.DisplayFor(modelItem => item.DateSubmitted)
</td>
</tr>
}
and finally the controller
public class SearchController : Controller
{
[HttpGet]
//public ViewResult Index(string DropDownValues, string Criteria, string Days)
public ViewResult Index()
{
Populate();
Session["list"] = SearchModel.GetQuickList();
return View(new List<SearchModel>());
}
[HttpPost]
public ViewResult Index(string DropDownValues, string Criteria, string Days)
{
Populate();
Criteria = Server.UrlPathEncode(Criteria).Replace(",", "%2c");
List<Location> list = (List<Location>)(Session["list"]);
List<SearchModel> results = SearchModel.Search(Criteria, Days, DropDownValues, list.Take(5).ToList());
return View(results);
}
private void Populate()
{
List<DropDownModel> ddlList = DropDownModel.GetDropdowns();
ViewBag.DropDownValues = (from a in ddlList
select new SelectListItem
{
Text = a.Text,
Value = a.Value
}).ToList();
}
}
What I am looking for is to have the partial only be "called" on POST. Is this something that can easily be done? Let me know if other info is needed.
Thanks!
There are a couple of ways you could approach solving this problem.
One option would be to use AJAX. When the user clicks the button make an AJAX call to the controller and have the controller render the partial and return the HTML which you then inject into the page DOM.
Another option is to add a "DisplayGrid" property to your model. When you first render the View set it to false and then set it to true after the post.
One point I want to make is that rendering a View from the Post action isn't a great idea. If the user hits Refresh in their browser it's going to post again, giving the user that annoyingly ugly dialog that tells them they're going to send data to the server. In general you should use a Post-Redirect-Get pattern, the user posts, you redirect, and then you use a get to re-render in the browser. If you use the AJAX approach this all becomes moot because you never post the whole form back to a controller action. If you continue to use a full post the HttpPost version of Index should return a RedirectToAction result sending the user back to the Get version of Index. You can communicate between the two actions by using TempData, for example:
[HttpGet]
public ActionResult Index()
{
if( TempData.ContainsKey( "DisplayGrid" )
{
// Use the other values from TempData to populate the model with the grid data
myModel.DisplayGrid = (bool)TempData["DisplayGrid"];
}
}
[HttpPost]
public ActionResult Index( string dropDownValues, string criteria, string days )
{
TempData["DisplayGrid"] = true;
TempData["DropDownValues"] = dropDownValues;
TempData["Criteria"] = criteria;
TempData["Days"] = days;
return RedirectToAction( "Index" );
}
You should pass whatever information you need to render view via model (or ViewBag).
Since you already have sample of how to use Model here is how you can use ViewBag
#if(ViewBag.RenderSearch)
{
#Html.Partial("~/Views/Search/_Search.cshtml")
}
And set RenderSearch in particular action:
public ViewResult Index(string DropDownValues...
{
....
ViewBag.RenderSearch = true;
With model just add property to your model (you'd need to make it custom class instead of just List) and check that flag instead.
Try This, the Ajax only replace the GridDiv only
<h2>Search Craigslist</h2>
using (Ajax.BeginForm("BindGridAction", "Contoller",new AjaxOptions { UpdateTargetId = "GridDiv" }))
{
<p>
Enter Search Criteria:<br />
#Html.TextBox("Criteria", null, new { #style = "width: 450" })
#Html.DropDownList("DropDownValues")
Days:
#Html.TextBox("Days")
<br />
<input type="submit" value="Submit" />
</p>
}
<div id="GridDiv">
#Html.Partial("~/Views/Search/_Search.cshtml")
</div>
Bind your partial view in controller
public PartialViewResult BindGridAction()
{
// your model coding
return PartialView("~/Views/Search/_Search.cshtml", model);
}

couldn't get model in action on post?

I have a strongly typed view which has text fields and a submitted link. After editing data I press link and try to submit form. Placing a break point I am able to see control comes to action; modle is not null but all of its properties are always null, not sure where I m doing wrong. My tiny view code is:
#model BL.Model.Speaker
#using (Html.BeginForm())
{
<table>
<tr>
<td>#Html.EditorFor(s => #Model.Name)</td>
<td>#Html.EditorFor(s => #Model.Email)</td>
</tr>
</table>
#Html.ActionLink("Submit", "All");
}
and my controller action is:
public ActionResult All(Speaker model){
return View(database.Speakers.FirstOrDefault());
}
Help please
You should change your button from ActionLink to a submit button like this
#Html.ActionLink("Submit", "All");
By
<input type="submit" value="submit"/>
Also change your Action to recieve the data by post
[HttpPost]
public ActionResult All(Speaker model)
{
return View(database.Speakers.FirstOrDefault());
}

MVC Hover Over Item in View to render View

I have this fiddle here. It demonstrates a hover over and a box appearing with info in it.
What I'm trying to achieve is, when the "View Details" is hovered over, it triggers the MVC action Details(guid Id) and the result of that action is rendered in the box.
I'm not entirely sure how to do this. I would assume that a AJAX form is submitted on hover, so this will need to be done by JS (I really don't know how to do AJAX with JS). Then the div would be displayed with a newly rendered #Html.Action("Detail", "Stuff", new { Id = #item.Model.Id })
Am I close?
The View would be something like this
<table>
<thead>
<tr>
<td>Name</td>
<td>E-mail</td>
</tr>
</thead>
<tbody>
#foreach (var item in Model.ItemList)
{
<tr>
<td>#Html.DisplayFor(model => item.Name)</td>
<td>#Html.DisplayFor(model => item.Email)</td>
<td>#using(Ajax.BeginForm(ajaxOpts))
{
<span>Hover for Details</span>
#Html.HiddenFor(model => item.Id)
}
</td>
</tr>
}
</tbody>
</table>
The action would purely be:
[ChildActionOnly]
public ActionResult Detail(Guid id)
{
var item = _repository.Stuff.FirstOrDefualt(x => x.Id.Equals(id));
return View(item);
}
So the specification is:
When "Hover for Details" has been hovered to show a box where the cursor is displaying the recently got details from the database
I've wrote this all off the top of my head, so don't scrutinise it for accuracy, its purely to demonstrate an idea, i'm not looking for errors in my code. I'm looking for ideas with valid working examples. Thanks.
1) Submit Ajax on Hover.
2) Follow the example here Render a view as a string to render your view as a HTML string within the server.
3) Use $("#showme").html(ReturnedHTMLData); to place the returned html into the DOM object.
i.e. server side
public JsonResult GetDetail(Guid id)
{
return Json(ViewToStringMethod(ViewName, id), "rest of Json allowget stuff");
}
i.e. Client side
$("#DomElement").on("hover", function(){
$.getJSON("GetDetail", {id: $('this').attr("id"), function(returnedData){
$("#showme").html(ReturnedHTMLData);
}
});

Categories