Can anyone see why this code doesn't work?
I know that someone will notice that I am using Delete links and I should be using a DELETE verb rather than a POST, but I have not been able to resolve that issue, even with the help of SO.
No the issue here is that I click on delete, the underlying data gets deleted OK, but after I delete the data, when I try to redirect to the Payroll GET method, it does not get called and as a result the screen does not get refreshed.
So here is the code on the Controller;
[HttpGet]
[Authorize(Roles = "Administrator, AdminAccounts, ManagerAccounts")]
public ActionResult Payroll()
{
if ((SessionObjects.PeriodStartDate > DateTime.MinValue) && (SessionObjects.PeriodEndDate > DateTime.MinValue))
if (SessionObjects.PayrollSelectedEmployeeId == 0)
return View(new PayrollViewModel()
{
PeriodStartDate = SessionObjects.PeriodStartDate,
PeriodEndDate = SessionObjects.PeriodEndDate
});
else
return View(new PayrollViewModel(
SessionObjects.PeriodStartDate,
SessionObjects.PeriodEndDate,
SessionObjects.PayrollSelectedEmployeeId
));
return View();
}
[HttpPost]
[Authorize(Roles = "Administrator, AdminAccounts, ManagerAccounts")]
public ActionResult Payroll(PayrollViewModel _pvm)
{
if (ModelState.IsValid)
{
SessionObjects.PeriodStartDate = _pvm.PeriodStartDate;
SessionObjects.PeriodEndDate = _pvm.PeriodEndDate;
if (_pvm.SearchTextId > 0)
SessionObjects.PayrollSelectedEmployeeId = _pvm.SearchTextId;
return RedirectToAction("Payroll");
}
return View(_pvm);
}
//[AcceptVerbs(HttpVerbs.Delete)]
[HttpPost]
[Authorize(Roles = "Administrator, AdminAccounts, ManagerAccounts")]
public RedirectToRouteResult Delete(int id)
{
EmployeeOtherLeaf.Delete(id);
return RedirectToAction("Payroll");
}
Part of the View and Editor Template;
<table class="groupBorder">
<tr>
<th></th>
<th>Leave Type</th>
<th>Notes</th>
<th>Day Amount</th>
<th>Date</th>
<th>Approver</th>
</tr>
<%: Html.EditorFor(x => x.LeaveList)%>
</table>
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<SHP.WebUI.Models.Leave>" %>
<%# Import Namespace="SHP.WebUI.HtmlHelpers" %>
<%# Import Namespace="SHP.Models" %>
<%: Html.RowStyle(Model.RowColour) %>
<tr>
<td style="background-color:White;">
<%-- Ajax Delete --%>
<% if(Model.LeaveId > 0) { %>
<%: Html.DeleteEmployeeOtherLeave()%>
<%} %>
</td>
<td><%: Model.LeaveType %></td>
<td><%: Model.Notes %></td>
<td><%: Model.DayAmount %></td>
<td><%: String.Format("{0:ddd MMM d yyyy}", Model.Date)%></td>
<td><%: Model.ApproverName %></td>
</tr>
</tbody> <%-- Leave this here, it closes from the above Html.RowStyle!--%>
HTML Helper method;
public static MvcHtmlString DeleteEmployeeOtherLeave(this HtmlHelper<Leave> html)
{
var leave = html.ViewData.Model;
return html.RouteLink(
"Delete",
"Default",
new {id = leave.LeaveId, action = "Delete" },
new { onclick = "return DeleteRow(this);" }
);
}
You seem to be invoking the Delete action using AJAX inside the DeleteRow javascript function (which you haven't shown). You cannot redirect in AJAX requests. That's the whole point them: do not refresh the entire browser but only portions of it.
If you wanted to perform a full redirect inside the success callback of your AJAX call you could use the window.location.href property, like this:
success: function(result) {
window.location.href = '/somecontroller/Payroll';
}
Now of course doing something like this is meaningless. I would simply use a standard HTML form which will post top the Delete action and not use any javascript at all:
<% if(Model.LeaveId > 0) { %>
<% using (Html.BeginForm("Delete", "Home", new { id = leave.LeaveId })) { %>
<button type="submit">Delete</button>
<% } %>
<% } %>
Now when the form is submitted the Delete action will be invoked which will perform the actual delete and redirect the browser to the Payroll action => pretty standard HTTP dialogue.
And if you decide to go this way you even get a bonus: you could decorate your controller action with the [HttpDelete] attribute and use a technique on the client :
<% if(Model.LeaveId > 0) { %>
<% using (Html.BeginForm("Delete", "Home", new { id = leave.LeaveId })) { %>
<%= Html.HttpMethodOverride(HttpVerbs.Delete) %>
<button type="submit">Delete</button>
<% } %>
<% } %>
and then:
[HttpDelete]
[Authorize(Roles = "Administrator, AdminAccounts, ManagerAccounts")]
public RedirectToRouteResult Delete(int id)
{
EmployeeOtherLeaf.Delete(id);
return RedirectToAction("Payroll");
}
Under the hood it's not a real DELETE HTTP verb since browsers do not support it for forms but it simulates it using a hidden field which ASP.NET MVC understands and is capable to properly redispatch the request to the corresponding action.
Related
Hi I am fairly new to MVC and facing a issue.
I have a View which lists all the countries having just 2 columns
I want column headers clickable and should sort on clicking on it
I have shown my Controller & view code below and I expect that when I click on the column header it should hit Index action method decorated with [HttpPost]
When I click the header link page just refreshes without hitting Action method I expect it to hit
Any help would be appreciated.
Here is my controller code
[HttpPost]
public ActionResult Index(string sortcolumn)
{
return View(SortedList(sortcolumn));
}
private List<Country> SortedList(string sortcol)
{
List<Country> sortedlist = new List<Country>();
switch (sortcol)
{
case "idCountry":
if ((SortOrder)ViewData["sortorder"] == SortOrder.Ascending)
{
sortedlist = db.Countries.OrderByDescending(c => c.idCountry).ToList<Country>();
ViewData["sortorder"] = SortOrder.Descending;
}
else
{
sortedlist = db.Countries.OrderBy(c => c.idCountry).ToList<Country>();
ViewData["sortorder"] = SortOrder.Ascending;
}
break;
case "Countryname":
if ((SortOrder)ViewData["sortorder"] == SortOrder.Ascending)
{
sortedlist = db.Countries.OrderByDescending(c => c.Countryname).ToList<Country>();
ViewData["sortorder"] = SortOrder.Descending;
}
else
{
sortedlist = db.Countries.OrderBy(c => c.Countryname).ToList<Country>();
ViewData["sortorder"] = SortOrder.Ascending;
}
break;
}
return sortedlist;
}
Here is my view
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<eduSmart.Data.Entities.Country>>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Index
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>
List of Countries</h2>
<p>
Manage list of countries on this page. You can choose your action creating, editing,
removing the country data.
</p>
<% using (Html.BeginForm("Index","Country")) { %>
Total Countries : <%: Model.Count() %>
<table>
<tr>
<th>
<%: Html.ActionLink("CountryID","Index",new { sortcolumn = "CountryID" }) %>
</th>
<th>
<%: Html.ActionLink("CountryName ", "Index", new { sortcolumn = "CountryName" })%>
</th>
<th>
</th>
</tr>
<% if (Model != null)
{ %>
<% foreach (var item in Model)
{ %>
<tr>
<td>
<%: item.idCountry%>
</td>
<td>
<%: item.Countryname%>
</td>
<td>
<%: Html.ActionLink("Edit", "Edit", new { id = item.idCountry })%>
|
<%: Html.ActionLink("Details", "Details", new { id = item.idCountry })%>
|
<%: Html.ActionLink("Delete", "Delete", new { id = item.idCountry })%>
</td>
</tr>
<% }
}%>
</table>
<%} %>
<p>
<%: Html.ActionLink("Create New", "Create") %>
</p>
</asp:Content>
You can't hit a post method with an Action link. One thing you could do, if all you're doing is sorting here, is change your post method to a get and change the name
Instead of
[HttpPost]
public ActionResult Index(string sortcolumn)
Have something like
public ActionResult Sort (string sortColumn)
And point your actionlink to Sort.
I have a form in MVC:
<% using (Html.BeginForm("Get", "Person"))
{ %>
<%= Html.TextBox("person_id")%>
<input type="submit" value="Get Person" />
<% } %>
This redirects me to Person/Get. Okay. The question:
How do I make this Form so it redirects me to Person/Get/{person_id}?
Edit:
<% using (Html.BeginForm("Get", "Person", new { id = ??? }))
{ %>
<%= Html.TextBox("person_id")%>
<input type="submit" value="Get Person" />
<% } %>
What do I write in ???
I think the most difficult way would be using a javascript clientside.
The more straightforward way is to retrieve it on the action Person/Get and from there return a RedirectResult pointing to Person/Get/{person_id}
[HttpPost]
public ActionResult Get(string person_id)
{
return RedirectToAction("Get", "Person", new { id = person_id });
}
[HttpGet]
public ActionResult Get(string id)
{
//Do your thing
}
The redirect is usually so fast that the user will never notice. He/she will arrive at /Person/Get/{person_id}
What you want to do is specify the route values as the third parameter on the BeginForm method.
<% using (Html.BeginForm("Get", "Person", **new { person_id = this.Model}**))
{ %>
<%= Html.TextBox("person_id")%>
<input type="submit" value="Get Person" />
<% } %>
Then your controller action would look something like this
public ActionResult Get(int person_id)
{
return View(person_id);
}
I have question about accessing html.checkbox() in controller method.
In my view i have
<% foreach (var item in Model.PredmetTbl){ %>
<td>
<%:Html.CheckBox(item.Predmet) %>
<%:item.Predmet %>
</td>
<%} %>
Predemts are in DB and I want create new db records. How can i test if the checbox is checked or not ?
My controller code
[HttpPost]
public ActionResult PridajSaduPredmetov(int id, FormCollection data)
{
var zoznam = from predmet in ziakDB.PredmetTables select predmet;
ZoznamPredmetovTable predmety;
foreach (var item in zoznam)
{
if (HERE TESTING IF CHECKED)//IF Checked==true will add to db
{
predmety = new ZoznamPredmetovTable();
predmety.ZiakID = id;
predmety.PredmetID = item.PredmetID;
predmety.SkolskyRokID = IndexViewModel.GetSkolskyRokTeraz();
try
{
ziakDB.ZoznamPredmetovTables.InsertOnSubmit(predmety);
ziakDB.SubmitChanges();
}
catch { }
}
}
return RedirectToAction("DetailZiaka", "Administration", new { id = id });
}
(controlid).checked will return true/false
I did not make use of the html checkbox extension but I based my solution on this post:
How to handle checkboxes in ASP.NET MVC forms?
<% For Each item As x In Model.predmetTbl%>
<div><input type="checkbox" name="SelectedPredMet"
<% If Model.SelectedPredMet.Contains(item.Id) Then%>
checked="checked"
<% End If %>
value="<%: item.Id %>" /> <%: item.Predmet %></div>
<% Next%>
I have an index view grid that i would like to put a create partial view at the bottom of-- that way a user can create a new item on the same page as viewing all items. However, upon submitting the partial create view, nothing happens to the main index page; it doesn't update with the new item (although its there after a refresh).
Index page:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<Paris.Domain.Models.PhoneNumberType>>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Index
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Index</h2>
<table>
<tr>
<th></th>
<th>
PhoneNumberTypeID
</th>
<th>
Name
</th>
</tr>
<% foreach (var item in Model) { %>
<tr>
<td>
<%: Html.ActionLink("Edit", "Edit", new { id=item.PhoneNumberTypeID}) %> |
<%: Html.ActionLink("Delete", "Delete", new { id=item.PhoneNumberTypeID })%>
</td>
<td>
<%: item.PhoneNumberTypeID %>
</td>
<td>
<%: item.Name %>
</td>
</tr>
<% } %>
</table>
<p>
Create New
</p>
<% Html.RenderAction("Create"); %>
</asp:Content>
And here is the controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Paris.Domain.Access;
namespace Paris.Web.Areas.Domain.Controllers
{
public class PhoneNumberTypesController : Controller
{
//
// GET: /Domain/PhoneNumberTypes/
private IRepository<Paris.Domain.Models.PhoneNumberType> db;
public PhoneNumberTypesController(IRepository<Paris.Domain.Models.PhoneNumberType> context)
{
db=context;
}
public ViewResult Index()
{
return View(db.Get().Select(i=>i));
}
public ViewResult IndexCreate(Paris.Domain.Models.PhoneNumberType num)
{
Create(num);
return View();
}
public ViewResult Edit()
{
return View();
}
[HttpPost]
public ViewResult Edit(Paris.Domain.Models.PhoneNumberType num)
{
try
{
if (ModelState.IsValid)
{
db.Update(num);
return Index();
}
else
return View();
}
catch
{
return View();
}
}
[HttpPost]
public ViewResult Delete(int PhoneNumberTypeID)
{
db.Delete(db.Get().First(i => i.PhoneNumberTypeID == PhoneNumberTypeID));
return Index();
}
[HttpPost]
public ViewResult Create(Paris.Domain.Models.PhoneNumberType num)
{
try
{
if (ModelState.IsValid)
{
db.Create(num);
return View();
}
else
return View();
}
catch
{
return View();
}
}
[HttpGet]
public ViewResult Create()
{
return View();
}
}
}
If i am doing this completely wrong, feel free to point out how my thinking is flawed.
Thanks
In your Create action methods, you are returning just the Create view, not the whole page. This is because the default behaviour of View() is to return a view with the same name as the action method. Therefore, if you want the Post to return the whole page you need to either specify the correct view name, or even better return a redirect to the Index action. eg: return RedirectToAction("Index");
Logically what you are doing is right. However on the submit button of partial create, your entire page should get refreshed. Why is it not happening? Are you using Ajax to create new record?
I guess this is what you are doing:
Posting to create action.
Record is created and then new record set is fetched from data source.
You pass it back to your view (return View("Index", recordset)).
Index view is refreshed with new record set.
If you are doing these steps then it should work.
I have a strongly typed partial view that populates all the Records from Search table.
Now i have a textbox to enter name & a button to filter the records that can match a name.(Like Search page).
Can anybody provide code sample for this scenario?
My Main Page loads particular view based on the radiobutton selection(Search or Inquiry) as below using JQuery:
/* Loading the partial view based on radio button click... */
$(document).ready(function() {
$(':radio').click(function() {
if (this.value == '2') {
$('#ViewAllInquiries').load('/Home/Inquiry', function(html) { $('#ViewAllInquiries')[0].value = html; });
}
else {
$('#ViewAllInquiries').load('/Home/Search', function(html) { $('#ViewAllInquiries')[0].value = html; });
}
});
})
Here is my one of the Partial view ControllerCode:
[HttpGet]
public ActionResult Search()
{
var search = from s in entity.Search
select s; return PartialView(search);
}
Here is the User control Partial view(Search.ascx):
>" %>
<table >
<thead>
<tr>
<th align="left"> </th>
<th align="left"> TX_Id</th>
<th align="left">Name
<%= Html.TextBox("Name")%> <input type="submit" value="Filter" /></th>
<th align="left">Email Address</th>
</tr>
<% foreach (var item in Model)
{ %>
<%= Html.Encode(item.TX_Id) %>
"><%= Html.Encode(item.CustomerMaster.FullName()) %>
<%= Html.Encode(item.CustomerMaster.MS_Id) %>
<% } %>
Thanks for your time.
I do the same thing using an Ajax form. It's really easy. Here's the code I use:
Html:
<div>
<%
using (Ajax.BeginForm("Home", "Search", null,
new AjaxOptions { UpdateTargetId = "Output" },
new { id = "SearchForm" }))
{
%>
<!-- Form Fields -->
<input name="searchField" />
<input type="submit" value="Search" />
<%
}
%>
<div id="Output">
</div>
</div>
Then in the controller you just have:
public PartialViewResult Search(FormCollection form)
{
var model = YourSearchMethod(form["searchField"]);
return PartialView("Search", model);
}
The div with the id "Output" will be updated with your partial view result every time the submit button is clicked. In your case you have two different potential partial views, just submit the radio button value as part of your form and you can switch the output view from within the controller.
Why use FormCollection instead of parameters? I've had some difficult using named parameters with ajax forms, but you can try it and see how it works. It should look something like this instead:
public PartialViewResult Search(string searchField, bool inquiry)
{
if (inquiry)
{
var model = YourInquiryMethod(searchField);
return PartialView("Inquiry", model);
}
else
{
var model = YourSearchMethod(searchField);
return PartialView("Search", model);
}
}
I do the same on one of my sites but have implmented it a little diffently.
I have, in my View the following html;
<div class="EditProductContainer hidden"></div>
I also have the following jQuery;
function editBenefit(objThis) {
var id = $(objThis).parents('.Benefit').attr("id");
$.post("/Home/jQueryGetBenefit", { Id: id },
function(newHTML) {
$('.EditProductContainer').html(newHTML);
});
}
Then in my controller I have;
[AcceptVerbs(HttpVerbs.Post)]
public PartialViewResult jQueryGetBenefit(int Id)
{
Application application = Helpers.CacheHelper.Get();
Benefit thisBenefit = application.findBenefit(Id);
return PartialView("EditBenefit", thisBenefit);
}
I think this is doing what you want but I'm returning a rendered PartialView and replacing the contents of a containing div with the html generated by the partial view.
Hope this is of some help.