Calling ActionResult from PartialView never hits - c#

Pretty new with MVC and going through a few tutorials. Have done the following:
Add a controller called CustomerController.
Add 2 methods
public ActionResult Render()
{
// Go to a third party WebAPI and get the results in a List
return PartialView("CustomerList", custList);
}
public ActionResult SomeTest()
{
Response.Redirect("Somepage");
}
I then add a page (LandingView.cshtml) and create a PartialView called CustomerList and add the below code to the LandingView page
#Html.Action("Render", "Customer")
When i view this page it renders the page with a list of customers. The HTML for the PartialView is
#using (Html.BeginForm("SomeTest", "Customer"))
{
<div class="container">
#foreach (var i in Model)
{
<a href="#i.Url">
<div class="product-grid__item__name">#i.Title</div><br />
<div class="product-grid__item__price">#i.Price.ToString("C")</div>
</a>
<input type="button" id="btnGo" value="Go" />
}
</div>
}
When i click the button it never hits the SomeTest method? In debug mode i have put a breakpoint on Render and SomeTest, Render hits on page load but when clicking Go it never hits the SomeTest method?
What am i missing here?

Set the 'type' attribute value of the input element to "submit" not "button". This will trigger the form submission on click.
<input type="submit" id="btnGo" value="Go" />
You may experience some build errors because the SomeTest() controller method is expecting a return value of type ActionResult.

Related

ViewModel becomes null after creating a POST request in ASP.NET MVC

I'm creating an edit user page which will modify a user's existing role name within the web app. However, upon POST request, the Edit's view model becomes null after containing the values from the form (checked this using breakpoints).
I then get a prompt message in Visual Studio saying:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
Microsoft.AspNetCore.Mvc.Razor.RazorPage\<TModel\>.Model.get returned null.
UserController.cs
[HttpPost]
public async Task<IActionResult> Edit(IFormCollection formCollection)
{
try
{
await _userControllerService.EditAzureUser(formCollection);
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
UserControllerService.cs
public async Task EditAzureUser(IFormCollection formCollection)
{
string id = formCollection["User.PrincipalId"];
string appRoleId = formCollection["User.AppRoleId"];
//Edit logic here
}
Edit.cshtml
#model CreateUserViewModel
#{
ViewData\["Title"\] = "Edit User";
}
<label asp-for="User.PrincipalDisplayName" class="col-form-label">Name: </label>
<input asp-for="User.PrincipalDisplayName" value="#Model.User.PrincipalDisplayName" readonly class="form-control"/>
<label class="col-form-label">Role: </label>
<select asp-for="User.AppRoleName" title="Select Role" required class="form-control" style="margin-bottom: 1rem"/>
#{
foreach(var role in AppRole.Roles)
{
if(Model.User.AppRoleName == role)
{
<option value="#role" selected>#role</option>
} else
{
<option value="#role">#role</option>
}
}
}
<input type="hidden" asp-for="User.AppRoleId"/>
<input type="button" value="Cancel" class="btn btn-secondary btn-sm" onclick="goBack()" />
<input type="submit" class="btn btn-info btn-sm" />
Your POST method doesn't return a model to the view, but the view expects a non-null CreateUserViewModel, which it uses in the markup. My guess is that's why you're getting the NRE.
However, if the POST was successful, why are you going back to the edit page anyway? Either send them to another page (often a user list if this is an admin facility, or a "thank you page if it's for the users themselves), or modify the Razor in this view to show a message saying the edit was successful. Either way, you don't need the model.

How to get the current Action name inside the View

In my _Layout page, I have got a search form and each controller has an index view. When the user clicks the search button, it searches in the current index view.
I want to show the search field if the user is index view if they go to other views, I wanted to hide it.
In my _Layout
<form asp-action="Index" method="get" class="navbar-form form-inline navbar-right">
<input class="form-control mr-sm-2" id="search" name="search" type="search" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success my-2 my-sm-0" id="BtnSearch" name="BtnSarch" type="submit">Search</button>
</form>
I am using JQuery at the moment but it is quite difficult to put every single view
$("#search").hide();
$("#BtnSearch").hide();
Basically, in my _Layout page, I wanted to show or hide Search form if the user is in the index view.
how can i get current view name in _Layout view, please?
Basically, in my _Layout page, I wanted to show or hide Search form if the user is in the index view.
Try with below codes :
#if ("Index".Equals(ViewContext.RouteData.Values["Action"].ToString()))
{
<form asp-action="Index" method="get" class="navbar-form form-inline navbar-right">
<input class="form-control mr-sm-2" id="search" name="search" type="search" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success my-2 my-sm-0" id="BtnSearch" name="BtnSarch" type="submit">Search</button>
</form>
}
This sounds like it is the ideal candidate for mvc tag helpers.
You will need to create a class which inherits from TagHelpers and override the process method.
[HtmlTargetElement(“website-search”)]
Public class Search : TagHelper
{
Public WebsiteContext Info { get; set; }
Public override void Process(TagHelperContext context, TagHelperOutput output)
{
Output.TagName = “section”;
Output.Content.SetHtmlContent(“ HTML for your search form “);
Output.TagMode = TagMode.StartTagAndEndTag;
}
}
In order to get the controller and action you will need to add a property to the tag helper:
[HtmlAttributeNotBound]
[ViewContext]
Public ViewContext ViewContext { get; set; }
Now that you have the view context in place, you can look to do something like the following:
If(ViewContext.RouteData.Values[“action”]) != “Index”)
{
Output.SuppressOutput();
}
You can then reference this by putting website-helper in your view.
Please see the following link for an intro on tag helpers https://learn.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/intro?view=aspnetcore-2.2
There is the following stack overflow question detailing how to get the controller and action executed against : Knowing what route the user is on inside TagHelper in .NET Core
Hope this helps
You can add a hidden input to layouts file and assign to it an id.
Then you can get action and controller name from anywhere:
<input type="hidden" value="#this.ViewContext.RouteData.Values["action"].ToString()" />
<input type="hidden" value="#this.ViewContext.RouteData.Values["controller"].ToString()" />
So if you don't use them in JS, you can declare a variable and show your form when action is Index.
Hope to help.
If you want to show the search form only in specific views, I would not base this on the view name. In the future, you might also need it in other views. So, why not simply add a flag to show the search form to your ViewBag. It will mean, setting this flag in every "Index" action, but you will be more flexible with where to show it.
Controller:
public ActionResult Index()
{
this.ViewBag.ShowSearch = true;
// … your normal code
return this.View();
}
_Layout.cshtml
#if (this.ViewBag.ShowSearch == true) // explicitly check for true, so not having set the flag in the ViewBag will not pose a problem, i.e. null != true.
{
<form action="">#* … *#</form>
}

Different buttons in view to perform different operation in mvc

I am facing some problem in MVC
Inside view I have 2 buttons, one is for final submit and the other is for adding dynamic content to the view. Again both are used to post the form. I wanted to know how these would be used in controller.
example
If I click final submit, it will redirect to some view or any other operation and also if I click add button in the same view I want to return to the same view.
note: I am using both buttons to post the same action.
<input type="submit" name="actionBtn" value="add value" />
<input type="submit" name="actionBtn" value="finalsubmit" />
in Action
public ActionResult YourPostAction(string actionBtn)
{
if(actionBtn == "Add Value")
{
}
else if(actionBtn == "finalSubmit")
{
}
}
Another way if you want ( You have to play with name but different way)
#using (Html.BeginForm())
{
<input type="hidden" name="actionName" id="hdnAction" />
<input type="submit" value="test" name="actionBtn" onclick="setThis('test')" />
<input type="submit" value="test1" name="actionBtn" onclick="setThis('test1')"/>
}
<script language="javascript">
function setThis(obj) {
document.getElementById('hdnAction').value = obj;
}
</script>
In controller action
[HttpPost]
public ActionResult Index(string actionName)
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
return View();
}

Post from IFRAME and Return to Partial View in Parent

My apologies if this is a dupe, but I haven't been able to find an exact solution to my problem.
Goal:
Upload a file, do work, return results. Easy right?
Problem:
I've been working on this for a few days without any luck. I've tried XmlHttpRequest, but due to the browser limits (can't get away with forcing end-users and clients to use IE10 or higher) that doesn't seem to be an option.
What I've spent most of my time on is uploading via iframe. I've gotten the upload piece working fine. What I need to happen is after doing work with the file, results should be returned back to the parent window and a partial view.
----------------------Index--------------------
Partial View Data Entry----Partial View Results
-----Upload iframe----------Results from file--
Here's what I have for code:
DataEntry.cshtml
<div>
...textboxes, radiobuttons, etc...
<iframe id="uploadFrame" class="seamless" frameborder="0" src='#Url.Action("UploadFile")'></iframe>
</div>
UploadFile.cshtml
<script type="text/javascript">
$(document).ready(function () {
$("#uploadFile").click(function () {
$("#field1").val(window.parent.document.getElementById("field1").value);
$("#field2").val(window.parent.document.getElementById("field2").value);
...other fields...
$("#fileForm").submit();
});
$("#file").change(function () {
if ($("#file").val() != "") {
$("#uploadFile").removeAttr("disabled");
}
else {
$("#uploadFile").attr("disabled", "disabled");
}
});
});
</script>
<form id="fileForm" action='#Url.Action("UploadFile")' method="post" enctype="multipart/form-data">
<div>
Please use this template (link) to upload a list of employees and dependents.
</div>
<div class="center">
<br />
<input type="hidden" id="field1" name="field1" />
<input type="hidden" id="field2" name="field2" />
<input type="file" id="file" name="file" /><br /><br />
<input type="button" disabled="disabled" id="uploadFile" name="uploadFile" value="Upload File" class="greenButton" />
</div>
</form>
HomeController.cs
public ActionResult UploadFile()
{
return View();
}
[HttpPost]
public ActionResult UploadFile(String field1, String field2, HttpPostedFileBase file)
{
...do work...
//return View("UploadFile", object);
//return View("Result", object);
//return ?
}
The return part is where I'm stuck. What can I do to return the object to the partial view, without having the partial view load within the iframe?
Any ideas or at least a point in the right direction would be appreciated. Even a link to a duplicate!
The Partial View would always be refreshed when it is returned. It is how things work. Unless you do the upload differently using AJAX.
Please refer to the link below:
Ajax.BeginForm in MVC to upload files
Alternatively by using the same logic described in your question, you could put some additional logic in your view, like for example using TempData as flag which is set in the action controller, to determine the partial view is for upload or showing result.
Then, in your partial view, using that flag to render the UI accordingly.
Hope it helps.
I ended up finding a solution based on the concepts from these questions:
Get JSON text from HTML iframe
How to display action result of iframe in parent window
Basically, I modified the HomeController > UploadFile action to return JSON text
JsonResult result = new JsonResult();
result.Data = listOfEmployeesWithRates.ToList();
result.ContentType = "text/plain";
return result;
Then in jQuery, I check to see if the iframe contains the JSON when it loads.
//uploadFrame
$("#uploadFrame").load(function(){
if ($("#uploadFrame").contents().find("pre").html() != null) {
//pass json via ajax call to Result partial view
//refresh iframe
$("#uploadFrame").attr('src', $("#uploadFrame").attr('src'));
}
});

MVC 4 Clicking a Button to get a Partial Page

I am creating a page with a search property. The user will enter a number in the search field and hit the submit button. I need to be able to call a controller method in order to run the search code.
For now I am just trying to hit a partial page to just get some functionality in there. Below is the code I have so far. As of now nothing happens when I click the button. I hear Ajax was something to use so I have been playing with that a little. I am still learning the framework so bear with me.
<div class="span6 roundedCorners">
<div class="row-fluid Title">
<div class="span6">
Search Area
</div>
</div>
<div class="row-fluid individual">
<div class="row-fluid">
<div class="span4"><span class="pull-right fieldDescription">Number</span></div>
<div class="span8"><input /></div>
</div>
<div class="row">
<div class="span12" id="adminSubmitButton">
#using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "_adminDetails" }))
{
<button type="submit" class="btn btn-inverse">Submit</button>
}
</div>
</div>
</div>
Your form is empty, it only has a submit button. You will need to move your search button inside the form. Something like this:
Model
public class SearchModel
{
[Required]
public string Query { get; set; }
}
View
#model SearchModel
...
#using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "_adminDetails" }))
{
#Html.EditorFor(m => m.Query)
#Html.ValidationMessageFor(m => m.Query)
<button type="submit" class="btn btn-inverse">Submit</button>
}
<div id="_adminDetails"></di>
Note: Make sure you have an element with id _adminDetails
Your controller will have to take the model and perform the search. Example:
Controller
[HttpPost]
public ActionResult Index(SearchModel model)
{
//do something with your model (perform search)
return View(results);
}
Alright, I have it partially working now. When i click the button I am able to call the controller method using:
function adminButton()
{
$("#adminDetails").load("#Url.Action("ControllerMethod", "ControllerName")");
}
The #Url.Action helper allows me to call the method which returns a partial view. This is a step in the right direction. But from what I am reading I should be able to use the following Url helper instead of the #Url.Action:
"#Url("ControllerMethod", "ControllerName")/" + submitButton.Value
I have tried some variations and I am still doing some reading to figure out a solution. Is there a helper that would let me do this?
Thanks for the help guys. I did end up getting my problem solved friday evening. Basically what I did was load the partial page when the button was clicked through the help of JS/jQuery and the Razor #Url.Action() to call the controller method that returns a PartialView:
<script type="text/javascript">
function adminButton()
{
var link = "#Url.Action("ControllerMethod", "ControllerClass", new {
number = -1})";
var num = parseInt($("#number").val());
link = link.replace(-1, num);
$("#details").load(link);
}
</script>

Categories

Resources