When I click the save button to save my controller function is firing twice. What is the problem in my code I don't know. Please help me.
Here is my button click to call ajax and save values.
<button id="btnSave" type="submit" title="Save" class="btn btn-success" onclick="getPage('#(Url.Action("Save", "Carriers"))')">
<span class="glyphicon glyphicon-floppy-disk"></span>Save
</button>
Here is my ajax
$.ajax({
type: "POST",
url: page,
data: $("#frmEdit").serialize(),
xhrFields: {
withCredentials: true
},
success: function (html) {
$('#CarrierList').empty();
$('#CarrierList').append($.parseHTML(html));
},
error: function () {
var error = "Error occured during loading Carrier items...";
$('#errorMessage').empty();
$('#errorMessage').append(error);
$('#errorModal').modal('show');
},
complete: function () {
$('#loaderImg').modal('hide');
}
});
}
Here is my controller method
public override ActionResult Save(CarrierDTO carrierDTO)
{
string[] ErrorMessageArray = new string[4];
int errorIndex = 0;
if (ModelState.IsValid)
{
MessageCollection messages = new MessageCollection();
carrierDTO.Save(ref messages);
if (messages.IsErrorOccured() || messages.IsExceptionOccured())
{
ModelState.AddModelError("", messages[0].Text);
return View("Edit", carrierDTO);
}
return View("Edit", carrierDTO);
}
You need to add 'preventDefault()'.
If the prevent default method is called, the default action of the event will not be
triggered.
In your case, the prevent default will stop submitting the form(the default action of the submit button), and use the ajax snippet to do so instead.
JQ:
$(function(e){
e.preventDefault();
$.ajax({
type: "POST",
url: page,
data: $("#frmEdit").serialize(),
xhrFields: {
withCredentials: true
},
success: function (html) {
$('#CarrierList').empty();
$('#CarrierList').append($.parseHTML(html));
},
error: function () {
var error = "Error occured during loading Carrier items...";
$('#errorMessage').empty();
$('#errorMessage').append(error);
$('#errorModal').modal('show');
},
complete: function () {
$('#loaderImg').modal('hide');
}
});
}
});
Two solutions
Use type="button" in your button control
<button id="btnSave" type="button" title="Save" class="btn btn-success" onclick="getPage('#(Url.Action("Save", "Carriers"))')">
<span class="glyphicon glyphicon-floppy-disk"></span>Save
</button>
or remove onclick="getPage('#(Url.Action("Save", "Carriers"))'), because the submit button take a post action in default .
<button id="btnSave" type="submit" title="Save" class="btn btn-success" ">
<span class="glyphicon glyphicon-floppy-disk"></span>Save
</button>
In you App_Start folder open the BudleConfig.cs file and do few changes:
First take a look to this line (this is an original VS generated content).
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.validate*"));
Note that '...validate*' pattern will load five scripts from your Scripts folder (this is true for my case):
~/Scripts/jquery.validate-vsdoc.js
~/Scripts/jquery.validate.js
~/Scripts/jquery.validate.min.js
~/Scripts/jquery.validate.unobtrusive.js
~/Scripts/jquery.validate.unobtrusive.min.js
As you can see, you are loading the unobtrusive.js twice (jquery.validate.unobtrusive.js and jquery.validate.unobtrusive.min.js). So, make your own code something like this to exclude, say full version js:
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.validate-vsdoc.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.validate.min.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.validate.unobtrusive.min.js"));
or make some mechanism to load full or min versions per your desire.
Related
Shopping cart with many items how to remove any item asynchronously with JavaScript this is my work so far. Can anyone improve on this?
your help would be greatly appreciated. Have a great day
Ok so this works if you remove items from the top of the list but fails if you remove items from some other place.
The problem seems to be that the form names are all the same "remove" without any indexing.
Problem is I'm not sure how to proceed with this.
document.forms['remove'].onsubmit = () => {
let formData = new FormData(document.forms['remove']);
fetch('/sales/cart?handler=RemoveItem', {
method: 'post',
body: new URLSearchParams(formData)
})
.then(() => {
var url = "/sales/cart?handler=CartPartial";
console.log(url)
$.ajax({
url: url,
success: function (data) {
$("#exampleModal .modal-dialog").html(data);
$("#exampleModal").modal("show");
//alert('Posted using Fetch');
}
});
});
return false;
}
<pre>
#foreach (var item in Model.Items)
{
<form name="remove" method="post">
<h4 class="text-left text-body">#item.Price.ToString("c")
<button class="btn btn-sm" title="Trash"><i style="font-size:large"
class="text-warning icon-Trash"></i></button>
</h4>
<input type="hidden" asp-for="#Model.Id" name="cartId" />
<input type="hidden" asp-for="#item.Id" name="cartItemId" />
</form>
}
</pre>
Update
----------
New markup
I added an index to the id and included an onclick event.
<form method="post" id="#i" onclick="removeItem(this.id)">
<button class="btn btn-sm" title="Trash">Item One</button>
<input type="hidden" asp-for="#Model.Id" name="cartId" />
<input type="hidden" asp-for="#item.Id" name="cartItemId" />
</form>
and create a new function that captured the form id including it in a constant.
<script>
function removeItem(formId) {
const form = document.getElementById(formId);
form.onsubmit = () => {
let formData = new FormData(form);
fetch('/sales/cart?handler=RemoveItem', {
method: 'post',
body: new URLSearchParams(formData)
})
.then(() => {
var url = "/sales/cart?handler=CartPartial";
console.log(url)
$.ajax({
url: url,
success: function (data) {
$("#exampleModal .modal-dialog").html(data);
$("#exampleModal").modal("show");
//alert('Posted using Fetch');
}
});
});
return false;
}
}
</script>
If anybody can improve on this please post it here.
Thanks.
Updates code behind Cart.cshtml.cs
using System;
using System.Threading.Tasks;
using Malawby.Models;
using Malawby.Services.Interfaces;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace Malawby.Pages.Sales
{
public class CartModel : PageModel
{
private readonly ICartRepository _cartRepository;
public CartModel(ICartRepository cartRepository)
{
_cartRepository = cartRepository ?? throw new
ArgumentNullException(nameof(cartRepository));
}
[BindProperty]
public Cart Cart { get; set; } = new Cart();
public const string SessionKeyName = "_Name";
public string SessionInfo_Name { get; private set; }
public void OnGetAsync()
{
}
public async Task<PartialViewResult> OnGetCartPartialAsync()
{
var userName = GetUserName();
if (userName != null)
{
Cart = await _cartRepository.GetCartByUserName(userName);
}
return Partial("_ToCart", model: Cart);
}
private string GetUserName()
{
return HttpContext.Session.GetString(SessionKeyName);
}
public async Task OnPostRemoveItemAsync(int cartId, int cartItemId)
{
await _cartRepository.RemoveItem(cartId, cartItemId);
}
}
}
Update 2
This is the modified code I used. This is the error in the console.
XML Parsing Error: no root element found Location: localhost:44331/sales/cart?handler=RemoveItem Line Number 1, Column 1
There is no error on the page just nothing happens on the click of the trash can.
<script type="text/javascript">
function removeItem(cartItemId, cardId) {
var removeUrl = "/sales/cart?handler=RemoveItem";
$.post(removeUrl,
{
cartItemId: cartItemId,
cardId: cardId
})
.done(function (data) {
alert(data); //usually return true or false if true
remove card
$('#card_' + cardId).remove();
});
}
</script>
I am not familiar with asp.net core, but I will show how I usually do it without focusing on syntax.
first on the view no need to add multiple form but should use card id as index and delete button sent selected index like this:
#foreach (var item in Model.Items)
{
<div id="card_#item.cardId">
<h4 class="text-left text-body">#item.Price.ToString("c")
<button class="btn btn-sm" onclick="removeItem('#item.cardId') title="Trash"><i style="font-size:large"
class="text-warning icon-Trash"></i></button>
</h4>
</div>
}
then the script function will call remove api and remove selected card with no need to re-render the page:
<script type="text/javascript">
function removeItem(cardId) {
var removeUrl = "your apiUrl";
$.post( "removeUrl", { cardId: cardId })
.done(function( data ) {
alert( data ); //usually return true or false if true remove card
$('#card_'+ cardId).remove();
});
}
</script>
I have a Vue 'app' of sorts. It's just part of a larger Django application - but I'm using this to springboard my Vue learning.
I'm trying to create unique forms that would be editable.
I have been messing about with this for a while trying to figure out how to 'disable all the forms except the one being edited'.
If a new 'evidence' is added, that form should be enabled and the others uneditable.
If an existing evidence is being edited then the 'add evidence' button should not be active and only the form being edited should be able to be edited.
My Vue looks like this - I have a base container (that's the Vue app) and a component (that is the forms):
var evidenceFormComponent = Vue.component("evidence-form", {
template: "#evidenceFormTemplate",
props: ["csrf_token", "evaluation_id", "element"],
components: {},
data: function () {
console.log("data function");
return {
evidence: getEvidence(),
subdomains: getSubdomains(),
isDisabled: null,
baseUrl: null
};
},
created: function () {
console.log("created_function!");
this.baseUrl = "/api/";
this.subdomainUrl = "/api/";
this.fetchAdditionalEvidence();
this.fetchSubdomainList();
this.isDisabled = true;
},
methods: {
fetchSubdomainList: function () {
// get the evidence if any using a Jquery ajax call
console.log("this should be fetching the subdomains");
return getSubdomains;
},
fetchAdditionalEvidence: function () {
// get the evidence if any using a Jquery ajax call
console.log("this is fetching additional evidence");
return getEvidence();
},
editForm: function (element) {
console.log("editing the form!");
this.isDisabled=false;
},
cancelEdit: function () {
console.log("cancel the edit!");
}
}
// watch:
});
const vm = new Vue({
el: "#evidenceFormsContainer",
data: function () {
console.log("parent data function");
return {
evidence: getEvidence(),
subdomains: getSubdomains(),
isDisabled: false,
baseUrl: null
};
},
methods: {
addForm: function () {
console.log("adding a child form!");
this.evidence.unshift({});
},
}
});
getEvidence and getSubdomains just return generic stuff atm as I would expect from an API.
I have read that it is best to have all UI elements present in case someone has JS disabled or something odd. So I figured I would create all 4 buttons then show/hide depending on if they should be disabled or not.
<button class="btn btn-primary text-white valign-button" v-on:click.prevent="element.isDisabled=false" #click="editForm()">
<i class="far fa-edit"></i> EDIT
</button>
<button :id="'saveButton'+element.id" v-if="element.isDisabled" v-on:click.prevent="element.removedRow=true" class="btn btn-primary text-white valign-button">
<i class="far fa-save"></i> SAVE
</button>
<button class="btn bg-danger text-white valign-button" data-toggle="modal" data-target="#deleteEvidenceModal" v-on:click.prevent>
<i class="fal fa-trash-alt"></i> DELETE
</button>
<button v-if="element.isDisabled" v-on:click.prevent="element.removedRow=true" class="btn bg-secondary text-white valign-button" #click="cancelEdit()">
<i class="fas fa-minus"></i> CANCEL
</button>
The problem I am running into is figuring how to tell if I'm editing one, or if it is a new one being added and properly disabling all the other elements.
For clarity I have made a JSFiddle of this in practice.
When you click 'add evidence' in the example, you will see. The form is 'disabled' still and the other forms still have the ability to click 'edit'.
I'm a bit lost. Would a child component for the buttons be better? Then if I'm editing a form or creating a new one, I can hide all the buttons on all the other instances?
All advice welcome!
Create a global reference to an activeForm element:
data: function () {
console.log("data function");
return {
evidence: getEvidence(),
subdomains: getSubdomains(),
isDisabled: null,
baseUrl: null,
activeForm: null // this is what we're adding
};
},
When you're in a loop, you know the index of the element you're working with. Pass that to your function:
#click="editForm(index)"
Assign that index to your activeForm variable:
editForm (index) {
this.activeForm = index
}
Change your v-if comparator assignment to observe whether the current index is the same as the activeForm:
v-if="activeForm && activeForm === index
In this way, a single variable is responsible for determining the edit state.
If you want to disable all the forms when adding, I'd just make another variable called adding and set it to true/false in the same way we did above with the other functions, and modify the v-if on the edit and delete buttons:
v-if="(activeForm && activeForm === index) && !adding"
I am unable to hit the server code using Ajax.BeginForm()
Here is part of my View where I used the Ajax Helper method
#model Ride.MMReports.ViewModels.ManualRecViewModel
.....
var options = new AjaxOptions
{
OnBegin = "OnBeginMethod",
OnFailure = "OnFailureMethod",
OnSuccess = "OnSuccessMethod",
OnComplete = "OnCompleteMethod",
HttpMethod = "Post"
};
using (Ajax.BeginForm("Index", "ManRecReport", options))
{
<button type="submit"
name="action"
value="Export to excel"
id="export-excel"
class="btn btn-primary"
Export to excel
</button>
}
#section scripts
{
#Scripts.Render("~/bundles/report")
#Scripts.Render("~/bundles/jqueryval")
}
My bundle include jquery.unobtrusive-ajax.js and also reports.js where I have all the event methods
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*"));
reports.js bellow. The browser is able to show the alert but last method it calls is OnFailureMethod
var isError = false;
function OnBeginMethod() {
alert("OnBeginMethod");
}
function OnFailureMethod(error) {
isError = true;
alert("OnFailure");
}
function OnSuccessMethod(data) {
alert("OnSuccess");
}
function OnCompleteMethod(data, status) {
if (!isError) {
alert("OnCompleteMethod");
}
}
The problem here is when I click the button, jquery-3.1.1.js is failing
http://localhost:31111/[object%20HTMLButtonElement] 404 (Not Found)
failing at this line
xhr.send( options.hasContent && options.data || null );
My Controller method looks like this:
[HttpPost, ValidateAntiForgeryToken]
public ActionResult Index(ManualRecViewModel vm)
{
....
}
Any thoughts what I am doing wrong?
I'm using BS-3 and bs-confirmation https://github.com/tavicu/bs-confirmation.
I have a button in my html which I click on, it display yes or no and then I can click on yes or no button
<a data-href="/api/yes-no/" class="btn" data-toggle="confirmation">Confirmation</a>
and javascript
<script type="text/javascript">
$(function() {
$('body').confirmation({
selector: '[data-toggle="confirmation"]',
btnOkLabel: 'Yes',
btnCancelLabel: 'No',
btnOkClass: "btn btn-sm btn-success",
btnCancelClass: "btn btn-sm btn-danger"
});
$('.confirmation-callback').confirmation({
onConfirm: function() { console.log('YES') },
onCancel: function() { console.log('NO') }
});
});
</script>
I want to send yes or no(whichever user click on) to my API (/api/yes-no/) using POST method.
but when I click on yes/no it open the api page(I mean GET request) how can I make it POST with form field ({'type': 'YES' or 'No' or anyother boolean})
onConfirm function is attached to the element you are initially clicking on, so you can use it to get data-href and then use standard jQuery method to invoke POST request.
<script type="text/javascript">
$(function() {
$('body').confirmation({
selector: '[data-toggle="confirmation"]',
btnOkLabel: 'Yes',
btnCancelLabel: 'No',
btnOkClass: "btn btn-sm btn-success",
btnCancelClass: "btn btn-sm btn-danger",
onConfirm: function () {
$.post(
$(this).attr('data-href'),
{}, // you data for POST body
function (data) {
// act on the response
}
);
}
});
});
</script>
I have an ajax function on the initialize of the main router that seems to hinder the event of my signin button in signin.js. When I click the signin button, it doesn't perform its function, instead the browser places the inputs on the URL, (i.e. username and password).
But when I remove the ajax function on the initialize, I can successfully log in.
I've included some of the codes I'm working on. Thanks
main.js
initialize: function(){
$.ajax({
type: "GET",
url: "something here",
contentType: "application/json",
headers: {
'someVar': something here
},
statusCode: {
404: function() {
console.log('404: logged out');
if (!this.loginView) {
this.loginView = new LoginView();
}
$('.pagewrap').html(this.loginView.el);
},
200: function() {
console.log('200');
if (!this.homeView) {
this.homeView = new HomeView();
}
$('.pagewrap').html(this.homeView.el);
}
}
});
// return false;
},
signin.js
var SigninView = Backbone.View.extend ({
el: '#signin-container',
events: {
"click #btn-signin" : "submit"
},
submit: function () {
console.log('signin');
$.ajax({ ... });
return false;
}
});
var toSignin = new SigninView();
window.anotherSigninView = Backbone.View.extend({
initialize: function() {},
render: function() {}
});
home.js
window.HomeView = Backbone.View.extend ({
initialize: function() {
this.render();
},
render: function() {
$(this.el).html( this.template() );
return this;
}
});
some html
<form id="signin-container">
<table id="tbl-signin">
<tr>
<td><div class="input-box"><input class="input-text" type="text" name="username" placeholder="Username"></div></td>
<td><div class="input-box"><input class="input-text" type="password" name="password" placeholder="Password"></div></td>
<td><input id="btn-signin" class="button" value="Sign In"></td>
</tr>
<tr>
<td class="opt"><input class="checkbox" type="checkbox" name="rememberMe" value="true"><label class="opt-signin">Remember Me?</label></td>
<td class="opt"><a class="opt-signin" href="#">Forgot Password?</a></td>
<td></td>
</tr>
</table>
</form>
You need to prevent the default behaviour of the submit button in your click handler. You can do this like so:
var SigninView = Backbone.View.extend ({
el: '#signin-container',
events: {
"click #btn-signin" : "submit"
},
submit: function (event) {
event.preventDefault();
console.log('signin');
$.ajax({ ... });
}
});
Alternatively, you might consider using the html button element which won't attempt to submit the form it's associated with.
Ok, I figured out what's your problem :)
Here is an example that resumes your code jsfiddle.net/26xf4/6. The problem is that you don't call new SigninView(); (instantiate the view) hence its events are never bound.
So, in this example try to uncomment the ligne 43 and your code will work as expected, because when you instantiate a View (new SigninView()) its constructor calls the delegateEvents() function (you don't see this in your code, it's in the backbone.js) enabling the events you declare in your view :
events: {
"click #btn-signin" : "submit"
},
I don't know about your HTML mockup, my best guess is that you are catching the incorrect event here. If you are submitting a form, you should catch submit form event not click #some-button. Because even if you catch click event of button inside a form and return false, the form will be still submitted because those are 2 different events