Hello I have a shopping cart which I am able to add products as well as delete products using a api . When deleting an item from cart it has to be deleted using a button click on each item by id, one by one. which is fine for that purpose, but I am trying to do empty all items in cart url from a function not a button which has not worked for me because id will be undefined.
My question is, if this is even possible? I have tried clearing the array of items such as cartItems.length = 0 or cartItems = [] this only empties a copy of the array from my understanding, so therefore the items will still exist in the cart from what I have tried.
I'm reaching out to see if anyone can answer this and point me in the right direction it will be greatly appreciated. I have included a snippet of of html and the cart service for the delete item.
cart service
RemoveProductFromCart(id:number):Observable<void>{
return this.http.delete<CartItem[]>(`${this.cartUrl}/${id}`)
.pipe(catchError(_err => of (null))
);
}
cartitem.ts
handleRemoveFromCart(){
alert("hit remove from cart");
this.cartService.RemoveProductFromCart(this.cartItem.id)
.subscribe(() =>
console.log("Product with Id deleted",
this.cartItem.id),
(err) => console.log(err)
);
}
html for cartitem
<div class="container-md" >
<div>
<img class="img-fluid" [src]="cartItem.imageUrl" style="padding-bottom:5px; padding-top:50px" alt="Cart">
<div class="text-nowrap" style="font-size:13px; width: 400px">{{cartItem.productName}}</div>
{{cartItem?.size}}{{Valuesize}}
<div style="font-size:13px;"><strong>{{ (cartItem.price) | currency}}</strong></div>
<div class=" float-right">
<span><i class="fa-solid fa-trash-can" style="color:#D30169; cursor: pointer;" (click)="handleRemoveFromCart();handleReload()"></i></span>
</div>
</div>
</div>
cart html
<div *ngIf="cartItems.length === 0"class="alert alert-
info">Your Cart is Empty</div>
<div *ngIf="product">
<div>{{product.name}}</div>
<input type="number" [(ngModel)]="product.num"
min="0"/>
</div>
<div *ngFor="let item of cartItems; let i = index">
<app-cart-item [cartItem]="item"></app-cart-item>
</div>
<li class="list-group-item" style="margin-top:30px;">
<strong>Total Items {{itemTotal}}</strong>
</li>
<li class="list-group-item active">
<strong>Total: {{ cartTotal | currency}}</strong>
</li>
<li class="btn btn-primary" style="height: 40px;
width:210px; margin-top:20px; color:ffffff">
<a class="" routerLink="/clothing" style="color:
#ffffff; cursor: pointer;">Continue
Shopping</a>
</li>
<li class= "btn" style="height: 40px; width:210px;
margin-top: 20px; background-color:#D30169">
<a class="" routerLink="/checkout" style="color:
#ffffff">Go To Checkout</a>
</li>
</div>
</div>
</div
Have a look into Rxjs
cart.service.ts
Create a behavior subject of cart items
cartItems$ = new BehaviorSubject<CartItem>([]);
To clear all the items
clearCart() {
this.cartItems$.next([]);
}
To add or remove an item
addToCart(cartItem: CartItem) {
this.cartItems$.next([cartItem, ...this.cartItems.getValue()]);
}
removeFromCart(cartItemId: string) {
this.cartItems$.next([this.cartItems.getValue().filter((cartItem) => cartItem.id !== cartItemId)]);
}
cartItem.ts
To subscribe to cartItems
cartItems:CartItem[] = []
constructor(private cartService: CartService) {
this.cartService.cartItems$.subscribe((v) => (this.cartItems = v));
}
Don't forget to unsubscribe
Related
I have an application that list's some content on page dynamically. So we choose some achievement, and create a section that have from 1 to X achievements and on this page, they're listed using col-md-x.
I want to use the col-md-6 size, because I have some elements that's dynamic and I need some space to show it.
But my problem here is, I want to make the columns to use the empty space, for example this image:
The red area, below section 'informe um nome2' isn't occupied, but I want to show the third, 'informe um nome3' below this section and use the free space.
Here's my current code:
<div class="row col-md-12">
#foreach (var item in Model)
{
<div class="col-md-6" style="margin-bottom:20px">
<div class="card">
#if (item.SectionHeader != null)
{
<div class="card-header text-center" style="height: 120px; background-image: url('#item.SectionHeader.ImageURL'); background-size:cover; color:black">
#item.SectionName <a title="Renomear" ng-click="Rename(#item.SectionID)"><i class="fa fa-xs fa-clipboard" style="cursor:pointer"></i></a> <a title="Baixar arquivo da seção" ng-click="DownloadFiles(#item.SectionID, false)"><i class="fa fa-xs fa-download" style="cursor:pointer; color:green"></i></a> <a title="Apagar seção" ng-click="Delete(#item.SectionID)"><i class="fa fa-xs fa-trash" style="cursor:pointer; color:red"></i></a>
</div>
}
else
{
<div class="card-header text-center">
#item.SectionName <a title="Editar" href="#Url.Action("EditSection", "Guides", new { sectionID = item.SectionID })"><i class="fa fa-xs fa-edit" style="cursor:pointer; color:orange"></i></a> <a title="Renomear" ng-click="Rename(#item.SectionID)"><i class="fa fa-xs fa-clipboard" style="cursor:pointer"></i></a> <a title="Baixar arquivo da seção" ng-click="DownloadFiles(#item.SectionID, false)"><i class="fa fa-xs fa-download" style="cursor:pointer; color:green"></i></a> <a title="Apagar seção" ng-click="Delete(#item.SectionID)"><i class="fa fa-xs fa-trash" style="cursor:pointer; color:red"></i></a>
</div>
}
<div class="card-body">
#foreach (var chievo in item.GameAchievements.OrderBy(f => f.SectionOrder.HasValue ? f.SectionOrder : f.AchievementID))
{
<div class="achievement" id="#chievo.AchievementID" style="margin-top:10px; margin-bottom:10px; z-index:1; height:60px">
<ul style="list-style:none; margin-top:20px; padding-left:0px" id="#chievo.AchievementID">
<img src="#chievo.Icon" style="float:left" />
<b> #chievo.DisplayName</b>
<br />
#if (string.IsNullOrEmpty(chievo.Description))
{
<i class="fa fa-edit" style="cursor:pointer" ng-click="OpenModal(#chievo.AchievementID)"></i>
}
else
{
<i>#chievo.Description</i>
}
<div style="float:right" class="text-center">
<a title="Alterar Dificuldade" ng-click="ChangeDifficulty(#chievo.AchievementID)"><i class="fa fa-xs fa-star" style="cursor:pointer; margin-left:10px; color:orange"></i></a>
<a title="Remover da Seção" ng-click="RemoveFromSection(#chievo.AchievementID)"><i class="fa fa-xs fa-trash-alt" style="cursor:pointer; margin-left:10px; color:red"></i></a>
</div>
#if (chievo.Difficulty != null)
{
<img src="#chievo.Difficulty.DifficultyImage" style="float:right; width:32px" />
}
</ul>
</div>
if (item.SectionDivisor != null && (item.IndividualDivisor.HasValue && item.IndividualDivisor.Value == true))
{
<div class="text-center">
<img src="#item.SectionDivisor.DivisorImageURL" />
</div>
}
}
#if (item.SectionDivisor != null && (item.IndividualDivisor.HasValue && item.IndividualDivisor.Value == false))
{
<div class="text-center" style="margin-top:20px">
<img src="#item.SectionDivisor.DivisorImageURL" />
</div>
}
</div>
</div>
</div>
}
</div>
So, basically, I want help making the columns auto-align and auto-fill.
I'm using sb admin 2 free template version.
In this case that won't work as desired, because the elements follow a row column distribution and when two elements fill a row, the next element goes all the way down to the space the longest element of the first two occupies and starts there.
What you could use in this case is something like Isotope using the horizontal masonry layout. You can check more on this on Isotope's docs.
If you still prefer to use the bootstrap option I would recommend you define a height for each column and make it scrollable. That would be the easiest way to order them visually.
I have a small accordion on one of my site's pages and I cannot seem to toggle properly between the plus and the minus signs.
it toggles fine if I open and close the same accordion item but the problem is when I click on another item while this one is active, it leaves the minus sign on.
<div class="accordion">
<div class="accordion-item item1 verde">
<div class="title"><i class="plusminus fas fa-plus" aria-hidden="true"></i>item1</div>
<div class="content"><p>content for item 1</p></div>
</div>
<div class="accordion-item item2 verde">
<div class="title"><i class="plusminus fas fa-plus" aria-hidden="true"></i>item2</div>
<div class="content"><p>content for item 2</p></div>
</div>
</div>
$('.accordion-item .heading').on('click', function(e) {
e.preventDefault();
// Add the correct active class
if($(this).closest('.accordion-item').hasClass('active')) {
// Remove active classes
$('.accordion-item').removeClass('active');
} else {
// Remove active classes
$('.accordion-item').removeClass('active');
// Add the active class
$(this).closest('.accordion-item').addClass('active');
}
if($(this).closest('.accordion-item').hasClass('active')) {
$(this).find(".plusminus").removeClass('fa-plus').addClass('fa-minus');
} else {
$(this).find(".plusminus").removeClass('fa-minus').addClass('fa-plus');
}
// Show the content
var $content = $(this).next();
$content.slideToggle(100);
$('.accordion-item .content').not($content).slideUp('fast');
});
You only need to reset the fa-minus and fa-plus classes at the begining of the event.
Change every plusminus class to fa-plus and at the end of the event handler you set as you are already doing.
$('.accordion-item .heading').on('click', function (e) {
e.preventDefault();
$(".plusminus").removeClass('fa-minus').addClass('fa-plus');
// Add the correct active class
if ($(this).closest('.accordion-item').hasClass('active')) {
// Remove active classes
$('.accordion-item').removeClass('active');
} else {
// Remove active classes
$('.accordion-item').removeClass('active');
// Add the active class
$(this).closest('.accordion-item').addClass('active');
}
if ($(this).closest('.accordion-item').hasClass('active')) {
$(this).find(".plusminus").removeClass('fa-plus').addClass('fa-minus');
} else {
$(this).find(".plusminus").removeClass('fa-minus').addClass('fa-plus');
}
// Show the content
var $content = $(this).next();
$content.slideToggle(100);
$('.accordion-item .content').not($content).slideUp('fast');
});
<div class="accordion">
<div class="accordion-item item1 verde">
<a href="#" class="heading">
<div class="title"><i class="plusminus fas fa-plus" aria-hidden="true"></i>item1</div>
</a>
<div class="content">
<p>content for item 1</p>
</div>
</div>
<div class="accordion-item item2 verde">
<a href="#" class="heading">
<div class="title"><i class="plusminus fas fa-plus" aria-hidden="true"></i>item2</div>
</a>
<div class="content">
<p>content for item 2</p>
</div>
</div>
</div>
I have a page, where in the top, there's a question, and below that, there's a list of answers, and you can add new answers too. (image below)
In a format like (name of the user who answered) "válasza: ekkor: (date), which means his answer, at this time.
Below every question there are 3 buttons, + C and -, the ppl who asked the question can accept the answer (+), deny the answer (-) and cancel applying or denying (C).
Problem is, if I press any button for example in any row, the effect only applies to the first one.
Accept -> Green Background, Deny -> red, C -> white
Here's the HTML code:
<div class="container" style="...">
<ul class="list-group">
<li id="colorTarget" class="list-group-item" *ngFor="let comment of actualComments">
<div style="...">
<b>{{comment.iro}}</b> válasza, ekkor: {{comment.mikor}} {{comment.leir}}
</div><br/>
<div class="row" style="width:120px;">
<div style="...">
<button class="btn btn-light" id="buttonTarget1" (click)="acceptAnswer(comment.iro,comment.mikor,comment.leir)">
+
</button>
</div>
<div style="...">
<button class="btn btn-light" id="buttonTarget2" (click)="clearAnswer(comment.iro,comment.mikor,comment.leir)">
C
</button>
</div>
<div style="...">
<button class="btn btn-light" id="buttonTarget3" (click)="denyAnswer(comment.iro,comment.mikor,comment.leir)">
-
</button>
</div>
</div>
</li>
</ul>
</div>
And the TypeScript:
acceptAnswer(iro, mikor, iras) {
var x = this.answerid(iro, mikor, iras);
this.actualComments[x].accepted = 1;
var target = document.getElementById("colorTarget");
target.style.background = 'green';
target.style.color = 'white';
}
Deny and Clear Answer functions are exactly like this, but with red and white color.
If you have any idea..please.
So, in short: unique id to all li and pass this unique id to the functions.
try to use CSS classes on li elements:
<div class="container" style="...">
<ul class="list-group">
<li id="colorTarget"
class="list-group-item {{ statuses[i] }}"
*ngFor="let comment of actualComments; let i = index;">
<div style="...">
<b>{{comment.iro}}</b> válasza, ekkor: {{comment.mikor}} {{comment.leir}}
</div>
<br/>
<div class="row" style="width:120px;">
<div style="...">
<button class="btn btn-light"
id="buttonTarget1"
(click)="statuses[i] = 'accept'; acceptAnswer(comment.iro,comment.mikor,comment.leir)">
+
</button>
</div>
<div style="...">
<button class="btn btn-light"
id="buttonTarget2"
(click)="statuses[i] = 'clear'; clearAnswer(comment.iro,comment.mikor,comment.leir)">
C
</button>
</div>
<div style="...">
<button class="btn btn-light"
id="buttonTarget3"
(click)="statuses[i] = 'deny'; denyAnswer(comment.iro,comment.mikor,comment.leir)">
-
</button>
</div>
</div>
</li>
</ul>
</div>
this way each li element will get CSS class ('accept', 'deny' or 'clear'). I made it inline within each button (click) function, it also could be dome in the TS file.
Now just add some SCSS:
li {
&.accept {
color: white;
background: green;
}
&.deny,
&.clear {
color: white;
background: red;
}
}
or CSS:
li.accept {
color: white;
background: green;
}
li.deny,
li.clear {
color: white;
background: red;
}
If you need to isolate li's selection, you may add its ID to each li, like li#colorTarget
Now, in you TS file you do not need to deal with styling:
acceptAnswer(iro, mikor, iras) {
var x = this.answerid(iro, mikor, iras);
this.actualComments[x].accepted = 1;
}
UPDATE
you wrote:
actualComment.accepted represent the accept/deny/clear colors
if I understand correctly, actualComment.accepted will have an integer representing one of three colors. If this is correct, let's assume 1 is green (accepted), 2 and 3 are red (deny and clear). In this case you could only change li from my original answer to:
<li id="colorTarget"
class="list-group-item"
[class.accept]="comment.accepted === 1"
[class.deny]="comment.accepted === 2"
[class.clear]="comment.accepted === 3"
*ngFor="let comment of actualComments">
or add public statusesMap (or ENUM) to your ts file:
statusesMap = {
1: 'accept',
2: 'deny',
3: 'clear'
}
and in your HTML:
<li id="colorTarget"
class="list-group-item {{ statusesMap[comment.accepted] }}"
*ngFor="let comment of actualComments">
Here is what I came up with.
Updated template. Added a div, and moved the *ngFor to it to allow me to grab the data needed to create the id. The createId function build the id based on values used.
<div class="container">
<ul class="list-group">
<div *ngFor="let comment of actualComments">
<li [id]="createId(comment.iro,comment.mikor,comment.leir)" class="list-group-item">
<div>
<b>{{comment.iro}}</b> válasza, ekkor: {{comment.mikor}} {{comment.leir}}
</div>
<br/>
…
</li>
</div>
</ul>
</div>
Addition to the component to create the id. This is used in the template above.
createId(iro, mikor, leir) {
return `${iro}${mikor}${leir}`;
}
Update to acceptAnswer function to use the new id created using the new createId function.
acceptAnswer(iro, mikor, iras) {
const x = this.answerid(iro, mikor, iras);
this.actualComments[x].accepted = 1;
const target = document.getElementById(this.createId(iro, mikor, iras));
target.style.background = 'green';
target.style.color = 'white';
}
I have a JSON file which has this structure:
{ "short_title":"HB_START_SHORT_TITLE",
"tips{"1":"HB_START_TIPS_1_1","2":"HB_START_TIPS_1_2","3":"HB_START_TIPS_1_3","4":"HB_START_TIPS_1_4"},
},
I would like to print the nested item "tips" as a slider with previous-next buttons.
Therefore I wrote this code in my HTML below:
<ul class="sb-parentlist">
<div data-ng-repeat="parts in data track by $index">
<li>
<div class="sb-open" ng-show="showDetails">
{{parts.short_text|translate}}
<br>
<li><span class="sb-text-title" href="#" ng-click="OpenTips = ! OpenTips"><b>Tips</b></span>
<span ng-show="OpenTips" class="sb-open">
<br>
<div ng-repeat="data in parts.tips track by $index" ng-class="{'tips-hide': $index > $index + 1}">
{{data|translate}}
<br>
<div class="keys">
<button type="button" class="btn btn-pre" ng-click="$index = $index > 1 ? $index - 1 : 1">Previous</button>
<button type="button" class="btn btn-next" ng-click="$index = $index < data.length ? $index + 1 : data.length">Next</button>
</div>
</div>
</span>
</li>
</ul>
</div>
</li>
</div>
</ul>
and the tips-hide class in CSS:
.tips-hide {
left: -100px !important;
opacity: 0 !important;
}
.sb-open {
display: block;
height: auto;
opacity: 1;
transition:all 0.6s ease;
li {
display: block;
}
}
But what I am getting is every element in the list one after the other, with the buttons on the bottom of each one of them.
ScreenShot below:
ng-repeat on object can't be tracked by index (at least not in that way)
I've changed few parts on code to simulate on code snippet, see if it helps.
var app = angular.module('app', []);
app.controller('DemoCtrl', function($scope) {
this.openTips = true;
$scope.tipsIndex = 1;
this.info = [{ "short_title":"HB_START_SHORT_TITLE",
"tips" : {"1":"HB_START_TIPS_1_1","2":"HB_START_TIPS_1_2","3":"HB_START_TIPS_1_3","4":"HB_START_TIPS_1_4"}
}];
$scope.decrement = function() {
if($scope.tipsIndex > 1){
$scope.tipsIndex = $scope.tipsIndex - 1;
}
};
$scope.increment = function(partsIndex) {
if($scope.tipsIndex < partsIndex){
$scope.tipsIndex = $scope.tipsIndex + 1;
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="app">
<div ng-controller="DemoCtrl as demo">
<ul class="sb-parentlist">
<ul class="sb-parentlist">
<div data-ng-repeat="parts in demo.info track by $index">
<li>
<div class="sb-open">
{{parts.short_title}}
<br>
<li><span href="#" ng-click="demo.openTips = !demo.openTips"><b>Tips</b></span>
<span ng-show="demo.openTips" class="sb-open">
<br>
<div ng-repeat="(key, value) in parts.tips" ng-show="key == tipsIndex">
{{key}}
{{value}}
<br>
<div class="keys">
{{Object.size(parts.tips)}}
</div>
</div>
<button ng-click='decrement()'>Previous</button>
<button type="button" class="btn btn-next" ng-click="increment(3)">Next</button>
</span>
</li>
</div>
</ul>
</li>
</div>
</ul>
</ul>
</div>
</body>
I have the below div tag which contains a dropdown and a carousel. Whenever I select from the dropdown I call a function in an AngularJS controller which calls golang at the backend using http.golang to generate the data to be displayed in the carousel and sends a response which is displayed in the carousel but creates a page refresh. I just want this div to be loaded again and not the entire page to load. How can I do this using AngularJS?
HTML:
<div class="col-xs-11" style="width:750pt; height:180pt; margin-top:30px; ">
<div class="row " style="margin-left:75pt;">
<div class="dropdown">
<label class="text-center" style="margin-top:10pt;font-size:14pt;"> Deals on</label>
<button data-toggle="dropdown" class=" btn dropdown-toggle dropdown-menu-hover-color text-center" style="padding:0px 0px;background-color:white;" href="#"><label style="font-size:14pt; color: #191970;" >{{currentCategory}}</label> <b class="caret"></b></button>
<ul class="dropdown-menu submenu-hover-color dropdown-menu-scroll" style="margin:0;">
<li>{{page.category_name}}</li>
</ul>
<div style="display: inline;" ng-hide="deals"> <label class="text-center" style="margin-top:10pt;font-size:14pt;color: #191970;;">No deals found, please select a different category. </label></div>
</div>
</div>
<div id ="carousel_category" class="carousel slide " ng-show ="deals">
<div class="carousel-inner text-center" style="margin-left:75px;">
<div class="item active">
<div class="carousel-width" ng-repeat="(key, value) in categoryList.slice(0, 5) track by $index">
<img ng-src= {{value.imageUrl}} alt="No Image"/>
<h6>{{value.productName}}</h6>
<b style="color:#990000;">{{value.price}}</b> <b style="color:green;">{{value.discount}}</b>
</div>
</div>
<div class="item" ng-repeat="page in category_products_pages">
<div class="carousel-width" ng-repeat="image in page">
<img ng-src= {{image.imageUrl}} alt="No Image" />
<h6>{{image.productName}}</h6>
<b style="color:#990000;">{{image.price}}</b> <b style="color:green;">{{image.discount}}</b>
</div>
</div>
</div> <!--End of carousel-inner-->
<a class="left carousel-control pull-left" href="#carousel_category" data-slide="prev"><i class="glyphicon glyphicon-chevron-left"></i></a>
<a class="right carousel-control pull-right" href="#carousel_category" data-slide="next"><i class="glyphicon glyphicon-chevron-right"></i></a>
</div><!--End of carousel_cartegory-->
</div>
Angularjs :(Below code is inside angularjs controller)
$scope.changeCategory = function(category_id) {
$http({
method : 'POST',
url : '/changeCategory',
data : {"category_id" : category_id},
headers : { 'Content-Type': 'application/x-www-form-urlencoded' }
// set the headers so angular passing info as form data (not request payload)
}).success(function(data, status, headers, config) {
$scope.deals = true;
$scope.products = JSON.parse(JSON.stringify(data));
$scope.currentCategory = $scope.products.categoryInfo.category_name;
$scope.dropdownCategoryList = $scope.products.dropdownCategoryList;
$scope.categoryList = $scope.products.categoryList;
if($scope.categoryList == undefined) {
$scope.deals = false;
return;
}
currList = $scope.categoryList;
for(var i=0;i<currList.length;i++){
product_type = currList[i].product_type;
if (product_type != "Price_With_Discount") {
currList[i].price = "";
currList[i].discount = "Save Upto "+currList[i].discount + "%";
} else {
currList[i].price = "$" + currList[i].price;
currList[i].discount = "(Save "+currList[i].discount+"%)"
}
}
//category_data
var category_products_page = [];
var category_products_pages = [];
var count_1 = 5;
$scope.category_products_pages = category_products_pages;
for(var j=5;j<$scope.categoryList.length;j++){
if(count_1 > 1){
category_products_page.push($scope.categoryList[j]);
count_1--;
if($scope.categoryList.length === j+1){
category_products_pages.push(category_products_page);
//return;
break;
}
}else{
category_products_page.push($scope.categoryList[j]);
category_products_pages.push(category_products_page);
category_products_page = [];
count_1 = 5;
}
}
}).error(function(data, status, headers, config) {
$scope.status = status;
$window.alert("error")
});
}
});