I am currently working on a MVC3 project. I currently stuck on this portion.
I have this Ajax form which contains several hyperlink
#using (Ajax.BeginForm("Click", new AjaxOptions { UpdateTargetId = "showpage" }))
{
<a href="#" id="0" onclick='document.forms[0].submit()'>Link 0</a>
<a href="#" id="1" onclick='document.forms[0].submit()'>Link 1</a>
}
I have this function called "Click" in a controller to fire whenever there'a link clicked.
public ActionResult Click(String id)
{
// Action here
}
However, Whenever I click one of the hyperlinks, the click function receive id as "null". I want the function to know which hyperlink is click when enter the controller. Help needed :(
From my understanding, I think you're after something like this:
HTML:
<div id="showpage">
</div><br />
#using (Ajax.BeginForm("Click", "", new AjaxOptions { UpdateTargetId = "showpage" },
new { #id = "myAjaxForm" }))
{
#Html.ActionLink("Link 0", "Click", null, new { #id = "Link0", #data_val = "0", #class = "links" })
#Html.ActionLink("Link 1", "Click", null, new { #id = "Link1", #data_val = "1", #class = "links" })
#Html.Hidden("id", "");
}
SCRIPT:
<script>
$(function () {
$('a[class=links]').click(function (e) {
e.preventDefault();
$("input#id").val($(this).attr("data-val"));
$('form#myAjaxForm').submit();
});
});
</script>
CONTROLLER:
public ActionResult Click()
{
var m = new MyModel();
return View(m);
}
[HttpPost]
public ActionResult Click(string id)
{
return Content("ID = " + id, "text/html");
}
Hope this helps
I would recommend you to use a separate forms per button (which contrary to classic WebForms is something that you should not be afraid of in ASP.NET MVC) and of course use submit buttons instead of hyperlink which are the semantically correct element to submit a form:
#using (Ajax.BeginForm("Click", new { id = "0" }, new AjaxOptions { UpdateTargetId = "showpage" }))
{
<button type="submit" value="Link 0" />
}
#using (Ajax.BeginForm("Click", new { id = "1" }, new AjaxOptions { UpdateTargetId = "showpage" }))
{
<button type="submit" value="Link 1" />
}
Now inside your Click action you will get the correct id.
Instead of hyperlinks you can use submit buttons with the name property.
#using (Ajax.BeginForm("Click", new AjaxOptions { UpdateTargetId = "showpage" }))
{
<input type="submit" name="action" value="0" />
<input type="submit" name="action" value="1" />
}
public ActionResult Click(string action, ... )
{
// Action here
}
Related
I am having an issue trying to return to my index (List) page with my filter set correctly. The filter is never set on return from the details page and always defaults to the first value in the drop down list. Using the Html helper EnumDropDownListFor.
EDIT: I should point out that if I change the current month filter from the index page that it posts correctly to the Index ActionResult and the filter is correctly set back in the Index page. So the issue is when posting from the details page back to the Index page only.
Here's my code.
Model Code:
using System;
using System.Collections.Generic;
namespace ShiftPatternConfigurator.Models
{
// shift view
public class ShiftViewModel
{
public IEnumerable<Shift> Shifts { get; set; }
public Month Month { get; set; }
}
// shift model
public class Shift
{
public int ShiftNo;
public string ShiftName;
public DateTime StartTime;
public DateTime FinishTime;
public string Team;
public int Week;
public int CycleWeek = 0;
public string StartDay;
public DateTime StartDate;
}
// month enum
public enum Month
{
January = 1,
February = 2,
March = 3,
April = 4,
May = 5,
June = 6,
July = 7,
August = 8,
September = 9,
October = 10,
November = 11,
December = 12
}
}
Controller Code:
using ShiftPatternConfigurator.DataAccess;
using ShiftPatternConfigurator.Models;
using System;
using System.Web.Mvc;
namespace ShiftPatternConfigurator.Controllers
{
public class HomeController : Controller
{
// GET: Index
public ActionResult Index()
{
ViewBag.Title = "Shift Pattern";
ShiftViewModel monthShiftView = new ShiftViewModel
{
Month = new Month()
};
monthShiftView.Month = (Month)DateTime.Now.Month;
monthShiftView.Shifts = DbContext.GetShiftsByMonth(monthShiftView.Month);
return View(monthShiftView);
}
// POST: Index
[HttpPost]
public ActionResult Index(Month month)
{
ViewBag.Title = "Shift Pattern";
ShiftViewModel monthShiftView = new ShiftViewModel
{
Month = new Month()
};
monthShiftView.Month = month;
monthShiftView.Shifts = DbContext.GetShiftsByMonth(monthShiftView.Month);
return View(monthShiftView);
}
// GET: Details
public ActionResult Details(int shiftNo, Month monthFilter)
{
ViewBag.Title = "Shift Details";
ViewBag.MonthFilter = monthFilter;
Shift shift = DbContext.GetShiftByShiftNo(shiftNo);
return View(shift);
}
}
}
Index (List) Code:
#model ShiftPatternConfigurator.Models.ShiftViewModel
<div class="jumbotron">
<h1>#ViewBag.Title - #Model.Month</h1>
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.EnumDropDownListFor(x => x.Month)
<input type="submit" value="Select Month" class="btn btn-primary btn-sm" />
}
</div>
<table class="table">
<tr>
<th>Shift No</th>
<th>Shift Name</th>
<th>Start Time</th>
<th>Finish Time</th>
<th>Team</th>
<th>Week</th>
<th>Start Day</th>
<th>Start Date</th>
<th></th>
</tr>
#if (Model.Shifts.Count() > 0)
{
foreach (var item in Model.Shifts)
{
<tr>
<td>#item.ShiftNo</td>
<td>#item.ShiftName</td>
<td>#item.StartTime</td>
<td>#item.FinishTime</td>
<td>#item.Team</td>
<td>#item.Week</td>
<td>#item.StartDay</td>
<td>#item.StartDate.ToShortDateString()</td>
<td>
<div class="btn-group btn-group-xs">
#Html.ActionLink("Edit", "Edit", new { shiftNo = item.ShiftNo, monthFilter = Model.Month }, new { #class = "btn btn-primary" })
#Html.ActionLink("Details", "Details", new { shiftNo = item.ShiftNo, monthFilter = Model.Month }, new { #class = "btn btn-primary" })
</div>
</td>
</tr>
}
}
else
{
<tr>
<td colspan="10" align="center"><h2>No Data</h2></td>
</tr>
}
</table>
Detail Code:
#using ShiftPatternConfigurator.Models
#model Shift
<h2>#ViewBag.Title</h2>
<div>
<h4>Shift - #Model.ShiftNo</h4>
<hr />
<dl class="dl-horizontal">
<dt>Shift No:</dt>
<dd>#Model.ShiftNo</dd>
<dt>Shift Name</dt>
<dd>#Model.ShiftName</dd>
<dt>Start Time</dt>
<dd>#Model.StartTime.ToShortDateString() #Model.StartTime.ToLongTimeString()</dd>
<dt>Finish Time</dt>
<dd>#Model.FinishTime.ToShortDateString() #Model.FinishTime.ToLongTimeString()</dd>
<dt>Team:</dt>
<dd>#Model.Team</dd>
<dt>Week:</dt>
<dd>#Model.Week</dd>
<dt>Cycle Week:</dt>
<dd>#Model.CycleWeek</dd>
<dt>Start Day:</dt>
<dd>#Model.StartDay</dd>
<dt>Start Date:</dt>
<dd>#Model.StartDate</dd>
</dl>
</div>
<p>
#Html.ActionLink("Edit", "Edit", new { /* id = Model.PrimaryKey */ }) |
#Html.ActionLink("Back to List-ActionLink", "Index", "Home", new { month = ViewBag.MonthFilter })
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.Hidden("month", (Month)ViewBag.MonthFilter)
<input type="submit" value="Back to List-Form" class="btn btn-primary btn-sm" />
}
#Ajax.ActionLink("Back to List-Ajax", "Index", "Home",
new { month = (Month)ViewBag.MonthFilter },
new AjaxOptions { HttpMethod = "POST" },
new { #class = "btn btn-primary btn-sm" })
</p>
I have tried the following ways to get back to my index page with the filter set:
#Html.ActionLink method
I have found that this will not work as ActionLink always sends a GET request so I cannot use this method.
#Html.ActionLink("Back to List-ActionLink", "Index", "Home", new { month = ViewBag.MonthFilter })
#Ajax.ActionLink method
Using the Ajax method the Index POST method gets hit but the page stays on the detail page.
#Ajax.ActionLink("Back to List-Ajax", "Index", "Home",
new { month = (Month)ViewBag.MonthFilter },
//new AjaxOptions { HttpMethod = "POST", OnSuccess = "window.location.href = '/'" },
new AjaxOptions { HttpMethod = "POST" },
new { #class = "btn btn-primary btn-sm" })
#Html.BeginForm method
With this method it posts correctly to the POST ActionResult correctly, however the drop down list defaults to the first in the list. So the filter is not passed correctly to the enumdropdownlistfor.
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.Hidden("month", (Month)ViewBag.MonthFilter)
<input type="submit" value="Back to List-Form" class="btn btn-primary btn-sm" />
}
Here is method 3 using #Html.BeginForm to post back, in screenshots with debugging values shown:
Page is launched and hits the GET index ActionResult, the Models Month enum is set to the current month and the list of shifts are returned from the BDcontext and assigned to the Model.
The Index view then renders the Html setting the EnumDropDownListFor to the currect date correctly
As you can see from the image the month is set to the current month.
EDIT: Here is the correctly generated HTML output by the #Html.EnumDropDownListFor helper
After Selecting the details link, the code hits the GET details ActionResult and renders the view correctly.
After hitting the back to List-Form button the POST Index Action Result is hit correctly setting all the parameters.
But the view is rendered with the Month filter defaulting to the first in the list, not my selected month.
EDIT: Here is the incorrectly generated HTML output by the #Html.EnumDropDownListFor helper
Please help with suggestions how I can make this work.
So I found a work around for my issue, although it feels like it is a hack opposed to the correct way to do it. Maybe someone can comment as to why it didn't work the way I wanted it to?
What I did was the following:
Use ActionLink in my Detail page passing a parameter.
Handle the optional parameter in my GET Index ActionResult method.
So the code looks like this.
Detail View Action Link:
#Html.ActionLink("Back to List", "Index", "Home", new { monthFilter = ViewBag.MonthFilter }, new { #class = "btn btn-primary btn-sm" })
Index (List) GET ActionResult:
// GET: Index
public ActionResult Index(string monthFilter)
{
ViewBag.Title = "Shift Pattern";
ShiftViewModel monthShiftView = new ShiftViewModel
{
Month = new Month()
};
// hack to fix issue with filter not being passed back to index page from Details view
if(monthFilter == null)
monthShiftView.Month = (Month)DateTime.Now.Month;
else
monthShiftView.Month = (Month)Enum.Parse(typeof(Month), monthFilter); ;
monthShiftView.Shifts = DbContext.GetShiftsByMonth(monthShiftView.Month);
return View(monthShiftView);
}
So as you can see I handle the month value, by using the current month if the month value passed in is null or I use the value passed in if its available to be used.
On my "Create" view for my model, I'd like a "Submit" button as well as a "Submit & Close" button. I was looking at the answer to this question for information on how to close a page from a controller action (although I see some comments saying that this isn't possible--is there a workaround for that?).
These are the buttons currently:
#using (Html.BeginForm())
{
#Html.HiddenFor(model => model.Project.ProjectID);
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="save-buttons">
#if (Model.ReadOnly)
{
<button type="button" id="close_page" onclick="closeWindow();">Close</button>
}
else
{
<button type="submit">Save Project</button>
if (Model.Project == null)
{
<button type="button">Delete Project</button>
}
else
{
<button type="button" onclick="deleteProject(#Model.Project.ProjectID, #Model.Project.SubmissionNumber);">Delete Project</button>
}
<button type="button">Save & Close</button>
}
</div>
... the form itself is below here
and the submit action is:
public ActionResult Create([Bind(Include = "ProjectName,ProjectDescription,DateReceived,EffectiveDate,ExpirationDate,GeneralContractor,ProjectTerm,ProjectType,SubmissionNumber,PolicyNumber,Status,Underwriter,Division,BrokerCity,TAName,Branch,FirstNamedInsuredAddress,FirstNamedInsured,ProjectAddress")] Project project)
{
if (ModelState.IsValid)
{
if (project.ProjectID <= 0)
{
db.Projects.Add(project);
}
else
{
db.Entry(project).State = EntityState.Modified;
}
db.SaveChanges();
return RedirectToAction("Create", new { sub_num = project.SubmissionNumber });
}
var model = new ProjectViewModel()
{
Project = db.Projects.Find(project.ProjectID),
States = GetSelectListItems(GetAllStates()),
Branches = GetSelectListItems(GetAllBranches()),
Divisions = GetSelectListItems(GetAllDivisions()),
ProjectTypes = GetSelectListItems(GetAllProjectTypes()),
Statuses = GetSelectListItems(GetAllStatuses()),
ReadOnly = false,
Cleared = false,
EditMode = false
};
return View("Maintenance", model);
}
My problem comes with differentiating which button sent the browser to the submit method. I've come up with a few different possibilities but I'm not sure if any of them are possible (I don't think they are):
First is to create a boolean model property that is true when the page should close after submitting and false otherwise. Then I can wrap my RedirectToAction in an if statement like so:
if (Model.Done) {
return JavaScript("window.close();");
}
else
{
return RedirectToAction("Create", new { sub_num = project.SubmissionNumber });
}
But I don't know how I could set this property on button click. I don't think this is possible so I can't do this but correct me if I'm wrong.
The other possibility would be to pass an argument to the action when the button is clicked, but I don't know how to do this when it is a <button type="submit"></button>.
Does anyone have any suggestions? Thanks!
Hope this may help you
You can can set name of button.If click on that button then we can get which button is clicked.
In Create.cshtml
<button type="submit" id="Create" name="Create"></button>
<button type="submit" id="Close" name="Close"></button>
In Controller.cs
Public ActionResult Create(string name){
if(name="Create")
{
//here code for create button
return RedirectToAction("Create", new { sub_num =project.SubmissionNumber });
}
if(name="Close")
{
//heere code for close button
return JavaScript("window.close();");
}
}
Im using CaptchaMvc.Mvc4 1.5.0 in a form with #Ajax.BeginForm in my Asp.net MVC4 application. when input is valid it works fine. but when captcha input is invalid. dispose method is called and i cant resubmit the message. and the captcha image dosnt change.
This is my View:
#using CaptchaMvc.HtmlHelpers;
#using CaptchaMvc;
#model Web.Models.Message
#using (Ajax.BeginForm("Contact", "Home", new AjaxOptions
{
HttpMethod = "post",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "Sent"
}))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
#Html.LabelFor(model => model.Message1)
#Html.TextBoxFor(model => model.Message1)
#Html.ValidationMessageFor(model => model.Message1)
#Html.MathCaptcha()
<input type="submit" value="Create" />
</fieldset>
<div id="Sent">
</div>
}
And this is my Action method:
[HttpPost]
public ActionResult Contact(Message message)
{
if(this.IsCaptchaValid("error"))
if (ModelState.IsValid )
{
db.Messages.Add(message);
db.SaveChanges();
return RedirectToAction("SuccessfullySent");
}
ModelState.AddModelError("", "there is some error");
return Content("there is some error");
}
ps: I tested it with #htmal.Beginform and it worked fine. some how problem is related to #Ajax.Beginform.
the trick is create a partial view and put code for generate captcha on it :
#using CaptchaMvc.HtmlHelpers
#using CaptchaMvc;
<div>
#Html.MathCaptcha()
</div>
<div>
#ViewBag.CaptchaInvalid
</div>
<div>
#ViewBag.Successfully
</div>
and in the main form where the ajax.beginform is put rendering this parcial view on the updateTargetId div "Sent":
<div id="Sent">
#{Html.RenderPartial("~/Views/Shared/CaptchaGenerate.cshtml");}
</div>
and in the action method make this changes:
[HttpPost]
public ActionResult Contact(Message message)
{
if(this.IsCaptchaValid("error"))
if (ModelState.IsValid )
{
db.Messages.Add(message);
db.SaveChanges();
ViewBag.CaptchaInvalid = "";
ViewBag.Successfully = "successfully sent";
return PartialView("~/Views/Shared/CaptchaGenerate.cshtml");
}
ModelState.AddModelError("", "there is some error");
ViewBag.CaptchaInvalid = "";
ViewBag.Successfully = "";
return PartialView("~/Views/Shared/CaptchaGenerate.cshtml");
}
I'm using #Ajax.BeginForm() with KendoUI ListView in my asp.net mvc3 application. The KendoUI ListView displays a list of Items with a button for each item.
My requirement is that onclick of submit button, I need to send the id of a clicked button to the controller and return the full information of the Item.
My approach was to use onclick function for all buttons to trigger AjaxBeginForm submit input but the AjaxBeginForm submit input doesn't seems to pass the right value into the controller,
How can I achieve this?
<pre>
//My Ajax form
var ajaxOpts = new AjaxOptions
{
HttpMethod = "POST",
UpdateTargetId = "gallery",
InsertionMode = InsertionMode.Replace,
OnBegin = "OnBeginT1",
};
#using (Ajax.BeginForm("BlogInformation", ajaxOpts))
{
<input id="input" type="submit" style="display: none"/>
}
<script id="listview-template" type="x-kendo-template">
<button class="k-button1" onclick="returnsubmitted()" id="#:id#" name="but">Read More</button>
//Displaying a list
</div>
#(Html.Kendo().ListView<Blog>()
.Name("listView")
.TagName("div")
.ClientTemplateId("listview-template")
.DataSource(dataSource =>
{
dataSource.Read(read => read.Action("Blog_Read", "Blog"));
})
)
<script>
function returnsubmitted() {
//$("input").val($("#:id#").val());//This does not pass the right value to input
// $("#input").val($('.k-button1').attr('class').id);
$("#input").click();
}
</script>
//Here is my controller
[HttpPost]
public ActionResult BlogInformation(string blogid)
{
XElement element = XElement.Load(Server.MapPath("~/App_Data/Blogs.xml"));
IEnumerable<Blog> data = null;
if (!string.IsNullOrEmpty(blogid))
{
var xElement = FindByID(blogid, element.Element("Blog")).Element("Blog");
if (xElement != null)
{
data = FindByID(blogid, element.Element("Blog"))
.Element("items")
.Elements("Blog")
.Select(e => ToBlog(e));
}
else
{
data = element.Elements("Blog").Select(e => ToBlog(e));
}
}
return View(data);
}
I have the following Create action method inside my ASP .NET MVC :-
public ActionResult CreateVMNetwork(int vmid)
{
VMAssignIps vmips = new VMAssignIps()
{
TechnologyIP = new TechnologyIP() { TechnologyID = vmid},
IsTMSIPUnique = true,
IsTMSMACUnique = true
};
return PartialView("_CreateVMNetwork",vmips);
}
Which will render the following partial view:-
#model TMS.ViewModels.VMAssignIps
#using (Ajax.BeginForm("CreateVMNetwork", "VirtualMachine", new AjaxOptions
{
InsertionMode = InsertionMode.InsertAfter,
UpdateTargetId = "networktable",
LoadingElementId = "loadingimag",
HttpMethod= "POST"
}))
{
#Html.ValidationSummary(true)
#Html.HiddenFor(model=>model.TechnologyIP.TechnologyID)
<div>
<span class="f">IP Address</span>
#Html.EditorFor(model => model.TechnologyIP.IPAddress)
#Html.ValidationMessageFor(model => model.TechnologyIP.IPAddress)
<input type="CheckBox" name="IsTMSIPUnique" value="true" #(Html.Raw(Model.IsTMSMACUnique ? "checked=\"checked\"" : "")) /> |
<span class="f"> MAC Address</span>
#Html.EditorFor(model => model.TechnologyIP.MACAddress)
#Html.ValidationMessageFor(model => model.TechnologyIP.MACAddress)
<input type="CheckBox" name="IsTMSMACUnique" value="true" #(Html.Raw(Model.IsTMSMACUnique ? "checked=\"checked\"" : "")) />
</div>
<input type="submit" value="Save" class="btn btn-primary"/>
}
Inside the following main view, after clicking on the Ajax.actionlink:-
#Ajax.ActionLink("Add Network Info", "CreateVMNetwork","VirtualMachine",
new { vmid = Model.VirtualMachine.TMSVirtualMachineID },
new AjaxOptions {
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "AssignNetwork" ,
LoadingElementId = "progress"
}
)
</p>
<p><img src="~/Content/Ajax-loader-bar.gif" class="loadingimage" id="progress" /></p>
<div id ="AssignNetwork"></div>
Then when clicking on the “Save” button it will call the following action method:-
[HttpPost]
public ActionResult CreateVMNetwork(VMAssignIps vmip)
{
if (ModelState.IsValid)
{
try
{
repository.InsertOrUpdateVMIPs(vmip.TechnologyIP,User.Identity.Name);
repository.Save();
return PartialView("_networkrow",vmip);
}
catch (Exception ex)
{
ModelState.AddModelError(string.Empty, "Error occurred: " + ex.InnerException.Message);
}}
return PartialView("_CreateVMNetwork", vmip);
}
When will render the following partial view _networkrow:-
#model TMS.ViewModels.VMAssignIps
<tr id="#Model.TechnologyIP.ID">
<td> #Model.TechnologyIP.IPAddress</td>
<td>#Model.TechnologyIP.MACAddress</td>
<td>#Ajax.ActionLink("Delete",
"DeleteNetworkInfo", "VirtualMachine",
new { id = Model.TechnologyIP.ID },
new AjaxOptions
{ Confirm = "Are You sure You want to delete (" + Model.TechnologyIP.IPAddress + ")" + "( " + Model.TechnologyIP.MACAddress + ").",
HttpMethod = "Post",
OnSuccess = "deletionconfirmation",
OnFailure = "deletionerror"
})</td>
</tr>
All the above will work fine unless a model state error or an exception occurred , then in this case the table will be updated with the partial view and the model state will be displayed under the table with the fields. But I need to display the model state error on the same original view. So that I need the Ajax.begin form to update the table only if no exception or model state errors occured, and to display an error message inside the original partial view, not under the table.
Can anyone advice on how to solve this issue?
I don't get your question clearly, however I think you should place a <div> into your view where you want to show errors.
Then, if you got some errors on your processing, push some error messages through ViewBag to the model.
So, your action method will become like this:
[HttpPost]
public ActionResult CreateVMNetwork(VMAssignIps vmip)
{
if (ModelState.IsValid)
{
try
{
repository.InsertOrUpdateVMIPs(vmip.TechnologyIP,User.Identity.Name);
repository.Save();
return PartialView("_networkrow",vmip);
}
catch (Exception ex)
{
ViewBag.ErrorMessage = "Some messages...";
// also any other info you like
}
}
return PartialView("_CreateVMNetwork", vmip);
}
And that in your view like this:
<div>
<p>#ViewBag.ErrorMessage</p>
</div>
So, if you you got some errors, they'll be shown in that
Where is networktable defined? Remember that even if there is an exception (since you have caught it) or ModelState error, the ajax response will come back with status 200 and the OnSuccess will be executed. In the case of the save button, the returned response from the server will be inserted after the html of whatever networktable contains already. If you want special action to be taken when there is a failure, you have to change the response status on the server and catch it through a function on the client side called by OnFailure on the ajax form.