Angular2 - call method after model changed - javascript

I have a simple component in my angular2 app:
#Component({
selector: 'app-product',
templateUrl: './product.component.html',
styleUrls: ['./product.component.css'],
providers: [ProductService, CardService]
})
export class ProductComponent implements OnInit {
private products;
constructor(private productService: ProductService, private cartService: CartService) {
}
ngOnInit() {
this.loadProducts();
}
loadProducts() {
this.productService.getProducts().subscribe(data => this.products = data);
}
addProductToCart(product: Product) {
this.cartService.addProduct(product);
}
basketAmount() {
this.cartService.getNumberOfProducts();
}
html file connected to it:
<div class="basket">
On your card: {{basketAmount()}}
</div>
<table class="table table-striped">
<thead class="thead-inverse">
<tr>
<th>#</th>
<th>Name</th>
<th>Desc</th>
<th>Price</th>
<th>Amount</th>
</tr>
</thead>
<tbody *ngFor="let product of products">
<tr>
<th scope="row">{{product.id}}</th>
<td>{{product.name}}</td>
<td>{{product.description}}</td>
<td>{{product.price}}</td>
<td>{{product.amount}}</td>
<button type="button" class="btn btn-success" (click)="addProductToCart(product)">Add to cart</button>
</tr>
</tbody>
</table>
and CartService
#Injectable()
export class CardService {
private cart: Product[] = [];
constructor() {
}
addProduct(product: Product) {
this.cart.push(product);
}
getTotalPrice() {
const totalPrice = this.cart.reduce((sum, cardItem) => {
return sum += cardItem.price, sum;
}, 0);
return totalPrice;
}
getNumberOfProducts() {
const totalAmount = this.card.reduce((sum, cardItem) => {
return sum += cardItem.amount, sum;
}, 0);
return totalAmount;
}
}
export interface Product {
id: number;
name: string;
description: string;
price: number;
amount: number;
}
I would like to update the number of items on my cart after added something into cart and show it on the view. I add items to cart by click and call addProductToCart method. At the same time I want to update a number of this items by basketAmount() which is defined in CartService and return number of items on the cart. I think I should trigger this basketAmount() method in a some way but I do not know how.
How to do it in a good way?

You have multiple options
After pushing item in your cart just call the basketAmount method again and you should have new value.
You can use BehaviorSubject. In this case you just need to subscribe to it and each time you will push the item to cart it will automatically update your cart.

Ok, I found a solution.
I add a simple numberOfItems variable into ProductComponent:
export class ProductComponent implements OnInit {
private products;
private numberOfItems;
constructor(private productService: ProductService, private cardService: CardService) {
}
ngOnInit() {
this.loadProducts();
}
loadProducts() {
this.productService.getProducts().subscribe(data => this.products = data);
}
addProductToCard(product: Product) {
this.cardService.addProduct(product);
}
basketAmount() {
this.numberOfItems = this.cardService.getNumberOfProducts();
}
}
and on the view I call two methods after click and update numberOfItems:
<div class="basket">
On your card: {{numberOfItems}}
</div>
<table class="table table-striped">
<thead class="thead-inverse">
<tr>
<th>#</th>
<th>Name</th>
<th>Desc</th>
<th>Price</th>
<th>Amount</th>
</tr>
</thead>
<tbody *ngFor="let product of products">
<tr>
<th scope="row">{{product.id}}</th>
<td>{{product.name}}</td>
<td>{{product.description}}</td>
<td>{{product.price}}</td>
<td>{{product.amount}}</td>
<button type="button" class="btn btn-success" (click)="addProductToCard(product); basketAmount()">Add to card</button>
</tr>
</tbody>
</table>

Related

Typescript error This condition will always return 'true' since the below type has no overlap

I am getting below error even by adding. can someone guide me or make some code changes so that the program executes as expected
Error: src/app/product/product.component.html:21:45 - error TS2367: This condition will always return 'true' since the types 'Observable<Product[]>' and 'number' have no overlap.
21 <table class = "table table-hover" *ngIf = "products != 0">
~~~~~~~~~~~~~
src/app/product/product.component.ts:9:16
9 templateUrl: './product.component.html',
~~~~~~~~~~~~~~~~~~~~~~~~~~
Error occurs in the template of component ProductComponent.
import { Component, OnInit } from '#angular/core';
import { Product } from './product.model';
import { Store } from '#ngrx/store';
import { Observable } from 'rxjs';
import { AppState } from './../app.state';
#Component({
selector: 'app-product',
templateUrl: './product.component.html',
styleUrls: ['./product.component.css']
})
export class ProductComponent implements OnInit {
products : Observable<Product[]>;
constructor(private store: Store<AppState>) {
this.products = this.store.select(state => state.product)
}
addProduct(name: any, price: any){
this.store.dispatch({
type : 'ADD_PRODUCT',
payload : <Product>{
name : name,
price : price
}
});
}
ngOnInit(): void {
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<table class = "table table-hover" *ngIf = "products != 0">
<thead>
<tr>
<td>Product Name</td>
<td>Product Price</td>
</tr>
</thead>
<tbody>
<tr *ngFor = "let product of products | async">
<td></td>
<td></td>
</tr>
</tbody>
</table>
You are missing something here
<table class = "table table-hover" *ngIf = "products != 0">
The error tells you that the condition its going to be always true because products is of type Observable<Product[]> and 0 is a number, and obviously they are diferent.
Maybe you want to go with this
<table class = "table table-hover" *ngIf = "(products | async).length != 0">
Here the condition is asking if the length of the array returned by the Observable products is not 0. You can use the someObservable | async when you want to retrieve the value emitted by an Observable in the template.

ERROR ReferenceError: $ is not defined at angular datatables [duplicate]

This question already has answers here:
JQuery - $ is not defined
(36 answers)
Closed 4 years ago.
I'm trying to add Paging and sorting to my table but I got this error , howerver I follow all the steps which listed here
http://l-lin.github.io/angular-datatables/#/getting-started.
I already check the previous problem but I did't work with me
I install all its dependencies
Here's the code of the component :-
import { Component, OnInit, OnDestroy } from '#angular/core';
import { ProductService } from '../../service/product-service.service';
import { Subscription, Subject } from 'rxjs';
#Component({
selector: 'app-admin-products',
templateUrl: './admin-products.component.html',
styleUrls: ['./admin-products.component.css']
})
export class AdminProductsComponent implements OnInit, OnDestroy {
products: any[];
filteredProducts: any[];
subscribtion: Subscription;
dtOptions: DataTables.Settings = {};
dtTrigger: Subject<any> = new Subject();
constructor(private productService: ProductService) {
this.subscribtion = productService.getAll().
// We take a copy of Products and Assigned to filteredProducts
subscribe(
products => {
this.filteredProducts = this.products = products;
this.dtTrigger.next();
}
);
}
ngOnInit() {
this.dtOptions = {
pagingType: 'full_numbers',
pageLength: 5,
processing: true
};
}
filter(queryStr: string) {
// console.log(this.filteredProducts);
if (queryStr) {
this.filteredProducts = this.products.
filter(p => p.payload.val().title.toLowerCase().includes(queryStr.toLowerCase()));
} else {
this.filteredProducts = this.products;
}
}
ngOnDestroy(): void {
// to UnSubscribe
this.subscribtion.unsubscribe();
}
}
Here's the code of the the HTML :-
I follow also all the steps here
<p>
<a routerLink="/admin/products/new" class="btn btn-primary">New Product</a>
</p>
<p>
<input type="text"
#query
(keyup)="filter(query.value)"
placeholder="Search ..." class="form-control">
</p>
<table
datatable [dtOptions]="dtOptions"
[dtTrigger]="dtTrigger" class="table" >
<thead class="thead-dark">
<tr>
<th scope="col">Title</th>
<th scope="col">Price</th>
<th scope="col">Edit</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let product of filteredProducts">
<td>{{ product.payload.val().title }}</td>
<td>{{ product.payload.val().price }}</td>
<td>
<a [routerLink]="['/admin/products/', product.key]">Edit</a>
</td>
</tr>
</tbody>
</table>
$ not defined mostly means you are not including JQuery.
try adding: to your program
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js" type="text/javascript"></script>
<script src="https://cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js" type="text/javascript"></script>
source

How to retrieve the single object from the datatable when it is clicked in angular?

Actually i have used datatable to show the data in angular using jquery libraries.
The user will search the name and suppose if the output comes this user will click on the body of datatable and i want to show this single JSON data in console.log(). How to get this approach?
component.ts file
export class ProcessAssessmentComponent implements OnInit {
selections: Observable<Selection[]>;
dataTable: any;
clients: any[];
constructor(private http: HttpClient,private selectionService: SelectionService,private processAssesstmentService:ProcessAssesstmentService,private chRef: ChangeDetectorRef) { }
ngOnInit() {
this.http.get('http://localhost:8080/api/selections')
.subscribe((data: any[]) => {
this.clients = data;
this.chRef.detectChanges();
// Now you can use jQuery DataTables :
const table: any = $('table');
this.dataTable = table.DataTable();
});
}
}
component.html file
<table class="table table-bodered">
<thead>
<tr>
<th>Mag No</th>
<th>SelectionDate</th>
<th> SelectedBy</th>
<th>PanEximNumber</th>
<th>Name</th>
<th>Address</th>
<th>PhoneNumber</th>
<th>SelectionType</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let client of clients">
<td>{{client.selectionId}}</td>
<td>{{client.selectionDate}}</td>
<td>{{client.selectedBy}}</td>
<td>{{client.panEximNumber}}</td>
<td>{{client.name}}</td>
<td>{{client.address}}</td>
<td>{{client.phoneNumber}}</td>
<td>{{client.selectionType}}</td>
</tr>
</tbody>
</table>
You can write a function which gets triggered on clicking the row.
<tr *ngFor="let client of clients" (click)="onClick(client)">
...
</tr>
In your js,
onClick(client:any){
console.log(client)
}
use (click) event on tr:
component.html
<table class="table table-bodered">
<thead>
<tr>
<th>Mag No</th>
<th>SelectionDate</th>
<th> SelectedBy</th>
<th>PanEximNumber</th>
<th>Name</th>
<th>Address</th>
<th>PhoneNumber</th>
<th>SelectionType</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let client of clients" (click)="selectedRow(client)">
<td>{{client.selectionId}}</td>
<td>{{client.selectionDate}}</td>
<td>{{client.selectedBy}}</td>
<td>{{client.panEximNumber}}</td>
<td>{{client.name}}</td>
<td>{{client.address}}</td>
<td>{{client.phoneNumber}}</td>
<td>{{client.selectionType}}</td>
</tr>
</tbody>
</table>
component.ts
export class ProcessAssessmentComponent implements OnInit {
selections: Observable<Selection[]>;
dataTable: any;
clients: any[];
constructor(private http: HttpClient,private selectionService: SelectionService,private processAssesstmentService:ProcessAssesstmentService,private chRef: ChangeDetectorRef) { }
ngOnInit() {
this.http.get('http://localhost:8080/api/selections')
.subscribe((data: any[]) => {
this.clients = data;
this.chRef.detectChanges();
// Now you can use jQuery DataTables :
const table: any = $('table');
this.dataTable = table.DataTable();
});
}
selectedRow(client)
{
console.log(client); //selected row data
}
}
Note:
Avoid use of jquery in angular.
because angular have its on events.
if you want pure angular datatable use ngx-datatable from https://www.npmjs.com/package/ngx-datatable.
You can handle the click and get the data from there.
export class ProcessAssessmentComponent implements OnInit {
selections: Observable<Selection[]>;
dataTable: any;
clients: any[];
constructor(
private http: HttpClient,
private selectionService: SelectionService,
private processAssesstmentService:ProcessAssesstmentService,
private chRef: ChangeDetectorRef
) { }
ngOnInit() {
this.http.get('http://localhost:8080/api/selections')
.subscribe((data: any[]) => {
this.clients = data;
this.chRef.detectChanges();
// Now you can use jQuery DataTables :
const table: any = $('table');
this.dataTable = table.DataTable();
$('table tbody').on('click', 'tr', function () {
var data = table.row(this).data();
alert( 'You clicked on '+ data[0] +'\'s row' );
});
});
}
}

Angular firebase not show the Key

I want To show the $key of client from firebasedatabase which also called uniquekey maybe like
-Kdl_wRRkn7njxgz4B54
i try but it dont show the key but show the other data in key
also try to replace $key with key dont work . i know it need change in code if any one can thanks :)
client.html
<div class="row">
<div class="col-md-6">
<h2><i class="fa fa-users"></i> Clients</h2>
</div>
<div class="col-md-6">
<h5 class="pull-right text-muted">Total Owed: {{totalOwed | currency:"USD":true}}</h5>
</div>
</div>
<table *ngIf="clients?.length > 0;else noClients" class="table table-striped">
<thead class="thead-inverse">
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Balance</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let client of clients">
<td>{{client.$key}}</td>
<td>{{client.firstName}} {{client.lastName}}</td>
<td>{{client.email}}</td>
<td>{{client.balance | currency:"USD":true}}</td>
<td><a [routerLink]="['/client/'+client.$key]" href="" class="btn btn-secondary btn-sm"><i class="fa fa-arrow-circle-o-right"></i> Details</a></td>
</tr>
</tbody>
</table>
<ng-template #noClients>
<hr>
<h5>There are no clients in the system</h5>
</ng-template>
client.ts
import { Component, OnInit } from '#angular/core';
import { ClientService } from '../../services/client.service';
import { Client } from '../../models/Client';
#Component({
selector: 'app-clients',
templateUrl: './clients.component.html',
styleUrls: ['./clients.component.css']
})
export class ClientsComponent implements OnInit {
clients:any[];
totalOwed:number;
constructor(
public clientService:ClientService
) { }
ngOnInit() {
this.clientService.getClients().valueChanges().subscribe(clients => {
this.clients = clients;
this.getTotalOwed();
});
}
getTotalOwed(){
let total = 0;
for(let i = 0;i < this.clients.length;i++){
total += parseFloat(this.clients[i].balance);
}
this.totalOwed = total;
console.log(this.totalOwed);
}
}
client.service.ts
import { Injectable } from '#angular/core';
import { AngularFireDatabase} from 'angularfire2/database';
import { AngularFireObject, AngularFireList } from 'angularfire2/database';
import { Observable } from 'rxjs';
import { Client } from '../models/Client';
#Injectable()
export class ClientService {
clients: AngularFireList<any>;
client: AngularFireObject<any>;
constructor(
public af:AngularFireDatabase
) {
this.clients = this.af.list('/clients') as AngularFireList<Client[]>;
}
getClients(){
return this.clients;
}
newClient(client:Client){
this.clients.push(client);
}
getClient(id:string){
this.client = this.af.object('/clients/'+id) as AngularFireObject<Client>;
return this.client;
}
}
Use snapshotChanges().map() to store the key:
constructor(
public af:AngularFireDatabase
) {
this.clientsRef = this.af.list('/clients') as AngularFireList<Client[]>;
this.clients = this.clientsRef.snapshotChanges().pipe(
map(changes =>
changes.map(c => ({ key: c.payload.key, ...c.payload.val() }))
)
);
}
Then you should be able to access it as normal:
<td>{{client.key}}</td>

Retrive firestore documentId and add to html

I'm building my first Angular app and I'm trying to integrate firestore.
So far I was able to retrieve data from firestore and also log the id with snapshot but I'm not being able to bring it all together.
Is there a way to add the id to the client Model?
I was reading the angularfire2 documentation where it says that we can't use the $key property and now we should use snapshot but I can't figure it out.
Thanks
This is my Service
#Injectable()
export class ClientService {
clientsCollection: AngularFirestoreCollection<Client>;
clients: Observable<Client[]>;
snapshot: any;
constructor(private afs: AngularFirestore) {
this.clientsCollection = this.afs.collection('clients');
this.clients = this.clientsCollection.valueChanges();
// snapshot for id/metadata
this.snapshot = this.clientsCollection.snapshotChanges()
.map(arr => {
console.log(arr);
});
}
}
My client.component.ts
#Component({
selector: 'app-clients',
templateUrl: './clients.component.html',
styleUrls: ['./clients.component.css']
})
export class ClientsComponent implements OnInit {
clients: Client[];
snapshots: any[];
constructor(
public clientService: ClientService
){}
ngOnInit(){
this.clientService.clients.subscribe(clients => {
this.clients = clients;
});
}
}
And my client.component.html
<table *ngIf="clients?.length > 0; else noClients" class="table table-striped">
<thead class="thead-inverse">
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Balance</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let client of clients">
<td></td>
<td>{{ client.firstName }} {{ client.lastName }}</td>
<td>{{ client.email }}</td>
<td>{{ client.balance }}</td>
<td>Details</td>
</tr>
</tbody>
</table>
<ng-template #noClients>
<hr>
<h5>There are no clients in the system</h5>
</ng-template>
this is what I have so far
You can find answer in angularfire2 documents
export class AppComponent {
private shirtCollection: AngularFirestoreCollection<Shirt>;
shirts: Observable<ShirtId[]>;
constructor(private readonly afs: AngularFirestore) {
this.shirtCollection = afs.collection<Shirt>('shirts');
// .snapshotChanges() returns a DocumentChangeAction[], which contains
// a lot of information about "what happened" with each change. If you want to
// get the data and the id use the map operator.
this.shirts = this.shirtCollection.snapshotChanges().map(actions => {
return actions.map(a => {
const data = a.payload.doc.data() as Shirt;
const id = a.payload.doc.id;
return { id, ...data };
});
});
}
}
and this template
<ul>
<li *ngFor="let shirt of shirts | async">
{{ shirt.id }} is {{ shirt.price }}
</li>
</ul>

Categories