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.
Related
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.
I have my this in my controller:
[HttpPost]
public IActionResult Save(int IdentifikaceZ, ReklamaceModel model)
{
_db.Add(model);
_db.SaveChanges();
return RedirectToAction("MyMainView");
}
#using (Html.BeginForm("Save", "MyMainView", FormMethod.Post))
{
....
<input id="Insert" name="Insert" value="Insert" type="submit">
<input id="Edit" name="Edit" value="Edit" type="submit">
}
What I need is to Save/Edit info inside Form but I dont know how to tell server to decide which to do. So when I click button A or button B it does same thing. I need it to do seperate things but with same elemetns (elements inside form) Thanks for any help.
The buttons are named, and the way named buttons work is that only the one that is clicked makes it into the POST. As such, you can simply check for the presence of one key or the other in the form data:
if (Request.Form.ContainsKey("Insert"))
// do insert
if (Request.Form.ContainsKey("Edit"))
// do edit
First, add a property string ActionType to your ReklamaceMode model.
Then change your HTML to:
#using (Html.BeginForm("Save", "MyMainView", FormMethod.Post))
{
....
<input id="Insert" name="ActionType" value="Insert" type="submit">
<input id="Edit" name="ActionType" value="Edit" type="submit">
}
Now in your csharp code:
[HttpPost]
public IActionResult Save(int IdentifikaceZ, ReklamaceModel model)
{
// check model.ActionType, it will be either Insert or Edit
}
Add and update operations can be handled with one form and one method as well.
Add id as hidden form control, for existing records this field will have a value which is the relevant record id. For new records the value will be 0 by default (assuming id type is int).
#using (Html.BeginForm("Save", "MyMainView", FormMethod.Post))
{
....
#Html.HiddenFor(x => x.Id)
<input type="submit" name="submit" value="Save" />
}
on the backend check for the id value, if it is > 0 then you trigger update, otherwise it is a new record.
[HttpPost]
public IActionResult Save(int IdentifikaceZ, ReklamaceModel model)
{
if(model.Id > 0)
_db.Update(model);
else
_db.Add(model);
_db.SaveChanges();
return RedirectToAction("MyMainView");
}
That was a simplified implementation, in a real life project you need to do more control over the model before adding/updating. For example it is recommended to use an input model then bind the values to the db model...
if ou still need to use multiple submit buttons in one form to target different backend actions see Multiple Submit Buttons for standard form and ajax forms as well.
Here you can do like this:::
<form method="post" asp-controller="Home">
<a id="Insert" name="Insert" value="Insert" type="submit" asp-action="Delete" asp-route-DeleteID="#model.DeleteID"/>
<a id="Edit" name="Edit" value="Edit" type="submit" asp-action="Edit" asp-route-DeleteID="#model.EditID"/>
</form>
[HttpPost] public IActionResult Delete(int id) {
}
[HttpPost] public IActionResult Edit(int id) {
}
I have a ToDo items dashboard page where I display the ToDo's, their status and some other app info.
I want to have one input where I can add a string value (the ToDo title) and on the button click
have that passed to the controllers Create get method, so it populates the Create views Title input field with that value.
I want to it without a form if that is possible as the dashboard page already has a model which is an IEnumerable, just pass that value as a querystring parameter to the Create pages get view (or is it doable in javascript?).
Im not an MVC expert and also not as familiar with the new tag helper methodologies. Any help in how to structure thiswould be helpful.
Here is the html
<!-- Add Task -->
<div class="input-group input-group-lg">
<input class="form-control" type="text" placeholder="Add task and press enter..">
<span class="input-group-addon">
<a asp-controller="ToDoItems" asp-action="Create" ><i class="fa fa-plus"></i></a>
</span>
</div>
<!-- END Add task -->
here is the new model
public Class MyModel{
public IEnumerable<your old model> Old Model {get; set;}
public string Title {get;set;}
}
You can create a form like so in html with razor syntax
#model MyModel
...
<form action="/Controller/PostTitle/" method="post">
#Html.TextBoxFor(m => m.Title,new {#class = "...", #placeholder="...",
#requried="required"})
<input id="export-btn" type="submit" value="Download" class="btn btn-info" />
</form>
The #TextBoxFor will create a textbox and the lambda lets you use your strongly typed model.
Here is the controller
[HttpPost]
public IActionResult PostTitle(string Title) {
...
}
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>
}
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();
}