Ajax, the old value appears first and then the new one - html

I have Session::flash notifications(bootstrap toasts), which is used to notify about adding a product to the cart:
#if(Session::has('add-product'))
<div aria-live="polite" aria-atomic="true" class="d-flex justify-content-center align-items-center">
<div class="toast fixed-top" role="alert" aria-live="assertive" aria-atomic="true" data-delay="3000">
<div class="toast-header bg-success">
<span class="mr-auto notif_text"></span>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
</div>
</div>
#endif
CartController with Session:flash:
public function addCart(Request $request, $id){
$product = Product::find($id);
$oldCart = Session::has('cart') ? Session::get('cart') : NULL;
$cart = new Cart($oldCart);
$cart->add($product, $product->id);
$request = Session::put('cart', $cart);
Session::flash('add-product', $product->name);
return response()->json([
'total_quantity' => Session::has('cart') ? Session::get('cart')->totalQty : '0',
'notif_text' => 'Product' . Session::get('add-product', $product->name) . 'added to cart'
]);
}
And Ajax request:
$(document).ready(function() {
$('.product-icon-container').find('.ajaxcartadd').click(function(event) {
event.preventDefault();
$('.toast').toast('show');
$.ajax({
url: $(this).attr('href'),
dataType: 'JSON',
success: function(response) {
$('.prodcount').html(response.total_quantity);
$('.notif_text').html(response.notif_text); //!!!Here I load the notification text!!
}
});
return false;
});
});
My question is how to make sure that the old value does not appear, but a new one appears immediately.
Now it works like this:
img_1
Then after about 1 second a new notification appears:
img_2
How can this be fixed?

first remove already assigned value like this then assign value to that class element.
document.getElementsByClassName("prodcount").innerHTML = ""; // or $('.prodcount').innerHTML = "";
document.getElementsByClassName("notif_text").innerHTML = ""; // or $('.notif_text').innerHTML = "";
$('.prodcount').html(response.total_quantity);
$('.notif_text').html(response.notif_text);

Solution for my case, maybe someone will find it useful:
The reason is that the product is added when the ajax status is success (this is where the handler for clicking on the "Add to cart" button is).
I am using bootstrap toasts, they are included with:
$('.toast').toast('show');
And since the inclusion of notifications was not inside the ajax request, it turned out that first an empty or old value was loaded, and then only a new one.
The solution was to move the inclusion "bootstrap toasts" inside ajax:
$(document).ready(function() {
$('.product-icon-container').find('.ajaxcartadd').click(function(event) {
event.preventDefault();
$.ajax({
url: $(this).attr('href'),
dataType: 'JSON',
success: function(response) {
$('.prodcount').html(response.total_quantity);
$('.notif_text').html(response.notif_text); //!!!Here I load the notification text!!
$('.toast').toast('show');//!!!I moved it here!!!
}
});
return false;
});
});
But there is one nuance that I understood just now. Flash messages are not always loaded asynchronously, since they depend on the session (if I understood correctly), and if your user visits the site for the first time, flash will not work. Therefore, it is worth displaying messages without flash:
Bootstrap toasts:
<div aria-live="polite" aria-atomic="true" class="d-flex justify-content-center align-items-center">
<div class="toast fixed-top" role="alert" aria-live="assertive" aria-atomic="true" data-delay="3000">
<div class="toast-header bg-success">
<span class="mr-auto notif_text"></span>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
</div>
</div>
CartController:
public function addCart(Request $request, $id){
$product = Product::find($id);
$oldCart = Session::has('cart') ? Session::get('cart') : NULL;
$cart = new Cart($oldCart);
$cart->add($product, $product->id);
$request = Session::put('cart', $cart);
return response()->json([
'total_quantity' => Session::has('cart') ? Session::get('cart')->totalQty : '0',
'notif_text' => 'Product' . $product->name . 'added to cart'
]);
}
P.S. If you want bootstrap toasts with the same styles as mine, add this to your css:
.toast{
max-width: none;
}
.toast.fixed-top{
margin-top: 30px;
left: auto;
right: auto;
}
.toast-header{
border: 0;
padding: 0.75rem 1.25rem;
color: #fff;
border-radius: 0.25rem;
}
.close{
opacity: 1;
color: #fff;
text-shadow: 0 1px 0 #000;
}
.close:hover{
color: #fff;
opacity: 0.5;
}

You can try the easiest way in your ajax call as. You can use this instead of toast also just as:
$('.prodcount').empty().show().html(response.total_quantity).delay(3000).fadeOut(300);
$('.notif_text').empty().show().html(response.notif_text).delay(3000).fadeOut(300);

Related

Modal ajax.form validation ASP.NET MVC, error message not showing, modal not closing after succesful submit

i'm new to the ASP.net and i get easilly confused when jquery and Ajax get in the middle of a code.
I have a popup bootstrap Modal that show when i click on a button, the Modal contains an Ajax.form that allows me to create projects, i'm using HTML.validation to check if my form is filled correctly. I have two main problems :
When I enter valid informations in my form and i submit the form, the project is created succesfully but the Modal doesn't close automaticlly.
When i enter non-valid informations when i submit no message error apear, nonetheless when I close the modal and open it again, the errors apear.
I need your help to :
Close the modal once the submit form is valid.
Have the error message show in my modal form.
*This is a part of my main view :
enter code here
<!--validation script -->
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
<!-- Button trigger modal -->
<div>
<div>
<a class="btn btn-red ms-auto my-auto h-50"
href="#Url.Action("createProject", "ProjectSelection")" title="Nouveau projet"
data-bs-toggle="modal" data-bs-target="#Modal_NewProject"
ajax-target-id="Body_NewProject" data-bs-loader="Loader_NewProject">
<i class="feather-16" data-feather="plus"></i>
Créer projet
</a>
</div>
</div>
<br />
<!-- Modal View -->
<div class="modal fade" id="Modal_NewProject" tabindex="-1"
aria-labelledby="Label_NewProject" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="Label_NewProject">
Créer un Projet</h5>
<button type="button" class="btn-close"
data-bs-dismiss="modal" aria-label="Close"></button>
</div>
#using (Ajax.BeginForm("createProjectPost",
"ProjectSelection", new AjaxOptions { HttpMethod = "POST",
UpdateTargetId = "Body_NewProject" }, new { id = "projectForm" }))
{
<div id="Body_NewProject">
<div class="spinner-border" role="status"
id="Loader_NewProject">
<span class="visually-hidden">Loading...</span>
</div>
<!-- partial view "createProject" -->
</div>
}
</div>
</div>
</div>
<!---->
*And this is my partial view that containts the elements of the form
<div class="modal-body" id="frmProject">
<div class="input-group mb-3" id="">
#Html.DropDownListFor(m => m.Project.Domain,
new SelectList(Model.Domains, "IdDomain", "Name"),"Selectionner un domaine",
new { #class = "w-100", #id = "DomainSelection" })
#Html.ValidationMessageFor(m => m.Project.Domain, null,
new { style = "color: red;" })
</div>
<div class="input-group mb-3 mb-3 " id="teamSelection">
#Html.DropDownListFor(m => m.Project.Teams,
new SelectList(Model.Teams.Select(teamElement => teamElement.Name).ToList()),
"Selectionner une équipe", new { #class = "w-100" })
#Html.ValidationMessageFor(m => m.Project.Teams, null,
new { style = "color: red;" })
</div>
<div class="form-floating mb-3">
<input class="form-control " data-val-required="Le champ Projet est requis."
id="Project.Name" name="Project.Name" type="text" value="">
<label for="Project.Name" class="">Nom du projet</label>
#Html.ValidationMessageFor(m => m.Project.Name, null,
new { style = "color: red;" })
</div>
</div>
<div class="modal-footer mb-0">
<div class="panel-footer">
<button class="btn btn-secondary" data-bs-dismiss="modal"
type="button">Annuler</button>
<button type="submit" form="projectForm"
class="btn btn-red">Valider</button>
<div class="col-xs-10" id="lblstatus"></div>
</div>
</div>
*This is my Controller code
public ActionResult createProject()
{
//initialization code
return PartialView();
}
[HttpPost]
[ValidateAntiForgeryToken]
[HandleError]
public ActionResult createProjectPost(Project project)
{
#region Variable verification
if (!ModelState.IsValid)
{
Session["Error"] = true;
if (project.Domain == null)
{
Session["DomainError"] = true;
}
if (project.Teams == null)
{
Session["TeamError"] = true;
}
if (project.Name == null)
{
Session["NameError"] = true;
}
ViewData["project"] = "create";
//return View("ProjectSelectionPage");
return PartialView(project);
}
#endregion
#region Initialisation
//initilization code
#endregion
#region Check possible copy of the project
if (projectDB.Exist(project.Name))
{
//Check that this project doesn't exist in the database
//Create a session variable that will trigger an error message
Session["Error"] = true;
Session["NameCopy"] = true;
ViewData["project"] = "create";
return PartialView(project);
}
#endregion
#region Project to database and generate name
//Add the project to the database
#endregion
return PartialView();
}
It's been several days since i'm searching for an answer, i either missed one or didn't use it correctly.
enter image description here
Thank you

Show / Hide javascript function

I'm trying to show a div if the URL contains a parameter.
I've run my JS on Chrome Console and it works just fine, but when I publish it doesn't work.
What am I doing wrong?
HTML
<div class="alert alert-success" id="vendaPremium" style="display: none;">
<div class="container">
<div class="alert-icon">
<i class="material-icons">check</i>
</div>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true"><i class="material-icons">clear</i></span>
</button>
<b>Uhulll! Você acaba de se tornar cliente Meu Marketing Premium. Em breve entraremos em contato. \o/</b>
</div>
</div>
JavaScript
function sucessoCompra() {
var alertaPagSeguro = window.location.href.split("?")[1];
if (alertaPagSeguro === 'sucessoPagSeguro') {
$('#vendaPremium').show();
} else {
$('#vendaPremium').hide();
}
}
window.onload = sucessoCompra();
This is how I would do it:
$(document).ready(function() {
sucessoCompra();
});
function sucessoCompra() {
const urlParams = new URLSearchParams(window.location.search);
var alertaPagSeguro = urlParams.get("query"); // Replace with name of your parameter
if (alertaPagSeguro == "sucessoPagSeguro") {
// Parameter equals string
$("#vendaPremium").show();
} else {
// Parameter does not equal string, or doesn't exist
$("#vendaPremium").hide();
}
}
Problem could be because the script is executed before the JQuery library has been processed. If that's the issue, simply insert the <script> element (of your main code) below the JQuery Library.
Otherwise, what's the URL to the published webpage? Using my example above would probably be more efficient, as URLSearchParams() is widely supported - not on Internet Explorer however, so you may need a fallback.

Initializing variable in HTML5 for Input tag

Here is the html source code of page that loads array of products:
<div class="container" *ngFor="let product of products">
<div class="product">
<div class="img-container">
<img
//image url
</div>
<div class="product-info">
<div class="product-content">
<h1>{{product.name}}</h1>
<p>{{product.description}}</p>
<p>Price: {{product.price}} $</p>
<p>Quantity:
<button type="button" class="btn btn-danger" (click)="minusQuantity(product)">-</button>
<input type="number" class="input-quantity" [(ngModel)]="product.count"/>
<button type="button" class="btn btn-success" (click)="plusQuantity(product)">+</button>
<div class="buttons">
<a class="button add" (click)="addToCart(product)">Add to Cart</a>
</div>
</div>
</div>
</div>
</div>
When page is loaded, numeric input is empty (there is no value visible inside). Hence clicking on - and + buttons to invoke minusQuantity() and plusQuantity() have no effect on the product.count and displaying it on the page.
I've tried to set default value, but it is overridden by ngModel. If i use only value without ngModel, then input does not react to any changes caused by -/+ buttons (since it's just hardcoded "1").
But if I input e.g. "1" manually on the input, then + and - buttons do work, since there is a value provided, and it works OK.
Question is:
How avoid this issue? Is there any way to initialize input type with some value and then pass it to the product.count correctly? Or the approach should be totally different?
Fragments of components that handle +/- methods:
product.component.ts
plusQuantity(product: ProductModel) {
if (product.count < 99) {
this.cartService.increaseQuantity(product);
}
}
minusQuantity(product: ProductModel) {
if (product.count > 1) {
this.cartService.decreaseQuantity(product);
}
}
cartService.ts
increaseQuantity(product: ProductModel) {
product.count = product.count + 1;
this.orderElement.quantity = product.count + 1;
return product.count;
}
decreaseQuantity(product: ProductModel) {
product.count = product.count - 1;
this.orderElement.quantity = product.count - 1;
return product.count;
}
Use a javascript file with the code:
var cartService = {}
cartService.plusQuantity = function(product) {
product = ProductModel;
if (product.count < 99) {
this.cartService.increaseQuantity(product);
}
};
cartService.minusQuantity = function(product) {
product = ProductModel;
if (product.count > 1) {
this.cartService.decreaseQuantity(product);
}
};
Then it might work!

Change boolean value of ith input field ondelete button press in Reactive form

I am trying to make a dynamic input field using a reactive form.In my project, First I am generating number of input fields as per Names coming from API and then I can also create new input field on and button press.With every input, there is a delete button.With that user can delete that particular field. I am successfully able to delete that field but now I need that fields name value and its uuid(which I am getting from api with name) on delete button press.Currently I am getting this response while submit form.
isdelete: false
screenname: "first"
screenuuid: "25ef9fde-bd02-4225-8c64-9cc12bea523"
By default, isdelete is set to false on formsubmit, but when user deletes this screen, I want to change the boolean value of isdelete to true of that screen.I tried to change the boolean value when input field deletion but failed to do that.Onsubmit I am getting same response as mentioned above.Here is my code.
Component.ts file
export class ScreenmodalComponent implements OnInit {
#Input() screendetails;
personalForm : FormGroup;
arrayItems: any = [];
screenList: string;
isDelete: boolean = false;
constructor(private activeModal: NgbActiveModal, private formbuilder: FormBuilder, private pagesService: PagesService, private utilService: UtilService) {}
ngOnInit() {
this.personalForm = this.formbuilder.group({
other: this.formbuilder.array([ ])
});
// Api call
this.pagesService.GetScreens('').then(res => {
console.log('get screens api', res);
for (let i = 0; i < res['data']['length']; i++) {
this.arrayItems = res['data'][i]['name'] ;
this.addanother(res['data'][i]['name'],res['data'][i]['screen_uuid'],this.isDelete);
}
}).catch(error => {
this.utilService.showError('Something went wrong', 'Error');
console.log('err is', error);
});
}
addanother(data: any,uuid:any,isdelete:boolean = false):void {
(<FormArray>this.personalForm.get('other')).push(this.addanotherForm(data,uuid,isdelete));
}
addanotherForm(data:any,uuid:any,isdelete:boolean): FormGroup{
return this.formbuilder.group({
screenname: [data],
screenuuid: [uuid],
isdelete: [isdelete]
});
}
clear(i : number){ //this function removes input field
console.log("dvdv"+i);
(<FormArray>this.personalForm.get('other')).removeAt(i);
}
onSubmit(): void {
console.log(this.personalForm.value);
}
closeModel() {
this.activeModal.close();
}
}
Component.html file
<div class="custom_modal pb-3">
<div class="modal-header border-0 p-1 pl-3 pr-3 d-flex justify-content-between align-items-center">
<h3 class="m-0">Project: {{screendetails.name}}</h3>
<button type="button" class="close" data-dismiss="modal" (click)="closeModel()">×</button>
</div>
<div class="modal-body p-3">
<div class="body_top d-flex justify-content-between mb-4 mt-2 align-items-center">
<h3 class="title m-0">Add Screens</h3>
</div>
<form [formGroup]="personalForm" (ngSubmit)="onSubmit()">
<div formArrayName="other" class="form-group" *ngFor="let other of personalForm.get('other').controls; let i = index" >
<div [formGroupName] = "i">
<div class="screen_row mb-4">
<div class="row">
<div class="col-md-3 col-sm-3 d-flex align-items-center">
<label>Screen Name</label>
</div>
<div class="col-md-8 col-sm-8 pl-0 pl-sm-3">
<input type="text" class="form-control rounded-0" formControlName="screenname" name="screenname" >
</div>
<div class="col-md-1 col-sm-1 d-flex align-items-center justify-content-center pl-0 pl-sm-3">
<button type="button" class="close" (click)="clear(i)">×</button> //delete button
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer border-0 d-table w-100">
<button type="button" class="btn-primary float-left" (click)="addanother('',null,false)">Add New Screen</button>
<div class="float-right">
<button type="button" class="btn-primary mr-3" data-dismiss="modal" (click)="onSubmit();">Apply</button>
<button type="button" class="btn-primary cancle" data-dismiss="modal" (click)="closeModel()">Cancel</button>
</div>
</div>
</form>
</div>
</div>
Please help me to change the boolean value of ith input field if it is deleted.Your small help will be appreciated .Thank you
I think first you need to update arrayItems correctly to maintains changes of screens.
for (let i = 0; i < res['data']['length']; i++) {
this.arrayItems.push(res['data'][i]['name']);
this.addanother(res['data'][i]['name'],res['data'][i]['screen_uuid'],this.isDelete);
}
Then in the clear function try update the particular isdelete value for screen in the arrayItems.
clear(i : number){ //this function removes input field
console.log("delete item index:"+i);
arrayItems[i]["isdelete"] = true;
(this.personalForm.get('other') as FormArray).removeAt(i);
// Try this if you want to update form value as well
((this.personalForm.get('other') as FormArray).at(i) as FormGroup).get("isdelete").setValue(true);
}
Hope this helps.

Template error with Angular Bootstrap accordion

I searched prior to posting question and cannot find something to help. Basically, am using Angular and Bootstrap with the ui.angular.js. For the likes of me, I cannot get the accordion to work because of reference to accordion.html and accordion-group.html based on the error I get from dev tools.
Using some quick help from the internet, I did the following:
html:
<div ng-controller="pptController">
<p>
<button class="btn btn-default btn-sm" ng-click="status.open = !status.open">Toggle last panel</button>
<button class="btn btn-default btn-sm" ng-click="status.isFirstDisabled = ! status.isFirstDisabled">Enable / Disable first panel</button>
</p>
<label class="checkbox">
<input type="checkbox" ng-model="oneAtATime">
Open only one at a time
</label>
<accordion close-others="oneAtATime">
<accordion-group heading="Static Header, initially expanded" is-open="status.isFirstOpen" is-disabled="status.isFirstDisabled">
This content is straight in the template.
</accordion-group>
<accordion-group heading="{{group.title}}" ng-repeat="group in groups">
{{group.content}}
</accordion-group>
<accordion-group heading="Dynamic Body Content">
<p>The body of the accordion group grows to fit the contents</p>
<button class="btn btn-default btn-sm" ng-click="addItem()">Add Item</button>
<div ng-repeat="item in items">{{item}}</div>
</accordion-group>
<accordion-group is-open="status.open">
<accordion-heading>
I can have markup, too! <i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': status.open, 'glyphicon-chevron-right': !status.open}"></i>
</accordion-heading>
This is just some content to illustrate fancy headings.
</accordion-group>
</accordion>
</div>
javascript in my controller:
app.controller('pptController', ['$scope', 'pptService', function ($scope, pptService) {
$scope.ppt = [];
pptService.getPpt().then(function (results) {
$scope.ppt = results.data;
}, function (error) {
//alert(error.data.message);
});
$scope.oneAtATime = true;
$scope.groups = [
{
title: 'Dynamic Group Header - 1',
content: 'Dynamic Group Body - 1'
},
{
title: 'Dynamic Group Header - 2',
content: 'Dynamic Group Body - 2'
}
];
$scope.items = ['Item 1', 'Item 2', 'Item 3'];
$scope.addItem = function() {
var newItemNo = $scope.items.length + 1;
$scope.items.push('Item ' + newItemNo);
};
$scope.status = {
isFirstOpen: true,
isFirstDisabled: false
};
}]);
And the error I get in Chrome dev tools:
I know I am missing things here and it should be quite simple. Hoping someone can quickly help me out here in detail!
Thanks much!
Angular Bootstrap uses a standalone template file. Ensure you are including ui-bootstrap-tpls.js along with the ui-bootstrap.js file.
You can see this is being caused by ui-bootstrap not having the template:
.directive('accordion', function () {
return {
restrict:'EA',
controller:'AccordionController',
transclude: true,
replace: false,
templateUrl: 'template/accordion/accordion.html'
};
})
It simply seems that template file cannot be loaded. Make sure you can navigate to the file http://localhost:9000/...../accordion.html from within the browser...
Most likely typo in specifying template location...