Info Modal after Confirmation Modal in Razor Pages - c#

I want to show an info modal that says "Record successfully deleted." after clicking the button inside a Confirmation Modal.
Here is my code to show the confirmation modal
Controller
public IActionResult Delete()
{
return PartialView("_ModalDelete");
}
_ModalDelete.cshtml
#using Data.ViewModels.Modal
#using (Html.BeginForm())
{
#await Html.PartialAsync("_ModalHeader", new ModalHeader { Heading = "Delete" })
<div class="modal-body form-horizontal">
Are you sure you want to delete this record?
</div>
#await Html.PartialAsync("_ModalFooter", new ModalFooter { SubmitButtonText = "Delete" })
}
Example Screenshot:
This seems to be okay on this part. No issues encounter. But after clicking the Delete button, it will show my modal like a whole view. See below:
Here is my code:
Controller - for post of data after clicking delete button
[HttpPost]
public async Task<IActionResult> Delete(int id)
{
try
{
var validationResult = await new RegionHandler(_regionService).CanDelete(id);
if (validationResult == null)
{
await _regionService.DeleteById(id);
return PartialView("_ModalInfo", new Tuple<string, string>(Constants.Message.Info, Constants.Message.RecordSuccessDelete));
}
ModelState.AddModelError(validationResult);
}
catch (Exception ex)
{
var exceptionMessage = await Helpers.GetErrors(ex, _emailService);
ModelState.AddModelError(new ValidationResult(exceptionMessage));
}
ModelState.AddModelError(string.Empty, "Invalid delete attempt.");
return PartialView("_ModalInfo", new Tuple<string, string>(Constants.Message.Error, ModelState.ToString()));
}
_ModalInfo.cshtml
#using Data.ViewModels.Modal
#model Tuple<string,string>
#await Html.PartialAsync("_ModalHeader", new ModalHeader { Heading = Model.Item1})
<div class="modal-body form-horizontal">
#Model.Item2
</div>
#await Html.PartialAsync("_ModalFooter", new ModalFooter { CancelButtonText = "OK", OnlyCancelButton = true})

With the submission of your form you are making a roundtrip to the server, which will issue a completely new html page (even if your html code is only partial).
To remove the question-modal and replace it with a message-modal in the original page (region-list), you will have to use javascript (for the post AND the replacement).
If you want to stick with the roundtrip, make the Delete method return a full html page, which integrates the message-dialog (like the region-list intergrates the question-dialog).

Finally found an answer with this. So basically I just revised everything so that the model validation from controller will still be there.
Heres my code:
For the table markup
<tr>
<td>
#Html.DisplayFor(modelItem => item.RegionName)
</td>
<td>
#Html.DisplayFor(modelItem => item.RegionCode)
</td>
<td>
#Html.DisplayFor(modelItem => item.RegionKey)
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td class="text-center">
<a asp-action="Edit" asp-route-id="#item.RegionId"><i class="fa fa-edit text-info"></i></a>
<i class="fa fa-trash text-danger"></i>
</td>
</tr>
where it call a javascript function below:
#section Scripts{
<script type="text/javascript">
function showDeleteConfirmation(message, event, id) {
event.preventDefault();
showConfirmationModal(message).then(function () {
$("#id").val(id);
$("#formDelete").submit();
});
}
</script>
}
where showConfirmationModal() is a promise function that uses bootbox.js (library that wraps bootstrap modal for easier usage).
site.js
function showConfirmationModal(message, title = "Confirm", size = "medium", confirmText = "Yes", canceltext = "No") {
const deffered = $.Deferred();
bootbox.confirm({
title: title,
message: message,
size: size,
buttons: {
confirm: {
label: confirmText,
className: "btn-success"
},
cancel: {
label: canceltext,
className: "btn-danger"
}
},
callback: function (result) {
if (result) {
deffered.resolve(result);
} else {
deffered.reject(result);
}
}
});
return deffered.promise();
}
On callback, it will submit the hidden form below. Ofcourse don't forget to set the id to be deleted.
Hidden form for Delete action
<form method="post" asp-action="Delete" id="formDelete" class="hidden">
<input type="hidden" id="id" name="id" />
<input type="hidden" asp-for="Item1.RegionName" name="RegionName" />
<input type="hidden" asp-for="Item1.Page" name="Page" />
<input type="hidden" asp-for="Item1.SortBy" name="SortBy" />
<input type="hidden" asp-for="Item1.SortOrder" name="SortOrder" />
</form>
To show the Info Message(for success delete), I created a PartialView to make the modal show if theres a data set in a Temporary Data or TempData. This was added under the _Layout.cshtml page:
_ModalScriptsInit.cshtml
#using Data.Utilities
#{
var text = TempData[Constants.Common.ModalMessage];
if (text != null && !text.Equals(string.Empty))
{
<script type="text/javascript">
showDefaultModal("#text");
</script>
}
}
So in my controller once successfully delete I will just set the TempData with its key as shown below:
Controller
[HttpPost]
public async Task<IActionResult> Delete(int id, RegionSearchViewModel searchViewModel)
{
try
{
var validationResult = await new RegionHandler(_regionService).CanDelete(id);
if (validationResult == null)
{
await _regionService.DeleteById(id);
TempData[Constants.Common.ModalMessage] = Constants.Message.RecordSuccessDelete;
return RedirectToAction(nameof(List), searchViewModel);
}
ModelState.AddModelError(validationResult);
}
catch (Exception ex)
{
var exceptionMessage = await Helpers.GetErrors(ex, _emailService);
ModelState.AddModelError(new ValidationResult(exceptionMessage));
}
ModelState.AddModelError(string.Empty, "Invalid delete attempt.");
return RedirectToAction(nameof(List), searchViewModel);
}
I am not sure yet if this is the best way so far. Please give suggestion on how to improve this code. Thanks!

Related

ajax.beginform - OnFailure dont return partial view

Hello
I am using ajax.beginform and i want to return partial view with the
errors inside. whan I change the status code to something bad to fire
the OnFailure method it is not returning the partial view. the view that called:
<fieldset>
#using (Ajax.BeginForm("SaveSocioDetails", "Student", new AjaxOptions { HttpMethod = "POST", OnSuccess = "firstsuccess",OnFailure = "sociodetailsfail", UpdateTargetId="partialsocio" ,LoadingElementId = "div_loading" }, new { #enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div id="partialsocio">
#Html.Action("PartialSocioDetails", "Student", new { SpId = ViewBag.SpId })
</div>
<div id="div_loading" style="display:none;">
<img src="#Url.Content("~/Content/Pic/Spinner.gif")" alt="" />
</div>
<button class="btn btn-primary" type="submit" value="Submit">שלח</button>
}
<input type="button" name="next" class="next action-button" value="Next" />
my controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SaveSocioDetails(SpSocio socio) // ajax for 1 step in socio
{
socio.StudentId = sStudentId; // bind student id to socio model
bool sociook = SocioDetValid(socio);
// add validation
if (ModelState.IsValid && sociook)
{
SaveSocioModel(socio);
Response.StatusCode = 200;
}
else
Response.StatusCode = 300; // return error to client the model is not valid
return PartialView("~/Views/Student/Socio/SocioDetails.cshtml", socio); // return the partial view of the forn with validation messages
}
js:
<script>
$(document).ready(function ()
{
});
function firstsuccess(data) {
console.log(data);
$('#partialsocio').html(data);
console.log('this is ajaxSuccess');
}
function sociodetailsfail(bdata) {
console.log('this is ajaxfail');
console.log(data);
$('#partialsocio').html(data);
}
</script>
please help me out with this
If your request fails then you will get the callback from server including problem definition inside sociodetailsfail witin java-script, where you can put logic to display error messages that you receive from server bdata object to user

Image Browser Asp.net MVC 5

I trying to create ImageBrowser in Asp.net MVC 5. Problem is when I try to switch to another picture.
Here's my code:
In View:
#model Katalog.Models.Model
#{
Model.enumerator = 0;
int count = Model.ImageList.Count;
int index = 1;
}
....
<table>
<tbody>
<tr>
<td> #index/#count </td>
....
</tr>
<tr>
#using (Html.BeginForm("previous", "Home",FormMethod.Post))
{
<td>
<input type="hidden" name="number" value="1" />
<input value="<" type="submit" onclick="ImageLeft()" class="buttonLeftRight"/>
</td>
}
<td>#{Html.RenderPartial("~/Views/Shared/ImageViews.cshtml", Model);}</td>
<td>
#using (Html.BeginForm("next", "Home", FormMethod.Post))
{
#Html.HiddenFor(a => a.ImageList)
#Html.HiddenFor(a => a.enumerator)
<input type="submit" class="buttonLeftRight" onclick="ImageRight()"/>
}
</td>
</tr>
</tbody>
</table>
....
<script>
function ImageRight()
{
#{ Model.enumerator++; }
}
</script>
My Controller
....
public ActionResult next(Katalog.Models.Model model)
{
model = MyModel;
return View("Searcher",model);
}
....
and my PartialView:
#model Katalog.Models.Model
<img id="foto" class="imgFinded" src="#Model.ImageList[#Model.enumerator]"/>
When I click Next button my model.ImageList is empty. Why?
The reason your models ImageList property is null is because your generating a hidden input #Html.HiddenFor(a => a.ImageList) which generates
<input name="ImageList" value="System.Collections.Generic.List[]String" .. />
which will not bind to your collection in the POST method (when something is not binding, always insect the name and value attribute of the form controls your generating.
In order to bind to that list you would need to generate an input for each item in the list using a loop.
The other issue is that your scripts does nothing at all. Your model is server side code, and you cannot increment the value of a model property using javascript - you need to send a request to the controller to do that.
Since you sending the collection of images to the view, there is no need to make a request back to the server - you can just update the src attribute of the <img> tag.
Change the model to
public class Model
{
public int InitialIndex { get; set; }
public int ImageCount { get { return ImageList.Count; } }
public List<string> ImageList { get; set; }
}
and then the view can be just (the partial is unnecessary)
<img id="foto" class="imgFinded" src="#Model.ImageList[#Model.InitialIndex ]"/>
<button type="button" id="previous">Previous</button>
<button type="button" id="next">Next</button>
and the scripts
var imagelist = #Html.Raw(Json.Encode(Model.ImageList));
var index = #Html.Raw(Json.Encode(Model.InitialIndex));
var max = #Html.Raw(Json.Encode(Model.ImageCount));
var image = $('#foto');
// Set the initial state of the buttons
if (index === 0) {
$('#previous').prop('disabled', true);
} else if (index === max) {
$('#previous').prop('disabled', true);
}
$('#next').click(function() {
$('#previous').prop('disabled', false);
index++;
image.attr('src', imagelist[index]);
if (index === max) {
$(this).prop('disabled', true);
}
})
$('#previous').click(function() {
$('#next').prop('disabled', false);
index--;
image.attr('src', imagelist[index]);
if (index === 0) {
$(this).prop('disabled', true);
}
})
The code #{Model.enumerator++;} and #{Model.enumerator--;} within the two Javascript functions is server side code so it will only be executed once when the view is rendering on the server and before it is passed to the client browser. So triggering the onclick="ImageRight()" by pressing the submit button will not change the server side value.
You could instead try to post the current index to the action in your controller and increment or decrement it depending on which action has been invoked.
#using (Html.BeginForm("Next", "Home", FormMethod.Post))
{
#Html.HiddenFor(a=>a.CurrentIndex)
<input type="submit" class="buttonRightLeft"/>
}
#using (Html.BeginForm("Previous", "Home", FormMethod.Post))
{
#Html.HiddenFor(a=>a.CurrentIndex)
<input type="submit" class="buttonLeftRight"/>
}
public ActionResult Next(int CurrentIndex)
{
// Get the NEXT image and return as model
model = MyModel;
return View("Searcher",model);
}
public ActionResult Previous(int CurrentIndex)
{
// Get the PREVIOUS image and return as model
model = MyModel;
return View("Searcher",model);
}

Post one item from model collection to controller method in MVC

I have a view where I display a list of users, setting the model as:
#model List<MyProject.Models.User>
In this view I want to be able to choose to perform an action on a specific user, i.e. post to the controller that I want to disable a user. How do I post the specific User object to the controller?
This is what I've got so far, but I can't see how to post the specific object from the collection:
#foreach (var c in Model)
{
<tr>
<td>#c.Username</td>
<td>#c.IsEnabled</td>
<td>
#using (Html.BeginForm("DisableUser", "UserManagement"))
{
<input type="submit" value="Disable" class="btn btn-primary"/>
}
</td>
</tr>
}
My controller has the signature:
public ActionResult DisableUser(User user)
Rather than posting back all propeties of User, you can just add a route value in the BeginForm() method to post back the ID or the User. Assuming that property is namedUserId`, then
#foreach (var c in Model)
{
<tr>
....
<td>
#using (Html.BeginForm("DisableUser", "UserManagement", new { id = c.UserId ))
{
<input type="submit" value="Disable" class="btn btn-primary"/>
}
</td>
</tr>
}
and the controller method would be
public ActionResult DisableUser(int id)
{
// Get the User based on id, update it and redirect
}
You could also consider using ajax to submit the value, which would allow the user to stay on the same page and continue to 'disable' other User objects without need to make a redirect, in which case the code might be
#foreach (var c in Model)
{
<tr>
....
<td>
<button type="button" class="disable" data-id="#c.UserId">Disable</button>
</td>
</tr>
}
var url = '#Url.Action("DisableUser", "UserManagement")';
$('.disable').click(function() {
var row = $(this).closest('tr');
$.post(url, { id: $(this).data('id') }, function(result) {
if(result) {
// for example, remove the row from the table
row.remove();
} else {
// Oops
}
}).fail(function (result) {
// Oops
});
});
and the controller method would be
public JsonResult DisableUser(int id)
{
// Get the User based on id and update it
return Json(true);
// or if the update failed - return Json(null);
}
The simple way to disable a user is to use a Html.ActionLink instead of the form - you should be able to see plenty of examples of this in the template code. The action link could redirect to a confirmation page or you could just disable the user and redirect to a message page saying "the user has been disabled".
I better way is to use AJAX. You can do this with jQuery or you could use the MVC Ajax form or Ajax Action Link. I would recommend that you google MVC Ajax Action Link examples.
You may also want to style the link by setting it's class to the Bootstrap 'btn' class.
There is a way to post a single user
#foreach (var c in Model)
{
<tr>
<td>#c.Username</td>
<td>#c.IsEnabled</td>
<td>
#using (Html.BeginForm("DisableUser", "UserManagement"))
{
<input type="text" name="Username" value="#c.Username">
<input type="text" name="IsEnabled" value="#c.IsEnabled">
<input type="hidden" name="id" value="#c.id">
<input type="submit" value="Disable" class="btn btn-primary"/>
}
</td>
</tr>
}
The ideas is same,due to lack of time, please proceed according to your requirements, use the Class Property name as input name, the action will get the value
First of all you must not declare Html.BeginForm inside of any loop to post the object to controller.
#using (Html.BeginForm("DisableUser", "UserManagement"))
{
#foreach (var c in Model)
{
<tr>
<td>#c.Username</td>
<td>#c.IsEnabled</td>
<td>
<input data-id="#c.Id" type="submit" value="Disable" class="clsBtnPost btn btn-primary"/>
</td>
</tr>
}
}
This is for your reference
$(document).ready(function(){
$(".clsBtnPost").click(function(){
var userId = $(this).data("id");
$.ajax({
contentType: 'application/json; charset=utf-8',
dataType: 'json',
type: 'POST',
url: 'ControllName/MethodName', //Your Url
data: { 'userId': userId },
success: function () {
alert("successfully called.");
},
failure: function (response) {
alert("Error");
}
});
});
});
You can go with the Ajax Call to pass a single object to controller.

change the button text without page refreshing in mVC4

I am doing my application in MVC. In my view i have one textbox named as EmailId, and one Submit button. If i enter the Email id and submit the button,the Label of the button want to change as Verify and the text box should be cleared without refreshing the page.
My view page is
<div class="sign" id="email">
#using (Html.BeginForm("Randa", "BU", FormMethod.Post))
{
<div class="sign1">
<div class="sign2" style="height:267px;width:562px;margin-left:214px" id="cdiv">
#Html.TextBox("EmailId","", new {#placeholder ="Enter the Email id",id="txtemail "})<br /><br />
<input type="submit" name="submit" value="Sign Up" id="btn" onclick="addbutton()" class="addbutton"/>
</div>
</div>
}
</div>
<div class="drnd" id="rnd" style="display:none">
#using (Html.BeginForm("Ra_verify", "BU", FormMethod.Post))
{
<div class="sign1">
<div class="sign2" style="height:267px;width:562px;margin-left:214px" id="cdiv">
#Html.TextBox("Getran", "", new { #placeholder = "Enter the Randam", id = "txtrnd" })<br /><br />
<input type="submit" name="submit" value="Verify" id="btnrnd" class="addbutton" />
</div>
</div>
}
</div>
}
<script type="text/javascript">
var btxt = "Verified";
document.getElementById("#btn").innerHTML = btxt;
</script>
<script type="text/javascript">
function addbutton()
{
($("#email").hide())
$("#rnd").show();
}
</script>
My controller code is
public ActionResult Randa()
{
return View();
}
[HttpPost]
// send the randam No to the Entered mail id . Store the mail id and randam no into tbl_bussiness table;
public ActionResult Randa(string EmailId, string submit)
{
string userId = System.Configuration.ConfigurationManager.AppSettings["UserTypeId"];
int typeid = Convert.ToInt32(userId);
if (ModelState.IsValid)
{
if (submit != null && EmailId != null)
{
EmailManager.SendConfirmationEmail(EmailId);
tbl_BusinessUser b = new tbl_BusinessUser();
b.EmailId = EmailId;
b.RandomNumber = (int)Session["rnd"];
b.UserTypeId = typeid;
b.CreateDTTM = System.DateTime.Now;
db.tbl_BusinessUser.Add(b);
db.SaveChanges();
ViewBag.message = "Please check ur Mail for randam no.Enter random in textbox ";
}
else
{
ModelState.AddModelError("", "Error");
}
}
return View();
}
public ActionResult Ra_verify()
{
return View();
}
[HttpPost]
// check the random no with table random no ,if match redirect to registration create page
public ActionResult Ra_verify(int EmailId, string submit)
{
if (submit != null)
{
// int c = Convert.ToInt32(EmailId);
tbl_BusinessUser b = new tbl_BusinessUser();
var tbra = db.tbl_BusinessUser.Where(x => x.RandomNumber == EmailId).FirstOrDefault();
//var tbram = Convert.ToInt32(tbra);
return RedirectToAction("Create", "BU");
}
return View();
}
Can anyone please help me?
Thanks in Advance.
We have to use Ajax whenever we want to update the value in the webpage without refreshing.
We have to do following things to make your page work.
Remove BeginForm block from your view because when we use BeginForm, it will send request to controller and refreshes the page.
Use Ajax to pass information to controller and update the page without refreshing it.
As you have two POST actions in controller, so keep both divs "rnd" and "email"
Here is sample script block with Ajax option to update the page as you requested,
$('#btn').click(function () {
var urlinfo = '/Home/Randa';
var textboxValue = $('#txtemail').val();
$.ajax({
type: "POST",
data: { value: textboxValue },
url: urlinfo,
success: function (result) {
$('#email').hide();
$('#rnd').show();
},
error: function () {
alert("failed");
}
});
});
First of all you need to use Ajax.BeginForm
Using Ajax.BeginForm with ASP.NET MVC 3 Razor
And on success function you can write the below code for clear text EmailId, and one Submit button.
$("#EmailId").val("");
$("#btn").val("Verify");
and you don't need two forms, if you are going to do the above.

Issue sending data to PartialView C# MVC

I'm trying to build an inbox that is very similar to facebooks message inbox, where you have a list of conversations(I only want a list of a message title) and when you click the conversation or message title in my situation, I want the whole message to be rendered next to it in a partial view.
Here's my Inbox view:
#model BlocketProject.Models.ViewModels.ProfilePageViewModel
#{
ViewBag.Title = "Inbox";
}
<h2>Dina meddelanden:</h2><br />
<div class="left">
<table id="messageTable">
#foreach (var message in Model.UserMessages)
{
<tr>
<td>
<button type="submit" class="messageButton">
#if (message.Unread == true)
{
<h4 style="font-weight:bold;">#message.MessageTitle</h4>
}
else if (message.Unread == false)
{
<h4>#message.MessageTitle</h4>
}
</button>
</td>
</tr>
}
</table>
</div>
<div class="right">
#Html.Partial("ReadMessage")
</div>
When I click this message-element that is a button, I want to pass that messageId to the PartialView ReadMessage:
#model BlocketProject.Models.DbClasses.DbMessages
<h2>#Model.MessageTitle</h2><br />
<p>#Model.MessageText</p>
and the controller looks like this:
[HttpPost]
public ActionResult Inbox()
{
var allMessages = ConnectionHelper.GetAllMessages(ConnectionHelper.GetUserByEmail(User.Identity.Name).UserId);
var model = new ProfilePageViewModel();
model.UserMessages = allMessages;
return View("Inbox", model);
}
[HttpPost]
public ActionResult ReadMessage(int messageId)
{
var model = ConnectionHelper.GetMessageByMessageId(messageId);
return PartialView("ReadMessage", model);
}
I've tried passing the messageId through a post as you can see in my controller, but then the partialView is returned as a new page and I simply want to render it in the Inbox view.
Any ideas?
EDIT:
Jonesy's answer fixed my problem when I edited it like this:
Controller:
public ActionResult ReadMessage(int messageId)
{
var model = ConnectionHelper.GetMessageByMessageId(messageId);
return PartialView("ReadMessage", model);
}
View:
<div class="left">
<table id="messageTable">
#foreach (var message in Model.UserMessages)
{
<tr>
<td>
#using (Ajax.BeginForm("ReadMessage", new { #messageId = message.MessageId }, new AjaxOptions { UpdateTargetId = "showMessage" }, FormMethod.Post))
{
<button type="submit" class="messageButton">
#if (message.Unread == true)
{
<h4 style="font-weight:bold;">#message.MessageTitle</h4>
}
else if (message.Unread == false)
{
<h4>#message.MessageTitle</h4>
}
</button>
}
</td>
</tr>
}
</table>
</div>
<div class="right" id="showMessage">
#Html.Partial("ReadMessage", new BlocketProject.Models.DbClasses.DbMessages())
</div>
Razor is run on the server, before the page is rendered. Once the page is on the client, and they can click a message, the concept of the PartialView is gone - it's all just one HTML page.
The easiest way for you to do this is to use an Ajax.BeginForm where your button is, and on click, update an element with a partial view retrieved from the server. Something like:
#using(Ajax.BeginForm("ReadMessage", "Messages", new AjaxOptions() { UpdateTargetId = "showMessage" })) {
//...
}
//...
<div class="right" id="showMessage">
//ReadMessage partial rendered on button click
</div>
A little method that could help you :
protected ActionResult View(string viewName, object model)
{
if (ControllerContext.IsChildAction)
return PartialView(viewName, model);
else if (Request.IsAjaxRequest())
return PartialView(viewName, model);
else
return View(viewName, model);
}
This will return a PartialView when you call you action via #Html.RenderAction() or call the action via Ajax (jQuery).
You can then use jQuery to prevent the form to be posted and post it with Ajax or when you click on a message, you can also use jQuery to get the result from the action and insert it in your DOM.

Categories