I need some help/advise on how to make this work.
I need to pass the model from the view to the controller through an ActionLink
#Html.ActionLink("Radera", "DeleteTraffic", new { model = Model, trafficId = traffic.Id }, new { #class = "btn btn-link NoBorder NoBackGround" })
the method in the controller looks like this.
public ActionResult DeleteTraffic(CalendarModel model, int trafficId)
{
return View("EditDay", model);
}
I have not put any code in the method yet, I've only been debugging it to get the call to work. model is null when I press the button, trafficId is however correctly set. so what have I done wrong?
Edit 1:
I've changed the code according to the suggestions here.
#using (Html.BeginForm("DeleteTraffic", "Calendar", new {trafficId = traffic.Id})) {<input type="submit" value="Radera" class="btn btn-link NoBorder NoBackGround"/>}
[HttpPost]
[ValidateAntiForgeryToken]
[ActionName("DeleteTraffic")]
public ActionResult DeleteTraffic(int trafficId)
{
return View("EditDay", Model);
}
but DeleteTraffic is never reched, instead it calls the Main Action for this page.
// GET: Calendar
public ActionResult Calendar()
{
CalendarModel model = new CalendarModel {SelectedDate = DateTime.Today};
if (Request.HttpMethod == "POST")
{
if (!string.IsNullOrEmpty(Request.Form.Get("submit.SelectDate")))
{
model.SelectedDate = Convert.ToDateTime(Request.Form["selectedDate"]);
model.TrafficDates = TrafficData.GeTrafficDatesPerMonth(model.SelectedDate);
Model = model;
return View("EditDay", Model);
}
}
Model = model;
return View(Model);
}
should I just tuck the trafficId into a hiddenfield and use this action for everything? MVC seems so inflexible at times...
First, something like a "delete" should never be handled by GET. Deleting is atomic and should be done utilizing either the POST or DELETE (preferably) verbs. Generally, you also should not just delete something without user confirmation, so the simplest and correct way to handle this would be to have the "delete" link take the user to a view that asks them to confirm deleting the item. On this view, then, you would submit the id of the item to be deleted via a form post:
public ActionResult Delete(int id)
{
var foo = db.Foos.Find(id);
if (foo == null)
{
return new HttpNotFoundResult();
}
return View(foo);
}
[HttpPost]
[ValidateAntiForgeryToken]
[ActionName("Delete")]
public ActionResult DeleteConfirm(int id)
{
var foo = db.Foos.Find(id);
if (foo == null)
{
return new HttpNotFoundResult();
}
db.Foos.Remove(foo);
db.SaveChanges();
return RedirectToAction("Index");
}
Then, for your GET action, you would add a Delete.cshtml file:
#model Namespace.To.Foo
<p>Are you sure you want to delete the foo, "#Model.Name"?</p>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.HiddenFor(m => m.Id)
#Html.ActionLink("Cancel", "Index")
<button type="submit">Delete</button>
}
Alternatively (or rather progressively, as you should still have the previous method as a fallback), you could use a JavaScript confirm and AJAX to do this, if you don't want to change pages:
#Html.ActionLink("Radera", "DeleteTraffic", new { id = item.Id }, new { #class = "btn btn-link NoBorder NoBackGround delete", data_id = item.Id })
Then:
<script>
$('.delete').on('click', function () {
var $deleteLink = $(this);
if (confirm('Are you sure?')) {
$.post('/url/for/delete/', { id = $deleteLink.data('id') }, function () {
$deleteLink.closest('tr').remove();
});
}
});
</script>
Related
I'm trying to use Ajax.BeginForm, so I can pass the value of the checkbox to my Controller Action.
I was using #Ajax.ActionLink, but I can't get the value of the newly introduced checkbox, so I want to use Ajax.BeginForm going forward.
Since #Ajax.ActionLink is working in the View, I assume that Ajax is working properly, so I can rule that out.
Here are a couple of options I have tried. When I hover over the buttons, they both display the URL of my web page instead of the URL of the Controller and Action. When I click on the buttons, the page basically refreshes instead of calling the GetCode Action in the PublicOffers Controller.
Any help is much appreciated! Thanks!
Option 1
<div>
#using (Ajax.BeginForm("GetCode", "PublicOffers", null, new AjaxOptions()
{
UpdateTargetId = "detailsDiv",
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET",
OnBegin = "onStart",
OnComplete = "onComplete",
OnSuccess = "myCallback"
}))
{
//bool checkedOption = false;
//#Html.CheckBoxFor(x => checkedOption, new { Name = "OptIn" })
#Html.CheckBoxFor(model => model.OptIn);
#Html.HiddenFor(model => model.Id);
<input type="submit" value="Get Code" class="button btn-primary" />
}
</div>
Option 2
<div>
#using (Ajax.BeginForm(new AjaxOptions()
{ HttpMethod = "GET",
Url = "PublicOffers/GetCode",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "detailsDiv",
OnBegin = "onStart",
OnComplete = "onComplete",
OnSuccess = "myCallback"
}))
{
//bool checkedOption = false;
//#Html.CheckBoxFor(x => checkedOption, new { Name = "OptIn" })
#Html.CheckBoxFor(model => model.OptIn)
//#Html.HiddenFor(model => model.Id, new { Name = "OfferId" })
#Html.HiddenFor(model => model.Id);
#Html.AntiForgeryToken()
<input type="submit" value="Submit" />
}
</div>
This is not exactly an answer but it might help.
First be sure you have this, I use NuGet to install it,
Microsoft.jQuery.Unobtrusive.Ajax
Then be sure it gets emitted to the page, either though bundle scripts or a script link in the cshtml
First a simple model...
namespace AjaxTest.Models
{
public class AjaxTestModel
{
public bool OptIn { get; set; }
}
}
Next some cshtml...
#model AjaxTest.Models.AjaxTestModel
#{ ViewBag.Title = "AjaxTest1";}
<h2>AjaxTest1</h2>
#using (Ajax.BeginForm("AjaxTest1", "Home", null,
new AjaxOptions
{
HttpMethod = "POST",
OnSuccess = "OnSuccess(xhr)",
OnFailure = "OnFailure(xhr, status)"
},
new { id = "myform" }))
{
#Html.CheckBoxFor(model => model.OptIn);
<span id="lbl_message"></span>
<br />
<button id="btn_submit" type="submit" class="">Submit</button>
}
#section scripts{
<script>
$(document).ready(function () {
console.log("ready to rock and roll....");
});
function OnBegin() {
console.log("OnBegin");
}
function OnSuccess(xhr) {
console.log("OnComplete");
$("#lbl_message").text("Ok:" + xhr.responseJSON.param2);
}
function OnFailure(xhr, status) {
console.log("OnFailure");
$("#lbl_message").text("Error:" + xhr.responseJSON.param2);
}
</script>
}
The magic is in the controller, notice that I am returning a Json object, not a view.
public class HomeController : Controller
{
public ActionResult AjaxTest1()
{
AjaxTestModel model = new AjaxTestModel();
return View();
}
[HttpPost]
public ActionResult AjaxTest1(AjaxTestModel model)
{
if (model.OptIn)
{
dynamic errorMessage = new { param1 = "param1", param2 = "You opted in." };
HttpContext.Response.StatusCode = (int)HttpStatusCode.OK;
return Json(errorMessage, JsonRequestBehavior.AllowGet);
}
else
{
dynamic errorMessage = new { param1 = "param1", param2 = "You opted out." };
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
return Json(errorMessage, JsonRequestBehavior.AllowGet);
}
}
}
Note, The HttpContext.Response.StatusCode will control which Ajax callback is activate, return HttpStatusCode.Ok and the OnSuccess gets called, return the HttpStatusCode.NotFound, or most any other error code, and the OnFailure will get called.
I got it to work by adding a beginning Ajax.BeginForm method. It's almost like the first instance invokes the second Ajax.BeginForm method. Since there is no button or anything else in the first method, nothing displays on the page.
NOTE: I changed my Get to a Post so the [ValidateAntiForgeryToken] attribute would work on the Controller Action. I read that it would not work with a Get.
<div>
#using (Ajax.BeginForm("GetCode", "PublicOffers", new { offerId = Model.Id }, new AjaxOptions()
{
//Nothing here, but for some reason without this code the Ajax.BeginForm below won't work
}))
{
//Nothing here, but for some reason without this code the Ajax.BeginForm below won't work
}
</div>
<div>
#using (Ajax.BeginForm("GetCode", "PublicOffers", new { offerId = Model.Id },
new AjaxOptions { OnBegin = "onStart", UpdateTargetId = "detailsDiv",
InsertionMode = InsertionMode.Replace, HttpMethod = "Post",
OnComplete = "onComplete",
OnSuccess = "myCallback"},
new { #style = "display:inline-block" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<div class="form-group">
#Html.CheckBoxFor(model => model.OptIn, new { Name = "optIn"})
</div>
</div>
<input type="submit" class="button btn-primary" value="Get Code" id="get-code button" />
}
</div>
Here is my Controller Action.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> GetCode(Guid offerId, bool optIn = false)
{
//Code here
return PartialView("_OfferCode");
}
I have a View with a dropdownlist. I would like to select a value from the dropdownlist and depending on that value pull into the View a form related to that selection.
Looks like this:
I would like the form to appear below this ddlist and if the user changes the value in the ddlist, it would change the form.
Here's my "Add" View:
<div id="matSelContainer">
#Html.Action("SelectMaterial", "Materials", "Materials")
</div>
Here's the "SelectMaterial" View:
#model Procurement.Models.Material.MaterialType
#Html.Label("Select a Material Type: ")
#Html.DropDownListFor(
x => x.MaterialTypeList,
new SelectList(Model.MaterialTypeList, "MaterialTypeId", "Name")
)
Here's my Controller:
public ActionResult Add()
{
return View();
}
public ActionResult SelectMaterial()
{
materialTypes.LoadMaterialTypes();
return PartialView(materialTypes);
}
I would like to have a separate View for each Material Type and just call that View based on the selection in the ddlist.
Can someone show me how to achieve this?
Would I add a separate #Html.Action to the "Add" View and then in the Controller have another action return a PartialView?
But how to connect this with the Onchange event?
Update<<
Here's what is almost working for me:
"SelectMaterial" View:
#model Procurement.Models.Material.MaterialType
#Html.Label("Select a Material Type: ")
#Html.DropDownListFor(
x => x.MaterialTypeList,
new SelectList(Model.MaterialTypeList, "MaterialTypeId", "Name"),
new { #onchange="CallChangefunc(this.value)" })
<script>
function CallChangefunc(val) {
//alert("i am a " + obj.options[obj.selectedIndex].text);
//alert(obj.options[obj.selectedIndex].text);
window.location.href = "/Materials/MyMethod/" + val;
}
</script>
In my Controller I have this ActionResult:
public ActionResult MyMethod(string value)
{
return PartialView();
}
My controller is getting called when I change selection but the value coming in is null. I can see on the browser address bar that the expected selected value is being passed. Why is it null in controller? (I tried adding an [HttpPost] to ActionResult with no luck.)
I am developing the website in MVC. I want to display details record one by one. The next record to be displayed when user clicks the Submit Button.
Kindly advise for the same
Code in Controller
// GET: Member_Details
public ActionResult Member_Details(int sysmemberid , string type)
{
Member_Details obj = new Member_Details();
obj=obj.getMemberDetails(Convert.ToInt64(Session["Id"]), sysmemberid, type);// assign values to model
Session["sysmemberid"] = sysmemberid;
Session["type"] = type;
Session["Next"] = obj.next;
return View(obj);
}
[HttpPost]
public void Member_Details(Member_Details obj ,string command)
{
string sysmemberid = Session["sysmemberid"].ToString();
string type1 = Session["type"].ToString();
string sqlQuery = "";
int next = Convert.ToInt32(Session["Next"]);
RedirectToAction("Member_Details", new { sysmemberid = next, type = type1 });
}
View Code :
#using (Html.BeginForm("Member_Details", "Member_Details", FormMethod.Post, new { id = "submitForm" }))
{
// Code to bind model
<button type="submit" id="btnSave" name="command" value="invite" class="btn btn-lg btn-primary btn-block link dtlSubmit">Connect</button>
}
It look like you already have the logic to get the next item and are building the ReditectToAction, however it doesn't look like you are returning it.
Add the ActionResult return value to the Member_Details that handles the POST:
public ActionResult Member_Details(Member_Details obj ,string command)
And return your RedirectToAction:
return RedirectToAction("Member_Details", new { sysmemberid = next, type = type1 });
I'm trying to set up some basic navigation on a web site I'm rewriting and I've run into a brick wall and don't see why this is not working. I'm doing something similar in a half dozen other places but it just ain't working.
What I want to do is if my article has a next and or previous ID I want to show a navigation bar with appropriate forward/reverse navigation arrows or whatever to allow user to navigate pages.
The ViewModel
public class NavViewModel
{
public int NextID { get; set; }
public int PreviousID { get; set; }
public string NextString { get; set; }
public string PreviousString { get; set; }
public bool SelectedMode { get; set; }
public NavViewModel() { }
}
The View
#Html.HiddenFor(model => model.NavigationViewModel.PreviousID)
#Html.HiddenFor(model => model.NavigationViewModel.NextID)
<div class="post-nav">
#if (#Model.NavigationViewModel.PreviousString != null)
{
using (Html.BeginForm("SinglePost", "Article", FormMethod.Post, new { #nvm = Model.NavigationViewModel }))
{
<input type="submit" class="btn btn-default" value="#Model.NavigationViewModel.PreviousString" />
}
}
#if (#Model.NavigationViewModel.NextString != null)
{
using (Html.BeginForm("SinglePost", "Article", FormMethod.Post, new { nvm = #Model.NavigationViewModel }))
{
<input type="submit" class="btn btn-default" value="#Model.NavigationViewModel.NextString" />
}
}
</div>
and the Controller
[HttpPost]
public ActionResult SinglePost(NavViewModel nvm)
{
return RedirectToAction("SinglePost", "Article", new { postID = nvm.PreviousID });
}
I've tried passing back the bool, the IDs, the ViewModel and they all come null or containing null values.
I had this code in a PartialView and because it wasn't working I moved it up a level into the calling view and it has the same result.
You have stated you want to navigate to the next and previous items so using forms and inputs and submitting to a POST method is not appropriate. Instead use a link to navigate to a GET method, passing the ID of the previous or next item.
#if (#Model.NavigationViewModel.PreviousString != null)
{
#Html.ActionLink(Model.NavigationViewModel.PreviousString, "SinglePost", "Article", new { postID = Model.NavigationViewModel.PreviousID }, null)
}
#if (#Model.NavigationViewModel.NextString != null)
{
#Html.ActionLink(Model.NavigationViewModel.NextString , "SinglePost", "Article", new { postID = Model.NavigationViewModel.NextID }, null)
}
The reason your code does not work will be obvious when you inspect the html generated for the <form> tag. Your generating an attribute nvm="YourAssembly.NavigationViewModel" (not a route value). If you used the correct overload to generate route values, which would be
using (Html.BeginForm("SinglePost", "Article", new { #nvm = Model.NavigationViewModel }))
it will still fail because it will generate something similar to (depending on you routes) action="/Article/SinglePost?nvm=YourAssembly.NavigationViewModel" so when you post back, the DefaultModelBinder will try to assign the string "YourAssembly.NavigationViewModel" to parameter nvm, but nvm is a complex object, not a string, so binding will fail.
You could make the POST method work by using
using (Html.BeginForm("SinglePost", "Article", Model.NavigationViewModel))
however this is just degrading performance by posting back unnecessary data and if your model contained properties that were complex objects or collections, it would fail anyway, so don't do it.
Finally, if you want to make the link look like a button, then style it using css.
Try to move hidden inputs into the form
<div class="post-nav">
#if (#Model.NavigationViewModel.PreviousString != null)
{
using (Html.BeginForm("SinglePost", "Article", FormMethod.Post, new { #nvm = Model.NavigationViewModel }))
{
#Html.HiddenFor(model => model.NavigationViewModel.PreviousID)
<input type="submit" class="btn btn-default" value="#Model.NavigationViewModel.PreviousString" />
}
}
#if (#Model.NavigationViewModel.NextString != null)
{
using (Html.BeginForm("SinglePost", "Article", FormMethod.Post, new { nvm = #Model.NavigationViewModel }))
{
#Html.HiddenFor(model => model.NavigationViewModel.NextID)
<input type="submit" class="btn btn-default" value="#Model.NavigationViewModel.NextString" />
}
}
</div>
I want to take a view and instead of opening a new page I want to just open that view inside a Jquery dialog. I was just wondering how it's done or if possible.
HomeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Jquery_Dialog.Models;
using Kendo.Mvc.Extensions;
using Kendo.Mvc.UI;
namespace Jquery_Dialog.Controllers
{
public class HomeController : Controller
{
private IEnumerable<Product> Products
{
get
{
return new List<Product>
{
new Product {ProductID = 1, Name = "Train", Category = "Toy", Price = 29.99M},
new Product {ProductID = 2, Name = "Truck", Category = "Toy", Price = 19.99M},
new Product {ProductID = 3, Name = "Bread", Category = "Food", Price = 2.49M},
new Product {ProductID = 4, Name = "Cookies", Category = "Food", Price = 2.99M}
};
}
}
public ActionResult Index()
{
IEnumerable<Product> productList = Products;
return View(productList);
}
public ActionResult Details(int id)
{
Product model = Products.Where(p => p.ProductID == id).SingleOrDefault();
return Request.IsAjaxRequest() ? PartialView(model) : PartialView(model);
}
public ActionResult About()
{
ViewBag.Message = "Your app description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
}
Index.cshtml
#model IEnumerable<Jquery_Dialog.Models.Product>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.9.1/themes/base/jquery-ui.css " />
<script src="http://code.jquery.com/jquery-1.8.2.js "></script>
<script src="http://code.jquery.com/ui/1.9.1/jquery-ui.js "></script>
<table> #foreach (var item in Model) {
<tr>
<td>
#Html.ActionLink(item.Name, "Details", new { id = item.ProductID }, new { #class = "ajax-details" })
</td>
</tr>
}
</table>
<div id="dialog" title="Title of dialog">
<p>This is the default dialog which is useful for displaying information. The dialog window can be moved, resized and closed with the 'x' icon.</p>
</div>
<script>
$(function () {
$('.ajax-details').on('click', function (e) { // bind to click event
// go get the page using the link's `href`
$.get($(this).prop('href'), function (response) {
$(response).dialog(); // take the response and throw it up in a dialog
// optional: Use jQuery UI's options to specify buttons and other
// settings here as well. It's also probably a good idea to
// bind the close event and $().remove() this element from
// the page on close since the user can click links over and
// over. (prevent DOM overload of hidden elements)
});
e.preventDefault(); // don't let it continue on
});
});
</script>
<script>
$("#dialog").dialog();
</script>
As you can see I have a simple dialog that opens a div but I want to be able to open the details view instead of clicking the ActionLink and going to a different page, I want to be able to click the ActionLink and have it open up in the dialog.
Assuming you make the ActionLink a little more accessible (by using a class name for instance):
#Html.ActionLink(item.Name, "Details", new { id = item.ProductID },
/* htmlAttributes: */ new { #class = "ajax-details" })
You also make a modification to the action so we can fetch partial contents when it's an ajax request:
public ActionResult Details(int id)
{
// this is another way of making sure that AJAX calls get partial content,
// but a normal visit would render the entire page.
return Request.IsAjaxRequest() ? PartialView(model) : View(model);
}
Optional You could also adjust your _ViewStart.cshtml file to do the same if this was common place on the website to render partial views/ajax supplementing:
#{
Layout = IsAjax ? null : "~/Views/Shared/_Layout.cshtml";
}
Now, we wire it up with AJAX. Again, reference the class name we game the link earlier (ajax-details):
$('.ajax-details').on('click',function(e){ // bind to click event
// go get the page using the link's `href`
$.get($(this).prop('href'), function(response){
$(response).dialog(); // take the response and throw it up in a dialog
// optional: Use jQuery UI's options to specify buttons and other
// settings here as well. It's also probably a good idea to
// bind the close event and $().remove() this element from
// the page on close since the user can click links over and
// over. (prevent DOM overload of hidden elements)
});
e.preventDefault(); // don't let it continue on
});
Don't have the opportunity to test it, but should get you in the ball park. if it doesn't get you close enough, let me know and I'll revisit the answer and adjust.