ASP.NET MVC Core - Preventing multiple form submission? - c#

I'm designing a time clock web app, where on the main page where the user can clock in/out if they have a current activity record or not (from a SQL server database)
I have the following form:
<form asp-action="RegisterClockAction" asp-antiforgery="false" method="post">
<div class="form-group">
<input asp-for="ActiveFlag" type="hidden"/>
</div>
<div class="form-group row">
<div class="col-sm-5 offset-sm-1 center-column">
<button id="clock-in-btn" type="submit" class="btn clock-button" disabled>Clock In</button>
</div>
<div class="col-sm-5 center-column">
<button id="clock-out-btn" type="submit" class="btn clock-button" disabled>Clock Out</button>
</div>
</div>
</form>
Then I have the following JavaScript for this specific View
<script type="text/javascript">
$(function() {
const activeFlag = #Html.Raw(Json.Serialize(Model.ActiveFlag));
if (activeFlag) {
$("#clock-out-btn").prop("disabled", false);
} else {
$("#clock-in-btn").prop("disabled", false);
}
});
$("form").submit(function() {
if ($(this).valid()) {
$(this).find(":submit").attr("disabled", "disabled");
}
});
</script>
Now the form submit event does prevent from double submission, but is this the correct way I should be doing it?
Not sure if it's necessary to know, but all the controller POST action does, if the active flag is true then we find the record in the database, update it and commit the changes to mark inactive. Otherwise, we create a new record to append into the database. Then, I just redirect back to the Index view to rebuild the model.

you can fix this by using javascript
you should use event.preventDefault() so that the default action of the event will not be triggered again.
$("form").submit(function(e) { // 'e' <- add this line of code
e.preventDefault(); // <- add this line of code
if ($(this).valid()) {
$(this).find(":submit").attr("disabled", "disabled");
}
});

Related

How do I pass the images while passing a model in Dropzone.js ASP.NET MVC C#

To start, I'm making a product edit page. When you edit a product, it's all the fields from a database going through a model. I'm using Dropzone.js to upload multiple images for the product with previews and an ability to remove before submitting the edits the user has made. A user can drag and drop or select multiple images. This works fine, the problem is when trying to submit it, the images are not passed to the controller for some reason where as the model is. I made a page dedicated to the uploading and it works great, but when I attempt to pass a model and the images, it passes only the Model and the images are null.
Controller
[HttpPost]
public ActionResult ProductEdit(IEnumerable<HttpPostedFileBase> files, TWebProduct tbl, HttpPostedFileBase file)
{
// A bunch of stuff that doesn't matter because it returns as null before it hits this.
}
This is the top of the ProductEdit.cshtml as you can see the model and using tag.
Top of the ProductEdit.cshtml
#model EcommerceAirmotion.DAL.TWebProduct
#{
ViewBag.Title = "ProductEdit";
Layout = "~/Views/Shared/_AdminLayoutPage.cshtml";
}
<h2>Product Details</h2>
<script src="~/Scripts/jquery-3.6.0.min.js"></script>
#using (Html.BeginForm("ProductEdit", "Admin", FormMethod.Post, new { #name = "myDropzone", id = "myDropzone", enctype = "multipart/form-data" }))
{
#* a bunch of other stuff *#
<div class="form-group">
<h5>Images</h5>
<div class="col-md-10">
<table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
<thead>
<tr>
<th>Image Prev</th>
<th>Name</th>
</tr>
</thead>
<tbody>
#foreach (var img in Model.TWebImages)
{
<tr>
<td><img src="~/ProductImages/#img.varImage" class="img-fluid" width="150" height="150" /></td>
<td>#img.varImage</td>
</tr>
}
</tbody>
</table>
<h5>Upload Images</h5>
<div>
<div id="previews" class="dz-default dz-message box__input dropzone border">
<br/>
<div style="text-align:center">
<i class="fa fa-cloud-upload" style="font-size:23px;position:relative;top:4px;"></i> <span style="margin-left:20px">Drop files to attach or browse</span>
</div>
<br />
</div>
<div id="previewFiles" class=""></div>
</div>
</div>
</div>
#* a bunch of other stuff *#
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</div>
}
#section scripts{
<script>
$(document).ready(function () {
Dropzone.autoDiscover = false;
$('#myDropzone').dropzone({
//parameter name value
paramName: "files",
//clickable div id
clickable: '#previews',
//preview files container Id
previewsContainer: "#previewFiles",
autoProcessQueue: false,
uploadMultiple: true,
parallelUploads: 100,
maxFiles: 100,
//url:"../ProductImages/", // url here to save file
maxFilesize: 100,//max file size in MB,
addRemoveLinks: true,
dictResponseError: 'Server not Configured',
//acceptedFiles: ".png,.jpg,.gif,.bmp,.jpeg,.pdf",// use this to restrict file type
acceptedFiles: ".png,.jpg,.gif,.bmp,.jpeg",// use this to restrict file type
init: function () {
var self = this;
// config
self.options.addRemoveLinks = true;
self.options.dictRemoveFile = "Delete";
//New file added
self.on("addedfile", function (file) {
console.log('new file added ', file);
$('.dz-success-mark').hide();
$('.dz-error-mark').hide();
});
// Send file starts
self.on("sending", function (file, xhr, formData) {
console.log('upload started', file);
$('.meter').show();
});
// File upload Progress
self.on("totaluploadprogress", function (progress) {
console.log("progress ", progress);
$('.roller').width(progress + '%');
});
self.on("queuecomplete", function (progress) {
$('.meter').delay(999).slideUp(999);
});
// On removing file
self.on("removedfile", function (file) {
console.log(file);
});
$('#Submit').on("click", function (e) {
e.preventDefault();
e.stopPropagation();
// Validate form here if needed
if (self.getQueuedFiles().length > 0) {
self.processQueue();
} else {
self.uploadFiles([]);
$('#myDropzone').submit();
}
});
self.on("successmultiple", function (files, response) {
// Gets triggered when the files have successfully been sent.
// Redirect user or notify of success.
$(".alert").alert('close');
});
}
});
})
</script>
}
Also on the edit page html
This is the ProductEdit.cshtml part where the drop and drag go
This is the script for the dropzone on the product edit page
Script on the ProductEdit.cshtml
These are the errors from the dev tools from chrome
DevTools Error messages
I have little to no experience with javascript, very small experience (like 40 hours of experience) in MVC, but am decently well versed in C#
Please help me find what I'm doing wrong.
Let me know if I need to clarify anything better.
I found the answer, the submit button didn't have the name attribute. This was not calling the javascript to actually save the images.
old submit button:
<input type="submit" value="Save" class="btn btn-primary" />
New submit button:
<input type="submit" value="Submit" id="Submit" name="Submit" class="btn btn-primary" />

Modal from Ajax loaded partial view is not showing after clicking button one time

I am trying to create a partial view that will appear in a modal when a button gets pressed. If there is another approach that works better for this, I am open for it -- I had the modal working great until I added the List to the main view. If there is a way to return back a list and form post a single entity that might work for my scenario. Right now the code I have works to an extent, however you need to push the Add Service button twice in order for the modal to show up and when it does the only way to get rid of it is to submit, both the X in the top as well as the close button don't work.
main view
#model List<ServicesPosting>
<div>
<button id="addService" onclick="OpenModal()">Add Service</button>
</div>
<div id="AddServiceForm"></div>
<script type="text/javascript">
function OpenModal() {
var url = '#Url.Action("AddServiceModal", "Services")';
$('#AddServiceForm').load(url);
$('#serviceModal').modal();
}
</script>
controller
public class ServicesController : Controller
{
// GET: Services
public ActionResult Index()
{
List<ServicesPosting> postings = DataAccess.GetAllPostings();
return View(postings);
}
[HttpGet]
public ActionResult AddServiceModal()
{
return PartialView("~/Views/Services/AddNewService.cshtml");
}
}
partial view
#model ServicesPosting
<!-- Modal -->
<div class="modal fade" id="serviceModal" tabindex="-1" role="dialog" aria-labelledby="serviceModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title" id="serviceModalLabel">Add New Service</h4>
</div>
<div class="modal-body">
#using (Html.BeginForm("Index", "Services"))
{
// TODO: validate up front
<div class="row">
Service Title : #Html.TextBoxFor(x => x.ServiceTitle)
</div>
<div class="row">
ServiceDescription : #Html.TextAreaFor(x => x.ServiceDescription)
</div>
<div class="modal-footer">
<button type="button" class="btn" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Save Changes</button>
</div>
}
</div>
</div>
</div>
</div>
The problem is when you click the button first time the
$('#serviceModal').modal(); function is being called before the modal load.
So, you need to call this $('#serviceModal').modal(); function after $('#AddServiceForm').load(url); is done. Means that ater loading the AddNewService.cshtml is completed then you can find your desired modal in you DOM. See for more in here http://api.jquery.com/load/
After that you can show the modal. Some times when you add any DOM elements achieved by ajax call added to you main DOM specially DOM with form elements; the DOM parser can't get it first time so use the body tag to find any child element. I have put a code bellow. Try with this :
$("#AddServiceForm").load(url, function(responseTxt, statusTxt, xhr) {
// debugger;
if (statusTxt == "success")
{
$('body #serviceModal').modal('show');
}
if (statusTxt == "error")
{
console.log("Error: " + xhr.status + ": " + xhr.statusText);
}
});

Asp.Net MVC 5 The ActionLink Only Calls The Action Method in The First Time When I Click on It

I'm working on an e-commerce website. What I'm trying to do right now is when users click on the "Open Modal" link, a modal dialog will open and show the product detail.
At the first time when I click on the ActionLink, it will call the QuickView action method in the Product controller and show the detail information of the product. However, when I click on the ActionLink for another product, it won’t call the QuickView action method. Also, the content of the modal dialog still displays the information of the previous product. Basically, the problem is that the action method only gets called in the first time that I click on the ActionLink. The action method won’t get called anymore when I click on the ActionLink again. When I right-click the page and inspect the element, I can see that the modal action link is generated correctly. I’m so confused about it. Please help.
The main view:
<div id="product">
#foreach (var product in Model.Products)
{
#Html.Partial("ProductSummary", product)
}
</div>
// The normal bootstrap modal with out any content in the modal-content div. Note that the id matches the data_target from the link
<div id="modal-container" class="modal fade product_view" tabindex="-1"
role="dialog">
<div class="modal-dialog">
<div class="modal-content">
</div>
</div>
</div>
#section scripts {
// The normal bootstrap behavior is to only grab the content for the
// modal once, if you need to pull in different partial views then the data on
// the modal will have to be cleared.
<script type="text/javascript">
$(function () {
$('#modal-container').on('hidden.bs.modal', function () {
$(this).removeData('bs.modal');
$('#modal-container .modal-content').empty();
});
});
// callback from form modal
function success() {
alert("Success!");
}
</script>
The product summary:
<div class="caption">
<h4 class="pull-right">#Model.Price.ToString("c")</h4>
<h4>
#Ajax.ActionLink(Model.Name, "ProductDetail", new { productId = Model.ProductID, returnUrl = Request.Url.PathAndQuery },
new AjaxOptions { UpdateTargetId = "content", InsertionMode = InsertionMode.Replace })
</h4>
</div>
<div class="space-ten"></div>
<div class="btn-ground text-center">
<button type="button" class="btn btn-primary" onclick="location.href='#Url.Action("AddToCart", "Cart", new { ProductId = Model.ProductID})'">
<i class="fa fa-shopping-cart"></i> Add To Cart
</button>
<div>
// Create a link that calls the controller method that returns the partial view
#Html.ActionLink("Open Modal", "QuickView", "Product", new { ProductId = Model.ProductID }, new
{
// Needed to link to the html of the modal
data_target = "#modal-container",
// Tells the bootstrap javascript to do its thing
data_toggle = "modal"
})
</div>
</div>
<div class="space-ten"></div>
</div>
The quick view:
<div class="modal-header">
<a href="#" data-dismiss="modal" class="class pull-right"><span
class="glyphicon glyphicon-remove"></span></a>
<h3 class="modal-title">#Model.Name</h3>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-6 product_img">
<img
src="http://img.bbystatic.com/BestBuy_US/images/products/5613/5613060_sd.jpg"
class="img-responsive">
</div>
<div class="col-md-6 product_content">
<h3 class="cost"><span class="glyphicon glyphicon-usd"></span> #Model.Price </h3>
<div class="space-ten"></div>
<div class="btn-ground">
<button type="button" class="btn btn-primary"><span class="glyphicon glyphicon-shopping-cart"></span> Add To Cart</button>
<button type="button" class="btn btn-primary"><span class="glyphicon glyphicon-heart"></span> Add To Wishlist</button>
</div>
</div>
</div>
The action method in the product contorller:
public ActionResult QuickView(int productId)
{
Product prod = repository.Products
.FirstOrDefault(p => p.ProductID == productId);
return PartialView(prod);
}
image of the inspect element
Make sure the modal content is empty before the action link tries to call the server.
You have javascript code which seems like it clears the modal content whenever the modal gets hidden. Is it possible that the modal still has content when you click an action link after the first time?
EDIT:
Try adding an onclick function that empties the modal content when the action link is clicked. So add the onclick property in your ActionLink call by changing your code like below:
change this:
$('#modal-container').on('hidden.bs.modal', function () {
$(this).removeData('bs.modal');
$('#modal-container .modal-content').empty();
});
to:
var emptyModalContent = function() {
$('#modal-container').removeData('bs.modal').find('.modal-content').empty();
};
$('#modal-container').on('hidden.bs.modal', function () {
emptyModalContent();
});
and add onclick property to action link:
#Html.ActionLink("Open Modal", "QuickView", "Product", new { ProductId = Model.ProductID }, new
{
// Needed to link to the html of the modal
data_target = "#modal-container",
// Tells the bootstrap javascript to do its thing
data_toggle = "modal",
onclick = "emptyModalContent()"
})
Thanks everyone who gives me the suggestion. In order to fix the issue, I need to combine the solutions that provide by #Anthony McGrath, #ecKO and #Kumar_Vikas.
Here is the solution:
As #Anthony McGrath mention:
Change the script from:
$('#modal-container').on('hidden.bs.modal', function () {
$(this).removeData('bs.modal');
$('#modal-container .modal-content').empty();
});
to:
var emptyModalContent = function() {
$('#modal-container').removeData('bs.modal').find('.modal-
content').empty();
};
$('#modal-container').on('hidden.bs.modal', function () {
emptyModalContent();
});
After that, follow #ecKO and #Kumar_Vikas' idea and change the action link that calls the action method from #Html.ActionLink to #Ajax.ActionLink.

client side validation in htmlhelper without Model Class

I visited through different links but couldn't find desired answer. I am not using model for this view. Before sending data to server I want to check whether the text input is null or not.How can I validate it?
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="container">
<h2>Student Information</h2>
#using (Html.BeginForm("insert", "Menu", FormMethod.Post, new { id="target"}))
{
<label>First Name:</label>
<input type="text" class="form-control" name="firstname" id="f_name" />
<button type="submit" class="btn btn-success" >Insert</button>
}
</div>
<script>
$(function () {
$("#target".submit(function (event) {
event.preventDefault();
//return false;
})
})
</script>
Try this, I use empty string ('') instead of null since usually empty textbox are just empty string and not necessarily null.
<script>
$(function () {
$('button.btn').unbind();
$('button.btn').click(function () {
if($("#f_name").val() == ''){
return false;
}
return true;
})
})
</script>
If you dont want custom script for validating data on client side, you can use jquery.validate.js and jquery.validate.unobstrusive.js. This will work just like mvc validation rules(Actually this validation logic is generated by MVC itself on the view when client side validation is enabled).
Just add this three script file inside your page.
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.16.0/jquery.validate.js"></script>
<script src="http://ajax.aspnetcdn.com/ajax/mvc/5.2.3/jquery.validate.unobtrusive.js"></script>
Or you can select from your Script folder inside MVC application.
And Try to add following attribute in your Htmlelement for validation.
<div class="container">
<h2>Student Information</h2>
#using (Html.BeginForm("insert", "Menu", FormMethod.Post, new { id="target"}))
{
<label>First Name:</label>
<input data-val="true" data-val-required="First Name field is required." type="text" class="form-control" name="firstname" id="firstname" />
<span class="field-validation-valid" data-valmsg-for="firstname" data-valmsg-replace="true"></span>
<button type="submit" class="btn btn-success" >Insert</button>
}
</div>
This will work just like MVC validation inside your view.
You can get more information how to use JqueryValidate and jquery unobtrusive by clicking on this link : http://bradwilson.typepad.com/blog/2010/10/mvc3-unobtrusive-validation.html
You can get the value with a simple jQuery statement - $("#f_name").val() and then check if it is null $("#f_name").val() == null
So your complete javascript would be
<script>
$(function () {
$("#target".submit(function (event) {
event.preventDefault();
if($("#f_name").val() == null){
return false;
}
return true;
})
})
</script>

Html.EditorFor inside java script

I am learning ASP MVC and I try do form, and when you click the button on the form, I want to add some content to div in this form.
#using (Html.BeginForm())
{
<div>
<input type="button" value="Click to edit address" onclick="#String.Format("addAddress()")" />
<div id='divResult'>
Here I want to add new conntent after click button.
</div>
<div>
}
My Java Script function does't work:
<script type="text/javascript">
function addAddress() {
$('#divResult').replaceWith("#Html.EditorFor(m => m.User.HomeAddress)");
}
</script>
Can anyone help me? I want to fill my User (model.User) object and post filled to server, but Athe address you can fill only after click button, because it has a lot of field to be filled, but address is not necessary.
Try this:
#using (Html.BeginForm())
{
<div>
<input type="button" value="Click to edit address" onclick="addAddress()" />
<div id='divResult' hidden='hidden' >
#Html.EditorFor(m => m.User.HomeAddress)
</div>
</div>
}
-
<script type="text/javascript">
function addAddress() {
$('#divResult').show();
}
</script>

Categories

Resources