How can i trigger the submit button from partial view without using Html.BeginForm tag in Partial View. Reason being when there are two form tags, that is in main view and partial view, the validation doesn't work.
The important thing is that I want the validation to work.
My Partial View code:
<div id="AddMe">
<div class="row">
#Html.LabelFor(model => model.FirstName)
#Html.TextBoxFor(model => model.FirstName)
#Html.ValidationMessageFor(model => model.FirstName) // this should work
</div>
</div>
<div>
<input name="submit" type="submit" id="submit" value="Save" /> // how do i trigger this from partial view
</div>
My Main View code:
#using (Html.BeginForm("ActionName", "ControllerName", FormMethod.Post))
{
<div class="modal" id="modalId" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
</div>
<div class="modal-body" id="modalbodyId">
#Html.Partial("PartilViewName")
</div>
<div class="modal-header">
<button type="submit">
</div>
</div>
</div>
</div>
}
For Validation purpose, I have added the following lines of code in Main View.
$("#modalId").click(function () {
var form = $("#modalbodyId").closest("form");
form.removeData('validator');
form.removeData('unobtrusiveValidation');
$.validator.unobtrusive.parse(form);
$.ajax({
url: "/ControllerName/ActionName",
type: "POST",
data: $("#modalbodyId").serialize(),
cache: false
});
});
Also just for the records all the models and other Annotations are in place.
Note: Do i need to add the unobtrusive/ajax code inside Partial View.
Related
Say, on a page I have a list of items and a Delete button next to each. Upon clicking, I want to show a pop-up with a confirmation message.
The confirmation dialog and the deletion functionality are put into a view component.
I know I can do like this:
foreach (var item in Model.List)
{
<tr class="row">
<td class="col-12">
#item.Name
<button class="btn btn-danger ml-auto" data-toggle="modal"
data-target="#delete-item-#item.Id">×</button>
<vc:delete-item-dialog id="delete-item-#item.Id" item-id="#item.Id"></vc:delete-item-dialog>
</td>
</tr>
}
But then each delete-item-dialog view component is rendered separately, bloating the size of the generated HTML.
Is it possible to place that view component only in one place, after the end of the list, and provide the item-id parameter more dynamically?
Is it possible to place that view component only in one place, after the end of the list, and provide the item-id parameter more dynamically?
Yeah, you can use ajax to load the view component dynamically. Below is a working demo.
View:
#model List<User>
<table>
#foreach (var item in Model)
{
<tr class="row">
<td class="col-12">
#item.Name
<button class="btn btn-danger ml-auto" onclick="deleteuser(#item.Id)">
×
</button>
</td>
</tr>
}
</table>
<div id="container">
</div>
#section scripts{
<script>
function deleteuser(id){
$.ajax({
type: 'get',
url: 'GetMyViewComponent?id=' + id,
success: function (result) {
$("#container").html(result);
$('.modal').modal('show');
}
})
}
</script>
}
Controller:
public IActionResult UserList()
{
var users = new List<User>
{
new User{ Id = 1, Name = "Mike"},
new User{ Id = 2, Name = "John"},
new User{ Id = 3, Name = "David"},
};
return View(users);
}
public IActionResult GetMyViewComponent(int id)
{
return ViewComponent("PopUp", id);
}
PopUpViewComponent class:
public class PopUpViewComponent : ViewComponent
{
public IViewComponentResult Invoke(int id)
{
return View(id);
}
}
PopUpViewComponent Razor View:
#model int
<div class="modal fade" id="delete-#Model" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLongTitle">Modal title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
Are you sure to delete #Model?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary">Ok</button>
</div>
</div>
</div>
</div>
Result:
I'm trying to find out the best way to create a submit and add button action in a controller.
I have a HttpGet for Create (Submit) but not sure how to do a HttpPost or if Get or Post is even needed:
[HttpGet]
public IActionResult Create()
{
var drafList = _drService.GetDraft().ToList();
var IndexViewModel = new IndexViewModel();
IndexViewModel.Draft = draftList;
IndexViewModel.Published = _drService.GetPublished();
IndexViewModel.Current = _drService.GetCurrent();
return View(IndexViewModel);
}
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text- danger"></div>
<div class="form-group">
<div class="col-md-3">
<label for="asof">As of:</label>
</div>
<div class="col-md-9">
<input name="AsOf" type="date" title="AsOf" class="form-control" />
</div>
</div>
<div class="clearfix col-md-12"></div>
<div class="clearfix col-md-12"></div>
<div class="form-group">
<div class="col-md-2">
<label for="title">Title:</label>
</div>
<div class="col-md-9 col-md-offset-1">
<input type="text" class="form-control" id="title" />
</div>
<div class="col-md-6">
<input type="submit" value="Add" class="btn btn-primary" />
</div>
</div>
</form>
</div>
</div>
I expect when clicking the Add button to perform the actions in the controller and add the record.
I think you can make a controller action with the same name (create() in this case) but with the [httpPost] prefix on the action so that the form would call the post create action when submitted
GET is for non-destructive actions, i.e. the same GET request should return the same response when repeated. For a create, you need to use a POST. Basically, you need to add an action like:
[HttpPost]
public async Task<IActionResult> Create(IndexViewModel model)
{
if (!ModelState.IsValid)
return View(model);
// map the data posted (`model`) onto your entity class
var entity = new MyEntity
{
Foo = model.Foo,
Bar = model.Bar
};
_context.Add(entity);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
My view blade like this :
<a href="javascript:" class="btn btn-block btn-success" #click="modalShow('modal-data')">
Click here
</a>
<data-modal id="modal-data"></data-modal>
If the button clicked, it will call dataModal component (In the form of modal)
dataModal component like this :
<template>
<div class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<!-- modal content data -->
<div class="modal-content modal-content-data">
<form id="form">
<div class="modal-body">
...
</div>
...
<button type="submit" class="btn btn-success" #click="add">
Save
</button>
...
</form>
</div>
<!-- modal content success -->
<div class="modal-content modal-content-success" style="display: none">
<div class="modal-body">
...
</div>
</div>
<!-- modal content failed -->
<div class="modal-content modal-content-failed" style="display: none">
<div class="modal-body">
...
</div>
</div>
</div>
</div>
</template>
<script>
export default{
...
methods:{
add(event){
const data = {
...
}
this.$store.dispatch('add', data)
.then((response) => {
if(response == true)
this.$parent.$options.methods.modalContent('#modal-data', '.modal-content-success')
else
this.$parent.$options.methods.modalContent('#modal-data', '.modal-content-failed')
})
.catch(error => {
console.log('error')
});
}
}
}
</script>
If response = true then modal with class = modal-content-success will appear
If response = false then modal with class = modal-content-failed will appear
I want if response = false, modal with class = modal-content-data still showing. So modal with class = modal-content-failed appears in modal with class class = modal-content-data
How can I do that?
How to order that when response = false, modal with class = modal-content-data still appear?
Please help me
As i can see you are using bootstrap, this worked for me:
<template>
<div>
<div id="modal-example" class="modal" tabindex="-1" role="dialog">
...insert rest of code here as is in your example
</div>
</div>
</template>
And then in your href link tag:
<a href="javascript:void(0)" class="btn btn-block btn-success" data-target="#modal-example" data-toggle="modal">
Show Modal
</a>
I'm trying to validate a changed password partial view in my Index page of my Manage section. Like so,
#inject UserManager<ApplicationUser> UserManager
#model IndexViewModel
#{
ViewData["Title"] = "Manage your account";
}
<div class="row settings">
<div class="col-sm-6">
<h4>Basic information</h4>
#await Html.PartialAsync("BasicInformation", new BasicInformationViewModel(UserManager))
</div>
<div class="col-sm-6">
<h4>Change Password</h4>
#await Html.PartialAsync("ChangePassword", new ChangePasswordViewModel())
</div>
Controller code
[HttpPost("ChangePassword")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ChangePassword(ChangePasswordViewModel model)
{
if (!ModelState.IsValid)
{
return PartialView(model);
}
var user = await GetCurrentUserAsync();
if (user != null)
{
var result = await _userManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation(3, "User changed their password successfully.");
ViewData["StatusMessage"] = TranslationService.TranslateManageMessage(_context, ManageMessageId.ChangePasswordSuccess, "Your password has been changed.");
return PartialView();
}
AddErrors(result);
return PartialView(model);
}
ViewData["StatusMessage"] = TranslationService.TranslateManageMessage(_context, ManageMessageId.Error, "An error has occured.");
return PartialView();
}
So when the user changes his password, the thing I actually want to happen is that the Action get completed and the statusmessage of the partialview ges updated with what happend (succes, error, etc..). So how i have implemented it now it's work as I want it to but the only thing that is bad about this setup is that this happens.
My layout just disappears and I have no idea why. I thought returning a partial view would just reload the page but seems like it's not that.
Could anyone point me in the right direction ? Or give a suggestion how to do this better ?
EDIT:
ChangePassword Partial
#model ChangePasswordViewModel
<form asp-controller="Manage" asp-action="ChangePassword" method="post" class="form-horizontal">
<p class="text-success">#ViewData["StatusMessage"]</p>
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="OldPassword" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="OldPassword" class="form-control" />
<span asp-validation-for="OldPassword" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="NewPassword" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="NewPassword" class="form-control" />
<span asp-validation-for="NewPassword" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="ConfirmPassword" class="form-control" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="submit" class="btn btn-default">Change password</button>
</div>
</div>
#section Scripts {
#{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}
The thing is you are initially loading the partialview inside index view on a get request, when you post to changepassword action it is not an ajax partial post it is a full post and it is not the Index action so it is not using the index view and you are returning a partialview that is not inside any other view so there is no outer view and no layout
UPDATE: since you requested in comments to show how to make it use ajax:
In your index view wrap a div with an id around the partial like this:
<div id="changepassword">
#await Html.PartialAsync("ChangePassword", new ChangePasswordViewModel())
</div>
and you need to include jquery unobtrusive ajax
in the Scripts section which should be in the index view not in the partial
#section Scripts {
#{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
<script src="~/path-to-jqueryajaxunobtrusive"></script>
}
In your ChangePassword view change like this:
#model ChangePasswordViewModel
<form asp-controller="Manage" asp-action="ChangePassword" method="post" class="form-horizontal"
data-ajax="true"
data-ajax-method="POST"
data-ajax-mode="replace"
data-ajax-update="#changepassword">
<p class="text-success">#ViewData["StatusMessage"]</p>
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="OldPassword" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="OldPassword" class="form-control" />
<span asp-validation-for="OldPassword" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="NewPassword" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="NewPassword" class="form-control" />
<span asp-validation-for="NewPassword" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="ConfirmPassword" class="form-control" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="submit" class="btn btn-default">Change password</button>
</div>
</div>
by including the jquery unobtrusive ajax script and adding the data-* attributes as shown that should wire up the form to do an ajax post and the result should update the contents of the div with the id indicated
I have view that has CompanyTable partial view. So this partial view has a CreateBankAccount partial view. Last partial view codes are
#model Invoice.Model.BankAccount
<script src="~/Scripts/jquery-1.7.1.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
Launch demo modal
<div id="#Model.TaxID" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="myModalLabel">Modal header</h3>
</div>
<div class="modal-body">
#using (Ajax.BeginForm("CreateModal", "CompanyController", new AjaxOptions { UpdateTargetId = "modal", HttpMethod = "post" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>BankAccount</legend>
#Html.HiddenFor(model => model.ID)
<div class="editor-label">
#Html.LabelFor(model => model.BankName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.BankName)
#Html.ValidationMessageFor(model => model.BankName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.CorrespondentAccount)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.CorrespondentAccount)
#Html.ValidationMessageFor(model => model.CorrespondentAccount)
</div>
<input type="submit" value="save" />
</fieldset>
}
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
<input type="submit" class="btn btn-primary" value=" Save changes" data-dismiss="modal" />
</div>
</div>
so this partial view is modal popup. My problem is that when I want to creat bankAccount my HttpPost method doesn't work, and I return Index action. How I can fix it?