I have a simple application in .NET Core MVC, with this simple form. When I try to post the form without refresh whole page only rest the form and got the result of done.
My code trial is:
<form asp-area="" asp-controller="ContactUs" asp-action="UserMessage" data-ajax="true">
<h5>Reach us quickly</h5>
<div class="row">
<span asp-validation-for="UserName" class="text-danger"></span>
<div class="col-sm-6 col-12">
<div class="form-group">
<input asp-for="UserName" class="form-control" name="UserName" placeholder="Enter name" required="required">
</div>
</div>
<span asp-validation-for="Email" class="text-danger"></span>
<div class="col-sm-6 col-12">
<div class="form-group">
<input asp-for="Email" class="form-control" name="Email" placeholder="Enter email" required="required">
</div>
</div>
</div>
<div class="row">
<span asp-validation-for="Msg" class="text-danger"></span>
<div class="col-12">
<div class="form-group">
<textarea asp-for="Msg" name="Msg" class="form-control" rows="7" cols="25" placeholder="Message"></textarea>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12 mt-3">
<button type="submit" class="btn solid-btn" id="btnContactUs">
Send Message
</button>
</div>
</div>
</form>
and my action is:
[HttpPost]
public async Task<IActionResult> UserMessage(ContactUs model)
{
try
{
if (ModelState.IsValid)
{
_context.Add(model);
await _context.SaveChangesAsync();
return NoContent();
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
return NoContent();
}
I want to change return NoContent(); so I can show a message to the user that his message has sent or failed, without refreshing the whole page.
Your form tag is missing some other ajax attributes:
<form method="post"
data-ajax="true"
data-ajax-method="post"
data-ajax-url="#Url.Page("UserMessage", "ContactUs")"
data-ajax-loading="#loading"
data-ajax-mode="replace"
data-ajax-update="#updateDiv">
<!-- form controls -->
<button type="submit" class="btn solid-btn" id="btnContactUs">Send Message</button>
<span id="loading" style="display:none;"> <i class="fas fa-spinner fa-spin"></i></span>
</form>
<div id="updateDiv">
<!-- ajax content will load here -->
<!-- you can put the form inside this div -->
<!-- so after submit the result will replace the form controls -->
</div>
<!-- include jquery libraries -->
#section Scripts {
<script src="https://cdn.jsdelivr.net/npm/jquery#3.5.1/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery-ajax-unobtrusive#3.2.6/dist/jquery.unobtrusive-ajax.min.js"></script>
}
The backend method can return the result as below:
public async Task<ContentResult> OnPostUserMessageAsync(ContactUs model)
{
try
{
if (ModelState.IsValid)
{
_context.Add(model);
await _context.SaveChangesAsync();
return Content("done");
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
return Content("something went wrong...");
}
See Ajax Request Using Inline Attributes
If you don't want the page to be replaced, you'll have to use ajax to post the data via javascript. Posting a form in a browser will replace the current html with the result of the controller, that's just how forms work.
It is also common practice to redirect any successful POST to a different page, so that the user can refresh that page without processing the POST request twice.
thanks for #Laz Ziya i have used his code and modify form to be
instead of
<form method="post"
data-ajax="true"
data-ajax-method="post"
data-ajax-url="#Url.Page("UserMessage", "ContactUs")"
data-ajax-loading="#loading"
data-ajax-mode="replace"
data-ajax-update="#updateDiv">
to be
<form asp-area="" asp-controller="ContactUs" asp-action="UserMessage" data-ajax-loading="#loading" data-ajax-mode="replace" data-ajax-update="#updateDiv" data-ajax-success="Success" data-ajax-failure="Failure" data-ajax="true">
for post form without refresh ,you need to use ajax in your code.
Related
I am developing a web application using ASP.NET Core 5 MVC, in which I seek to make multiple submissions of POST type forms to my controller from a view that receives an IEnumerable (from a model called Result) with which I am filling in dynamically the values of the inputs of each form.
However, when I send one of those forms from the view through the controller, in the controller I only receive an object from the model with all the null or empty values, which tells me that it seems that this data was never sent through the form to my controller.
Is there a better way to accomplish this or how do I pass these values from multiple forms to my controller? In advance an apology if what I am doing is already totally wrong, I have been learning ASP.NET Core MVC for a few days.
CONSIDERATIONS
What I seek to achieve is that the user can enter multiple values that belong to the same model in the same view, since each form although it is the same seeks to update a different record or column in the same model or table, so when the submit of the form is sent to the Controller the view does not change, and only the records in the database are updated with each submit in the view. If there is a better way or correct way to do this, I am willing to change the logic, because as I mentioned, I have been using the Framework for little.
Explained the problem and my goal, I will explain in greater detail the flow and code mentioned:
From the Mechanical method of my controller, I return a list of Objects to their corresponding View, which are brought by a DataBaseContext:
// CONTROLLER() that passes an enumerable list of Objects to View Mechanical.cshtml
public IActionResult Mechanical()
{
IEnumerable<Result> objList = _db.Results;
return View(objList);
}
In the Mechanical() view, I get this list of objects and iterate over it through a forEach() loop, where for each object I create a form that directs to the same controller method called Update(), in which I get the values of the object in null and empty (that being my problem):
// VIEW
#model IEnumerable<FatForm.Models.Result>
#if (Model.Count() > 0)
{
#foreach(var result in Model)
{
<form method="post" asp-action="Update">
<input asp-for="#result.Id" hidden />
<input asp-for="#result.Type" hidden />
<input asp-for="#result.FatId" hidden />
<div class="border p-3">
<div class="form-group row">
<h2 class="text-black-50 pl-3">Edit Result</h2>
</div>
<div class="row">
<div class="col-12">
<div class="form-group row">
<div class="col-3">
<label asp-for="#result.Section"></label>
</div>
<div class="col-3">
<label asp-for="#result.Procedure"></label>
</div>
<div class="col-3">
<label asp-for="#result.ResultDescription"></label>
</div>
<div class="col-3">
<label asp-for="#result.Passed"></label>
</div>
</div>
<div class="form-group row">
<div class="col-3">
<input asp-for="#result.Section" class="form-control" />
</div>
<div class="col-3">
<input asp-for="#result.Procedure" class="form-control" />
</div>
<div class="col-3">
<input asp-for="#result.ResultDescription" class="form-control" />
<span asp-validation-for="#result.ResultDescription" class="text-danger"></span>
</div>
<div class="col-3">
<input asp-for="#result.Passed" class="form-control" />
<span asp-validation-for="#result.Passed" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<div class="col-8 offset-2 row">
<div class="col">
<input type="submit" class="btn btn-info w-75" value="Save" />
</div>
</div>
</div>
</div>
</div>
</div>
</form>
}
}
else
{
<p>No results created yet</p>
}
#section Scripts{
#{
<partial name="_ValidationScriptsPartial" />
}
}
I'm supposed to be looking to send the form values to the following Update() controller method, in which I get all the object values to null:
// POST Update
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Update(Result obj)
{
if (ModelState.IsValid)
{
_db.Results.Update(obj);
_db.SaveChanges();
//return RedirectToAction("Index");
}
return View(obj);
}
As I explained at the beginning, I hope can help me by indicating what I am doing wrong or in what way I should do it. Thanks in advance for your help.
Model binding looks through the sources for the name pattern prefix.property_name. If nothing is found, it looks for just property_name without the prefix.In your code,the asp-for tag helper will generate the name like:result.propertyName.It could not match with backend model. You need use [Bind(Prefix ="result")] to specify the prefix:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Update([Bind(Prefix ="result")]Result obj)
{
//do your stuff...
}
As for receive IEnumerable parameter in action, firstly you need change your razor view to move the form outside the foreach loop, then you need change the asp-for tag helper tattribute to #Model.ToList()[index].PropertyName:
#model IEnumerable<Result>
#if (Model.Count() > 0)
{
<form method="post" asp-action="Update">
#for (int i = 0; i < Model.Count(); i++)
{
<input asp-for="#Model.ToList()[i].Id" hidden />
<input asp-for="#Model.ToList()[i].Type" hidden />
<input asp-for="#Model.ToList()[i].FatId" hidden />
<div class="border p-3">
<div class="form-group row">
<h2 class="text-black-50 pl-3">Edit Result</h2>
</div>
<div class="row">
<div class="col-12">
<div class="form-group row">
<div class="col-3">
<label asp-for="#Model.ToList()[i].Section"></label>
</div>
<div class="col-3">
<label asp-for="#Model.ToList()[i].Procedure"></label>
</div>
<div class="col-3">
<label asp-for="#Model.ToList()[i].ResultDescription"></label>
</div>
<div class="col-3">
<label asp-for="#Model.ToList()[i].Passed"></label>
</div>
</div>
<div class="form-group row">
<div class="col-3">
<input asp-for="#Model.ToList()[i].Section" class="form-control" />
</div>
<div class="col-3">
<input asp-for="#Model.ToList()[i].Procedure" class="form-control" />
</div>
<div class="col-3">
<input asp-for="#Model.ToList()[i].ResultDescription" class="form-control" />
<span asp-validation-for="#Model.ToList()[i].ResultDescription" class="text-danger"></span>
</div>
<div class="col-3">
<input asp-for="#Model.ToList()[i].Passed" class="form-control" />
<span asp-validation-for="#Model.ToList()[i].Passed" class="text-danger"></span>
</div>
</div>
</div>
</div>
</div>
}
<div class="form-group row">
<div class="col-8 offset-2 row">
<div class="col">
<input type="submit" class="btn btn-info w-75" value="Save" />
</div>
</div>
</div>
</form>
}
else
{
<p>No results created yet</p>
}
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Update(IEnumerable<Result> obj)
{
return View("Mechanical", obj);
}
Result:
Here you need to add FromBody attribute in your action method parameter.
public IActionResult Update([FromBody]Result obj)
{
.....
}
Code searchView and PartialResultView
SearchView
#model Shared.Model.Search.GLSearch
#{
ViewData["Title"] = "Search GL";
}
<!-- start page title -->
<div class="row">
<div class="col-12">
<div class="page-title-box">
<div class="page-title-right">
<ol class="breadcrumb m-0">
<li class="breadcrumb-item">UBold</li>
<li class="breadcrumb-item">Forms</li>
<li class="breadcrumb-item active">Elements</li>
</ol>
</div>
<h4 class="page-title">Search Customer</h4>
</div>
</div>
</div>
<!-- end page title -->
<form asp-action="" asp-controller="" method="post">
<div class="row">
<div class="col-lg-12">
<div class="card-box">
<div class="form-row">
<div class="form-group col-md-2">
<label asp-for="Name" class="col-form-label"></label>
<input asp-for="Name" type="text" class="form-control" />
</div>
<div class="form-group col-md-2">
<label asp-for="Code" class="col-form-label"></label>
<input asp-for="Code" type="text" class="form-control" />
</div>
<div class="form-group col-md-3">
<label asp-for="GLSectionId" class="col-form-label">Section </label>
<select asp-for="GLSectionId" asp-items="#(new SelectList(Model.glSections,"Id","Name"))" class="form-control">
<option value="">Choose</option>
</select>
</div>
<div class="form-group col-md-3">
<label asp-for="GLGroupId" class="col-form-label">Group</label>
<select asp-for="GLGroupId" asp-items="#(new SelectList(Model.glGroups,"Id","Name"))" class="form-control">
<option value="">Choose</option>
</select>
</div>
<button type="button" id="search" class="btn btn-primary waves-effect waves-light">Search</button>
</div>
</div> <!-- end card-box -->
</div> <!-- end col -->
</div> <!-- end row -->
</form>
<div id="view-all"></div>
Search_PartiaView
#model PagedResult<Shared.Model.Masters.GLMaster.GLViewModel>
#{
}
#if (Model == null || Model.RowCount == 0)
{
<p>No results found</p>
}
else
{
<div class="col-lg-12">
<div class="card-box">
<h4 class="header-title">Customers</h4>
<p class="sub-header">
</p>
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead>
<tr>
<th data-priority="1">#</th>
<th data-priority="3">Name</th>
<th data-priority="6">Code</th>
<th data-priority="6">Section</th>
<th data-priority="6">Group</th>
<th data-priority="6">
<a onclick="showInPopup('#Url.Action("AddOrEditGL","GLMaster",new {area = "Masters"},Context.Request.Scheme)','New GL')" class="btn btn-success text-white"><i class="fas fa-random"></i> New GL</a>
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.Results)
{
<tr>
<th scope="row">#item.Id</th>
<td>#item.Name</td>
<td>#item.Code</td>
<td>#item.GLSection</td>
<td>#item.GLGroup</td>
<td>
<div>
<a onclick="showInPopup('#Url.Action("AddOrEditGL","GLMaster",new { area= "Masters",id = item.Id},Context.Request.Scheme)','Update GL')" class="btn btn-info text-white"><i class="fas fa-pencil-alt"></i> Edit</a>
<form asp-area="Masters" asp-action="DeleteGL" asp-route-id="#item.Id" onsubmit="return jQueryAjaxDelete(this)" class="d-inline">
<input type="submit" value="Delete" class="btn btn-danger" />
</form>
</div>
</td>
</tr>
}
</tbody>
</table>
</div> <!-- end table-responsive-->
</div> <!-- end card-box -->
</div> <!-- end col -->
<!-- Responsive Table js -->
}
Partial View (AddEditGL)
#model Shared.Model.Masters.GLMaster.GLModel
#{
Layout = null;
ViewData["Title"] = "Add Customer";
}
<div class="row">
<div class="col-lg-12">
<div class="card-box">
<form asp-action="AddOrEditGL" asp-controller="GLMaster" asp-area="Masters" asp-route-id="#Model.Id" onsubmit="return jQueryAjaxPost(this);">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="#Model.Id" />
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" type="text" class="form-control">
<span asp-validation-for="Name" class="text-danger"></span>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label asp-for="NameLang" class="control-label"></label>
<input asp-for="NameLang" type="text" class="form-control">
<span asp-validation-for="NameLang" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-6 offset-md-3">
<input type="submit" value="Submit" class="btn btn-primary btn-block" />
</div>
</div>
</form>
</div> <!-- end card-box -->
</div> <!-- end col -->
</div> <!-- end row -->
I have View with Partial view (is for results in table) . When i click Edit button in Search_PartiaView
i need to open popup (Partial View (AddEditGL))
and data should be loaded ajax and submit the button after update.. I need to use jquery unobtrusive validation in popup and also without refresh the page ..Please let me know hw to do..Thanks
EDIT
I Have implemented similar to this Ajax crud popup
I Have Main view and Partial view. Also AddOrEdit View for Add/edit Master.
My Current solution works.. But inmy main view i have filter based on 2 filds.
After add/edit grid load all the result but if filter applied i also need to filter the grid ..
My Javascript code Here:
jQueryAjaxPost = form => {
try {
$.ajax({
type: 'POST',
url: form.action,
data: new FormData(form),
contentType: false,
processData: false,
success: function (res) {
if (res.isValid) {
$('#view-all').html(res.html) --- here actually data coming all without filter
$('#form-modal .modal-body.p-4').html('');
$('#form-modal .modal-title').html('');
$('#form-modal').modal('hide');
showAll(4, 1); --- it is the javascript fuction call to call the
api again
}
else
$('#form-modal .modal-body.p-4').html(res.html);
},
error: function (err) {
console.log(err)
}
})
//to prevent default form submit event
return false;
} catch (ex) {
console.log(ex)
}
}
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddOrEditGL(int id,GLModel glModel)
{
if (ModelState.IsValid)
{
var mappedGL = _mapper.Map<GLDTO>(glModel);
//Insert
if (id == 0)
{
await _glService.CreateGL(mappedGL);
}
//Update
else
{
await _glService.UpdateGL(mappedGL);
//Call Update
}
// How do i filter the based on Main view form controls
return Json(new { isValid = true, html = Helper.RenderRazorViewToString(this, "_GLViewAll", null) });
}
return Json(new { isValid = false, html = Helper.RenderRazorViewToString(this, "AddOrEditGL", glModel) });
}
my Current solution call the api again ( 2 server calls) one for update and another for call update table .. i need to do the same in single call ..Please help to do?
Note: I dont need complete solution , I only need to how to get the AddOrEditGL Controller post method Main view form control text fieds text to filter in DB
If you want to update/add and show the searched data in one request,The quick way is to copy the SearchGLPartial code to the AddOrEditGL function and pass the pageSize,pageIndex,name,code,GLSectionId and GlGroupId by ajax:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddOrEditGL(int id,GLModel glModel,string name,string code,string GLSectionId,string GlGroupId...)
{
if (ModelState.IsValid)
{
var mappedGL = _mapper.Map<GLDTO>(glModel);
//Insert or
//Update
//copy the SearchGLPartial code here and return view with data
}
return Json(new { isValid = false, html = Helper.RenderRazorViewToString(this, "AddOrEditGL", glModel) });
}
If you just do not want to remove showViewAll() jquery,I think you could set session for the filter condition when you first search the data in SearchGLPartial action.Then in your AddOrEdit action,get the session and set the correct url.Finally,you could get the url in ajax success response:
public IActionResult SearchGLPartial(string name,string code,string GLSectionId,string GLGroupId)
{
HttpContext.Session.SetString("name", name);
HttpContext.Session.SetString("code", code);
HttpContext.Session.SetString("GLSectionId", GLSectionId);
HttpContext.Session.SetString("GLGroupId", GLGroupId);
var data = Gllist.Where(a => a.Name.Contains(name) & a.Code.Contains(code)).FirstOrDefault();//filter by yourself
return PartialView("_Search", data);
}
AddOrEdit:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddOrEditGL(int id,GLModel glModel)
{
if (ModelState.IsValid)
{
//Insert
//Update
var name = HttpContext.Session.GetString("name");
//other session data...
return Json(new { isValid = true, url="Home/Index?name="+name+"&code="+xxx+"&...."});
}
return Json(new { isValid = false, html = Helper.RenderRazorViewToString(this, "AddOrEditGL", glModel) });
}
Then your ajax:
success: function (res) {
if (res.isValid) {
window.location.href = response.url;
}
else
$('#form-modal .modal-body.p-4').html(res.html);
},
I recommend using the jQuery Unobtrusive AJAX library. Its very easy to use:
It can fetch partial views and place them in a container of your choice using the
data-ajax-update="#panel"
Click here
<div id="panel"></div>
This is an example of a controller action that would return the modal:
public IActionResult GetEditModal() => Partial("ViewName");
Then when the modal is placed in your container, using the data-ajax-success attribute a callback method is called, parse the form using the jQuery Unobtrusive Validation like this:
function SuccessCallback() {
//You can also use the keyword "this" instead of getting the form with jquery
$.validator.unobtrusive.parse($(this));
//or
$.validator.unobtrusive.parse($form);
}
Very useful resources : Link - jQuery Unobtrusive AJAX - GitHub
Update
What i did in previous projects is to include an empty edit modal (a modal with an empty body) then using the library and the data-ajax-update i would replace the body of that modal every time the user pressed the edit button on a table item like this:
<div class="modal fade" id="eModal" data-keyboard="false" data-backdrop="static">
<div class="modal-dialog modal-lg">
<div class="modal-content id="modalContentE">
<div class="modal-header">
<h4 class="modal-title"><i class="nav-icon fas fa-edit"></i> Edit Data</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true"><i class="fas fa-times-circle text-white"></i></span>
</button>
</div>
//New data goes here
</div>
</div>
</div>
And your add Modal would be a regular full modal and each time the user use it to submit data, clear the form.
I am using ASP.NET Core MVC with identity but I want to perform my own logic on login. So, what I did is I scaffolded the login page and make my own design.
Here is my form:
#page
#model LoginModel
#{
ViewData["Title"] = "Login";
}
<div class="login-page cnt-bg-photo overview-bgi" style="background-image:
url('#Url.Content("~/assets/img/banner-1.jpg")')">
<div class="container">
<div class="row">
<div class="col-lg-12">
<div class="forgot-box contact-2 clearfix">
<div class="login-header clearfix">
<div class="pull-left">
<h4>Login</h4>
</div>
</div>
<!-- <div role="alert"
class="alert alert-danger font-12">
<strong>Oops!!</strong> Invalid Email or Password
</div>-->
<p>Please enter your user name and password to login</p>
<form action="/Account/Login" method="POST">
<div class="form-group">
<label asp-for="Input.Email"></label>
<input asp-for="Input.Email" class="form-control" />
<span asp-validation-for="Input.Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Input.Password"></label>
<input asp-for="Input.Password" class="form-control" />
<span asp-validation-for="Input.Password" class="text-danger"></span>
</div>
<div class="form-group w-46-f float-left">
<div class="form-check checkbox-theme pull-left">
<div class="checkbox">
<label asp-for="Input.RememberMe">
<input asp-for="Input.RememberMe" />
#Html.DisplayNameFor(m => m.Input.RememberMe)
</label>
</div>
</div>
</div>
<div class="form-group float-right">
<label class="pull-right forgotpassword-label">Forgot Password ?</label>
</div>
<div class="clearfix"></div>
<button type="submit" class="btn btn-color btn-md pull-right">Login</button>
</form>
<div class="clearfix"></div>
<div class="text-center p-t-46 p-b-20 font-14">
<span class="txt2">
Or Login with
</span>
</div>
<div class="login100-form-social flex-c-m">
<form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="#Model.ReturnUrl" method="post">
#foreach (var provider in Model.ExternalLogins)
{
if (provider.DisplayName == "Google")
{
<button type="submit" class="btn login100-form-social-item flex-c-m bg3 m-r-5" style="display: inline-block" name="provider" value="#provider.Name" title="Log in using your #provider.DisplayName account"><i class="fab fa-google"></i></button>
}
else if (provider.DisplayName == "Facebook")
{
<button type="submit" class="btn login100-form-social-item flex-c-m bg1 m-r-5" style="display: inline-block" name="provider" value="#provider.Name" title="Log in using your #provider.DisplayName account"> <i class="fab fa-facebook-f"></i></button>
}
else if (provider.DisplayName == "Twitter")
{
<button type="submit" class="btn login100-form-social-item flex-c-m bg2 m-r-5" style="display: inline-block" name="provider" value="#provider.Name" title="Log in using your #provider.DisplayName account"> <i class="fab fa-twitter"></i></button>
}
}
</form>
</div>
<hr>
<div class="text-center forgotpassword-label font-14">
Not a member?
<a href="#Url.Action("Packages","Account")">
<strong>Sign up now</strong>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
#section Scripts {
<partial name="_ValidationScriptsPartial" />
}
Now I want to post the login data to my own controller action. But it get null on my action method. Parameters are not filled with actual data of the form.
Here is my controller method:
[HttpPost]
public IActionResult Login(LoginModel.InputModel model)
{
//My logic
return View();
}
Why does it get model parameter as null?
Well, specifically, the source of it being null is the fact that in the Razor Page, all the input names begin with Input. because for the code-behind, the model being bound to is LoginModel and the data in those inputs will be bound to the InputModel property (Input) there. However, in your controller, you're binding directly to InputModel, and InputModel has no Input member. As a result, none of the posted inputs match anything and are therefore not bound.
If you're going to post from a Razor Page, you need to do it to a Razor Page. There's just too many inconsistencies between Razor Pages and MVC to line everything up correctly. If you you want to use a controller, you're going to need a view specifically geared for that, not your Razor Page.
There seems to be a couple ways to do it. I really wanted this video to work for me. https://www.youtube.com/watch?v=oHWEs8XWA2U
Searching the web, I found it hard to find this question being asked recently, so I am wondering if newer and improved (easier) ways have been implemented.
Here is my Home controller
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult PartialViewTest()
{
return PartialView();
}
[HttpPost]
public ActionResult PartialViewTest(Person person)
{
return View();
}
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
}
Here is my view I want the pop up on
#{
ViewBag.Title = "Contact";
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button type="button" class="btn-block" style="width:225px">Modal </button>
<div class="modal fade" id="myModal" role="dialog" data-url='#Url.Action("PartialViewTest","Home")'></div>
<script type="text/javascript">
$(document).ready(function () {
$('.btn-block').click(function () {
var url = $('#myModal').data('url');
$.get(url, function (data) {
$("#myModal").html(data);
$("#myModal").modal('show');
});
});
});
</script>
Here is my partial view
<div class="container">
<div class="row">
<div class="col-sm-4"></div>
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title" id="myModal-label">Bootstrap Dialog</h4>
<div>
<div class="modal-body">
<div class="form-group">
<input type="text" placeholder="enter name" class="form-control" id="text" name="text" />
</div>
<div class="form-group">
<input type="text" placeholder="enter name id" class="form-control" id="text" name="text" />
</div>
<div class="form-group">
<input type="text" placeholder="adress" class="form-control" id="text" name="text" />
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" id="btnOK" onclick="">OK</button>
<button class="btn btn-default" data-dismiss="modal" id="btnCancel">Cancel</button>
</div>
</div>
</div>
</div>
</div>
</div>
When I click the button as seen in the image above. Nothing happens. Why doesn't the popup view appear?
If this is not the best way to go about it, could you provide a link to a tutorial for the most professional, proper, and easiest way to accomplish this?
If this is Bootstrap then your partial view's modal HTML is broken. modal-body and modal-footer are under modal-header. I'm pretty sure modal-header, modal-body and modal-footer needs to be direct children of modal-content and that needs to child of modal-dialog and that needs to be child of modal and you have those container divs there wrapping them.
Edit:
Your problem is that you are loading your jquery and bootstrap after you try to use them. That <script> is run before your scripts are loaded. You need to put that into scripts section.
Camilo Terevinto notice you need remove jQuery script tag from your view. You already have jQuery in a bundle.
"When I click the button as seen in the image above. Nothing happens. " -> I have just tried your code and it works, modal is openning. Has Camilo Terevinto comment you should press 12 in chrome, and check for errors in console tab.
After modal open, what is missing in your code?
Use "Person" model in modal, create a post form and set button ok to submit
#model Person //Should contain person model namespace
#Html.BeginForm("PartialViewTest", "Home", FormMethod.Post)
{
<div class="container">
<div class="row">
<div class="col-sm-4"></div>
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title" id="myModal-label">Bootstrap Dialog</h4>
<div>
<div class="modal-body">
<div class="form-group">
#Html.TextBoxFor(p => p.name)
</div>
<div class="form-group">
#Html.TextBoxFor(p => p.adress)
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary" id="btnOK" onclick="">OK</button>
<button class="btn btn-default" data-dismiss="modal" id="btnCancel">Cancel</button>
</div>
</div>
</div>
</div>
</div>
</div>
}
Your post action should redirect to Index.
[HttpPost]
public ActionResult PartialViewTest(Person person)
{
/* do things */
return RedirectToAction("Index");
}
I'm trying to rerender the partialview ONLY in my modal, after the ajax request.
But when I get the response everything is rerendered and the only thing showing is the partialview..
PartialView:
#{
var bidModel = new BidModel();
}
<div>
<div class="row">
#if (ViewBag.Message != null)
{
<div class="alert alert-success">#ViewBag.Message</div>
}
</div>
<span class=" alert-danger">
#Html.ValidationSummary()
</span>
<div class="form-group">
<input name="Bidder" asp-for="#bidModel.Bidder" value="#User.Identity.Name" type="hidden" />
<input name="AuctionId" asp-for="#bidModel.AuctionId" type="hidden" id="auctionId" />
<label asp-for="#bidModel.Amount" />
<input name="Amount" asp-for="#bidModel.Amount" />
</div>
</div>
Controller:
public async Task<IActionResult> AddBid(BidModel Bid)
{
var result = await _bidBusinessInterface.CreateBidAsync(Bid, Bid.AuctionId);
if (result)
{
ViewBag.Message = "Bud lagt!";
}
else
{
ViewBag.Message = "Bud förlågt!";
}
return PartialView("_BidCreatorPartial");
}
And then we have the modal where i want to rerender the partialview:
<div class="modal fade" id="bidModal" tabindex="-1" role="dialog" aria-labelledby="bidModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="bidModalLabel">Lägg bud</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form asp-action="AddBid" asp-controller="Home" method="POST" data-ajax="true" data-ajax-update="frmBid">
<div id="frmBid">
<partial name="_BidCreatorPartial" model="#bidModel"/>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Avbryt</button>
<button type="submit" class="btn btn-primary">Spara</button>
</div>
</form>
</div>
</div>
</div>
</div>
As I said, what I want to accomplish is to rerender the form, so that the message can be shown in the modal.
What happens is it renders a whole white page with only the form and the message.
You didn't actually post your form itself, but my best guess is that you're just doing a standard old HTML form post. That's always going to cause the page to change, and then since you're only returning a partial view, and not a full view, your page gets replaced with just that snippet of HTML.
What you want requires AJAX. You need to catch the submit event on the form, and instead of letting it go as normal, you make an AJAX request with the form data serialized. Then, in the success callback of your AJAX request, you'll need to query the element you want to replace the HTML of from the DOM and change its innerHTML value to what's returned from the AJAX request.
Since an ASP.NET Core project comes with jQuery out of the box, I'm going to assume you can use that:
$('#MyForm').on('submit', function (e) {
e.preventDefault();
var $form = $(this);
$.ajax({
url: $form.attr('action'),
method: 'post',
data: $form.serialize(),
success: function (html) {
$('#frmBid').html(html);
}
});
});