MVC CORE [HttpPost] method not being called by View - model-view-controller

I have this link in a View page:
#{
var link = Url.RouteUrl("newOrgNodes", new { controller = "Nodes", Action = "PostBundleNodes", n_qty = 2, org_id = 20 });
Create Nodes
}
In Program.cs I have the following custom route mapping:
app.MapControllerRoute(name: "newOrgNodes",
pattern: "Nodes/PostBundleNodes/{n_qty}/{org_id}",
defaults: new { controller = "Nodes", action = "PostBundleNodes" } );
My Controller has 2 [HttpPost] methods, one of which is:
[HttpPost(), ActionName("PostBundleNodes")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> PostBundleNodes(NodeWriteViewModel newnodes, int n_qty, int org_id)
{
// code here
}
When I hover over the button in the View (parameters hard coded for now) the URL is what I expected it to be but when I click on the button, it loads a blank page instead of hitting the breakpoint in the PostBundleNodes Post method. I thought using [ActionName] in the controller would suffice but no. What am I doing wrong?

Related

MVC 5 - URL.Action Route Bind Issue

I'm using PagedList.Mvc to create Ajax pagination of my data. However I'm having a problem with an URL.Action that is added to a data-href attribute, after the partial view has been return.
When the page loads for the first time this issue doesn't occur, it's only after I have made an ajax request using the pagination results that url.action doesn't seem to bind correctly.
This is the action that the URL.Action should link to (note the 'Route' Attribute):
[Route("Project/{code}/Request/{number}")]
public ActionResult Details(string code, int number)
{
if (number == 0 || code == null) return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
var viewModel = _requestLogic.GetIrDetailsViewModel(code, number);
if (viewModel == null) return HttpNotFound();
return View(viewModel);
}
On the main view I add an Html.Action to this Action:
[HttpGet]
public PartialViewResult GetProjectRequests(string code, int page = 1, int pageSize = 10)
{
var viewModel = _requestLogic.GetRequestsForProject(code, page, pageSize);
return PartialView("_ProjectRequestsList", viewModel);
}
This Action is also used by the ajax call for the tables pagination, hence the page and pageSize arguments.
Inside this partial view I render a table with the model data, adding the data-href attribute to each row like so:
#foreach (var item in Model)
{
<tr class='clickable-row' data-href='#Url.Action("Details", new {number = item.RequestNo})'>
<td>....
}
This will render the data-href attribute like so:
However after I make a successful ajax call and the html for the partial is replaced. This attribute values doesn't resolve in the same way.
It ends up like this:
Any idea why this is happening?
I bind a double click attribute to any row with the class .clickable-row which is what makes use of this data-href attribute. Hence why I need this to work.
Cheers,
Thanks to #RosdiKasmin I have solved the problem. I have added a route attribute to the partial view action that is used on the initial page load and via the ajax call. Like so:
[HttpGet]
[Route("Project/{code}/")] // <- I've added this.
public PartialViewResult GetProjectRequests(string code, int page = 1, int pageSize = 10)
{
var viewModel = _requestLogic.GetRequestsForProject(code, page, pageSize);
return PartialView("_ProjectRequestsList", viewModel);
}
// GET: Requests/Details/5
[Route("Project/{code}/Request/{number}")]
public ActionResult Details(string code, int number)
{
if (number == 0 || code == null) return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
var viewModel = _requestLogic.GetIrDetailsViewModel(code, number);
if (viewModel == null) return HttpNotFound();
return View(viewModel);
}
This means that the Url.Action makes use of the existing URL when creating the action link.

Post selected dropdown to controller MVC using AJAX or other way

I need to post the selected item's id from dropdown to post method of same controller and the render the content in same view
PLease help me fast :(
My View
#Html.DropDownListFor(x => x.hotelmodel.SelectedHotelId, Model.hotelmodel.DrpHotelList)
<div>
<p>#Model.percentageoccupied</p>
<p>#Model.Revenue</p>
<p>#Model.UnSold</p>
<div>
MY HttpGetMethod
[HttpGet]
public ActionResult Counter()
{
var personid = ((PersonModel)Session["PersonSession"]).PersonId;
var model = new CounterforModel();
model.hotelmodel.DrpHotelList = iCommonservice.GetHotelListByPersonId(personid);
return View(model);
}
My HttpPostMethod
[HttpPost]
public ActionResult Counter(int id)
{
var result = iCommonservice.LoadCounter(id);
model.percentageoccupied = Convert.ToInt32(result[0].percentageoccupied);
model.Revenue = Convert.ToDecimal(result[0].Revenue);
model.UnSold = Convert.ToInt32(result[0].UnSold);
return View(model);
}
In your POST method pass your View Model as parameter. Then you can use the posted hotelModel.SelectedHotelId for what you need, update the model values and pass your updated model to the View.
public ActionResult Counter(CounterforModel model)
{
var result = iCommonservice.LoadCounter(model.hotelmodel.SelectedHotelId);
model.percentageoccupied = Convert.ToInt32(result[0].percentageoccupied);
model.Revenue = Convert.ToDecimal(result[0].Revenue);
model.UnSold = Convert.ToInt32(result[0].UnSold);
return View(model);
}
If you want, you can use #Ajax.BeginForm() or jQuery .ajax() to make an ajax call to your action. You can look here.

Sitecore context not loaded in custom controller

I followed this tutorial, and created this code:
using Glass.Sitecore.Mapper;
using Sitecore.Mvc.Controllers;
using Sitecore.SecurityModel;
using SitecoreCMSMVCBase.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace SitecoreCMSMVCBase.Controllers
{
public class CommentController : SitecoreController
{
ISitecoreContext _context;
ISitecoreService _master;
public CommentController()
: this(
new SitecoreContext(),
new SitecoreService("master"))
{
}
/// <summary>
/// This constructor can be used with dependency injection or unit testing
/// </summary>
public CommentController(ISitecoreContext context, ISitecoreService master)
{
_context = context;
_master = master;
}
[HttpGet]
public override ActionResult Index()
{
var model = _context.GetCurrentItem<CommentPage>();
return View(model);
}
[HttpPost]
public ActionResult Index(Comment comment)
{
var webModel = _context.GetCurrentItem<CommentPage>();
if (ModelState.IsValid)
{
var masterModel = _master.GetItem<CommentPage>(webModel.Id);
if (masterModel.CommentFolder == null)
{
CommentFolder folder = new CommentFolder();
folder.Name = "Comments";
using (new SecurityDisabler())
{
_context.Create(masterModel, folder);
}
masterModel.CommentFolder = folder;
}
using (new SecurityDisabler())
{
comment.Name = DateTime.Now.ToString("yyyyMMddhhmmss");
//create the comment in the master database
_master.Create(masterModel.CommentFolder, comment);
webModel.CommentAdded = true;
}
}
return View(webModel);
}
}
}
Models are identical with tutorial, so I will not paste them.
My route configuration looks like this:
routes.MapRoute(
"CommentController", // Route name
"Comment/{action}/{id}", // URL with parameters
new { controller = "Comment", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
When I navigate to /comment I see this exception:
Glass.Sitecore.Mapper.MapperException: Context has not been loaded
I tried with commenting my route specification (as there was nothing about routes in tutorial), and then error is different (throwing by Sitecore CMS itself):
The requested document was not found
Do you know how to load Sitecore context into custom Controller, and make this simple example work? I was looking everywhere but couldn't find any good answer...
I think this is more a Glass setup issue, rather than an MVC routing problem.
To setup Glass, you need to initialise the context in your application start method in your Global.asax file.
var loader = new Glass.Sitecore.Mapper.Configuration.Attributes.AttributeConfigurationLoader(
"Glass.Sitecore.Mapper.Tutorial.Models, Glass.Sitecore.Mapper.Tutorial");
Glass.Sitecore.Mapper.Context context = new Context(loader);
For other Glass-setup related stuff I recommend following the first tutorial on the glass.lu website.
http://www.glass.lu/tutorials/glass-sitecore-mapper-tutorials/tutorial-1-setup/
This method doesn't need Glass at all!
First step is to set your route in Global.asax file.
routes.MapRoute(
"DemoController", // Route name
"Demo/{action}/{param}", // URL with parameters
new { controller = "Demo", action = "Index", param = "", scItemPath = "/sitecore/content/DemoHomePage" } // Parameter defaults
);
Notice that controller is not taken as parameter, but is fixed, to prevent handling it by Sitecore. More info here and here. Notice that there is one additional parameter - scItemPath. It contains path to item which by default will be included in page context.
Having this route our traffic from /demo is handled by DemoController and Index action. Inside this action all you need is to add is this line:
Sitecore.Data.Items.Item item = Sitecore.Mvc.Presentation.PageContext.Current.Item;
item variable will contain your Sitecore item pointed by scItemPath.
And that's all - it should work well now - hope it helps!

MVC 3 How to tell what view a controller action is being called from-

Is there a way to tell what view a controller action is being called from?
For example, I would like to use "ControllerContext.HttpContext.Request.PhysicalPath" but it returns the path in which the controller action itself is located:
public ActionResult HandleCreateCustomer()
{
// Set up the customer
//..code here to setup the customer
//Check to see of the calling view is the BillingShipping view
if(ControllerContext.HttpContext.Request.PhysicalPath.Equals("~/Order/BillingShipping"))
{
//
return RedirectToAction("OrderReview", "Order", new { id = customerId });
}
else
{
return RedirectToAction("Index", "Home", new { id = customerId });
}
}
If you have a fixed number of locations that it could possibly be called from, you could create an enum where each of the values would correspond to a place where it could have been called from. You'd then just need to pass this enum value into HandleCreateCustomer, and do your condition statement(s) based on that.
At the moment I am using something of the sort:
In the View I am populating a TempData variable using:
#{TempData["ViewPath"] = #Html.ViewVirtualPath()}
The HtmlHelper method ViewVirtualPath() is found in the System.Web.Mvc.Html namespace (as usual) and is as follows and returns a string representing the View's virtual path:
public static string ViewVirtualPath(this HtmlHelper htmlHelper)
{
try{
return ((System.Web.WebPages.WebPageBase)(htmlHelper.ViewDataContainer)).VirtualPath;
}catch(Exception){
return "";
}
}
I will then obviously read the TempData variable in the controller.
I found another way.
In the controller you want to know what page it was called from.
I added the following in my controller
ViewBag.ReturnUrl = Request.UrlReferrer.AbsolutePath;
Then in the View I have a 'Back' button
#(Html.Kendo().Button().Name("ReturnButton")
.Content("Back to List").Events(e => e.Click("onReturn"))
.HtmlAttributes(new { type = "k-button" })
)
Then the javascript for the onReturn handler
function onReturn(e) {
var url = '#(ViewBag.ReturnUrl)';
window.location.href = url;
}

assigning variables in the View in ASP MVC 3

Actually I'm very new to ASP.NET MVC and I need your help.
Here I have some Create Method that takes an argument from the URL to use it as id:
in 'vote' controller :
public ActionResult Create(int id)
{
Meeting meeting = db.Meetings.Find(id); // get the object
ViewBag.meetingID = meeting.meetingID; // get its id and assign it to a ViewBag
return View();
}
and I would like to do something like :
vote.meetingID = #ViewBag.meetingID
in the model so that is directly assgin this property without excplicitely typing it from the HTML view (I mean #Html.EditorFor(model=>meetingID) )
The question is not that clear, but try this.
You could pass the entire model to the view.
Controller:
public ActionResult Create(int id)
{
Meeting meeting = db.Meetings.Find(id); // get the object
ViewData["myMeeting"] = meeting;
return View();
}
To use in the view you can declare it as a variable at the top of the view:
Declare:
#
{
var meetingData = ViewData["myMeeting"] as Meeting;
}
Usage:
<div>
#meetingData.meetingID
</div>

Resources