How to call custom service inside another services in angular 6 - javascript

How to call the custom service in another custom service in angular 6
I have created two service user.services.ts and counter.services.ts
Getting error : Cannot invoke an expression whose type lacks a call signature. Type 'Number' has no compatible call signatures
Even try to add service name in app.modules providers array but same error getting
user.services.ts
import { Injectable } from '#angular/core';
import { CounterService } from './counter.service';
#Injectable({
providedIn: 'root',
})
export class UserService {
activeUsers = ['Max', 'Anna'];
inactiveUsers = ['Chris', 'Manu'];
constructor(private counterSer: CounterService) { }
setUserActive(id: number) {
this.activeUsers.push(this.inactiveUsers[id]);
this.inactiveUsers.splice(id, 1);
this.counterSer.activeToInactiveCounter();
}
setUserInactive(id: number) {
this.inactiveUsers.push(this.activeUsers[id]);
this.activeUsers.splice(id, 1);
}
}
Counter.services.ts
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class CounterService {
constructor() { }
activeToInactiveCounter = 0;
inactiveToActiveCounter = 0;
incrementActiveToInactive() {
this.activeToInactiveCounter++;
console.log(this.activeToInactiveCounter);
}
incrementInactiveToActive() {
this.inactiveToActiveCounter++;
console.log(this.inactiveToActiveCounter);
}
}

You are calling the property activeToInactiveCounter as a method. I think you are trying to call incrementActiveToInactive().
So instead of: this.counterSer.activeToInactiveCounter(); in setUserActive, you should have: this.counterSer.incrementActiveToInactive();
setUserActive(id: number) {
this.activeUsers.push(this.inactiveUsers[id]);
this.inactiveUsers.splice(id, 1);
this.counterSer.incrementActiveToInactive();
}

Related

How can I avoid making multiple get request in this Angular app?

I am working on an e-commerce app in Angular 11.
I have a service that makes a get request and reads a JSON.
The purpose of this service is to determine which product is promoted.
The service:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Campaign } from '../models/campaign';
#Injectable({
providedIn: 'root'
})
export class PromoProductsService {
public apiURL: string;
constructor(private http: HttpClient) {
this.apiURL = `${apiURL}/promo-products`;
}
public getPromoData(){
return this.http.get<Campaign>(`${this.apiURL}/campaign`);
}
}
In the product card component I have:
public getPromoData() {
this.PromoProductsService.getPromoData().pipe(takeUntil(this.destroyed$)).subscribe(data => {
this.campaignData = data;
this.campaignProducts = this.campaignData.campaign.products;
let promoProduct = this.campaignProducts.find((product:any) => {
return this.product.product_id == product.id;
});
if (promoProduct) {
this.isCampaignProduct = true;
this.cdr.detectChanges();
}
});
}
The problem
The code above checks, for every product card, if the product is in the array of promoted products.
The problem with this is that there is a request for the array of promoted products for every product on the page.
Question:
How can I make (and use) a single request for the array of promoted products?
You should share the result of your HTTP request to all components who need it.
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Campaign } from '../models/campaign';
#Injectable({
providedIn: 'root'
})
export class PromoProductsService {
public apiURL: string;
promo$: Observable<Campaign>;
constructor(private http: HttpClient) {
this.apiURL = `${apiURL}/promo-products`;
this.promo$ = this.http.get<Campaign>(`${this.apiURL}/campaign`).pipe(shareReplay());
}
}
This observable can the be used by different components in order only perform one single HTTP call (on first subscription).
In your component you can adjust your code to do the following:
public getPromoData() {
this.PromoProductsService.promo$.pipe(takeUntil(this.destroyed$)).subscribe(data => {
this.campaignData = data;
this.campaignProducts = this.campaignData.campaign.products;
let promoProduct = this.campaignProducts.find((product:any) => {
return this.product.product_id == product.id;
});
if (promoProduct) {
this.isCampaignProduct = true;
this.cdr.detectChanges();
}
});
}
There are several approaches, just to name 2 I would recommend:
make use of shareReplay rxjs operator
Call the service from the parent, that holds all the products and provide the whole list to the child, so the child is pretty much dumb

Angular Behavior Subject multiple arguments

i need to get values from one component and get them in other one, i want to use behaviorSubject, but i dont get, how to pass 2 arguments in behaviorSubject, iam getting error TS2554: Expected 1 arguments, but got 2.
//service
import { Injectable } from '#angular/core';
import {BehaviorSubject} from 'rxjs';
#Injectable({
providedIn: 'root'
})
interface ICellSelection {
xId: number;
yId: number;
}
export class plotService {
public IdsForCellSelection: BehaviorSubject<ICellSelection> = new BehaviorSubject<ICellSelection>(null);
public setIdsForCellSelection(xItemIndex: number, yItemIndex: number) {
this.IdsForCellSelection.next(xItemIndex, yItemIndex);
}
constructor() { }
}
// component
public selectArea() {
this.store.dispatch(({ payload: [] }));
this.selectedItems = [this.xItem, this.yItem];
this.selectionChanged.emit(this.selectedItems);
//here iam trying to send 2 arguments+
this.plotService.setIdsForCellSelection( this.xItemIndex, this.yItemIndex);
}
BehaviourSubject takes one argument and you are trying to pass 2.
So below is not correct:
this.IdsForCellSelection.next(xItemIndex, yItemIndex);
Instead do this:
this.IdsForCellSelection.next({xItemIndex, yItemIndex});
Pass it around as an object of the defined type. Also if the default null isn't required, it'd be more suited to use ReplaySubject with buffer 1 instead of BehaviorSubject.
Service
import { Injectable } from '#angular/core';
import { ReplaySubject } from 'rxjs';
interface ICellSelection {
xId: number;
yId: number;
}
#Injectable({ providedIn: 'root' })
export class plotService {
public IdsForCellSelection: ReplaySubject<ICellSelection> = new ReplaySubject<ICellSelection>(1);
public setIdsForCellSelection(xItemIndex: number, yItemIndex: number) {
this.IdsForCellSelection.next(
{ xId: xItemIndex, yId: yItemIndex } as ICellSelection
);
}
constructor() { }
}
To maintain type coherence across the application, you'd need to define the type in an external file and import it where it's required.
Eg.
cell-selection.ts
export interface ICellSelection {
xId: number;
yId: number;
}
Component
import { ICellSelection } from './cell-selection';
...
public selectArea() {
this.store.dispatch(({ payload: [] }));
this.selectedItems = [this.xItem, this.yItem];
this.selectionChanged.emit(this.selectedItems);
this.plotService.setIdsForCellSelection({
xId: this.xItemIndex,
yId: this.yItemIndex
} as ICellSelection);
}
Service
import { Injectable } from '#angular/core';
import { ReplaySubject } from 'rxjs';
import { ICellSelection } from './cell-selection';
#Injectable({ providedIn: 'root' })
export class plotService {
public IdsForCellSelection: ReplaySubject<ICellSelection> = new ReplaySubject<ICellSelection>(1);
public setIdsForCellSelection(itemIndex: ICellSelection) {
this.IdsForCellSelection.next(itemIndex);
}
constructor() { }
}
you can also pass as an array, like so:
this.IdsForCellSelection.next([xItemIndex, yItemIndex, ..]);
suggestion
don't use an uppercase when naming a variable and use the '$' as a prefix for observables and subjects, it makes your code more readable throughout future developers
"idsForCellSelection$"

Why does this variable always return null ? Angular Service

Its now 8 hours trying to solve a trivial issue & I can't believe it !
here below a script of angular service
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class GetStockDataService {
constructor(public http:HttpClient) { }
RequestData={"query":"{\n stock{\n history{\n \n low\n high\n demand\n }\n }\n}"}
getstockdata(){
return this.http.post('http://localhost:3000/',this.RequestData)
}
}
and here is a component script which is calling that service
import { Component, OnInit } from '#angular/core';
import { GetStockDataService } from '../services/get-stock-data.service';
import { Platform } from '#ionic/angular';
#Component({
selector: 'app-Stocks',
templateUrl: 'Stocks.page.html',
styleUrls: ['Stocks.page.scss']
})
export class StocksPage implements OnInit {
constructor(private GetStockData:GetStockDataService , private platform : Platform) {}
res:any
ngOnInit(){
this.getdata().subscribe(data=>{this.res=data});
console.log(this.res)
}
getdata(){
return this.GetStockData.getstockdata() }}
WHY the "res" variable is always returning NULL ????
knowing that when I put the console log the variable inside there in the function in the subscription part .. it returns data
but I can't make this variable global ... how could I do that ? I just want to get the data from the subscription to the "res" variable to use it the HTML file later .
Due to Async call, console.log(this.res) executes before server call is processed.
Change
this.getdata().subscribe(data=>
{
this.res=data
});
console.log(this.res)
To
this.getdata().subscribe(data=>
{
this.res=data;
console.log(this.res)
});

Angular method returns undefined

As a beginner, I facing a problem with Angular and Observables. I have API for getting information about one specific restaurant in the database, but I have to get it with a POST request. I successfully get restaurantID from auth.service and another API when the restaurant is logged in, But when I tried to log restaurant in console, I get undefined. Uniformly I don't have permission to show API here. The code:
restaurant.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { Restaurant } from '../models/Restaurant';
import { LoggedRestaurant } from '../models/LoggedRestaurant';
#Injectable({
providedIn: 'root'
})
export class RestaurantService {
private restaurantUrl = 'xxxxxxxxxxxx';
public restaurant: Restaurant;
public loggedRestaurant: LoggedRestaurant
public restaurantID;
constructor(private http: HttpClient) { }
public getRestaurant(): Observable<LoggedRestaurant> {
return this.http.post<LoggedRestaurant>(this.restaurantUrl, this.restaurantID);
}
}
informacije.component.ts
import { Component, OnInit } from '#angular/core';
import { AuthService } from '../services/auth.service';
import { RestaurantService } from '../services/restaurant.service';
import { Restaurant } from '../models/Restaurant';
import { LoggedRestaurant } from '../models/LoggedRestaurant';
import { Observable } from 'rxjs';
#Component({
selector: 'app-informacije',
templateUrl: './informacije.component.html',
styleUrls: ['./informacije.component.scss']
})
export class InformacijeComponent implements OnInit {
restaurant: Restaurant;
loggedRestaurant: LoggedRestaurant;
restaurantID;
constructor(private restaurantService: RestaurantService, private authService: AuthService ) { }
getRestaurant() {
return this.restaurantService.getRestaurant()
}
ngOnInit() {
this.restaurant = this.authService.currRestaurant[0];
console.log(this.restaurant)
console.log(this.loggedRestaurant)
this.restaurantID = this.restaurant.id;
console.log(this.restaurantID)
this.restaurantService.restaurantID =this.restaurantID;
}
}
httpClient.post() returns an observable (RXJS). So you need to subscribe to that. Otherwise, you may use the async pipe.
in your html, you can try this,
<span>{{getRestaurant() | aync}}</span>
OR,
you can declare a variable in your ts like data, and,
this.restaurantService.getRestaurant().subscribe(payload => {
this.data = payload;
})
and in your html, you can add,
<span *ngIf="data">{{data}}</span>
You need to subscribe to your API call.
In informacije.component.ts
getRestaurant() {
return this.restaurantService.getRestaurant()
.subscribe(data => this.restaurant = data);
}
This will asign the value returned by your service to your restaurant field in an asynchronous fashion.
In ngOnInit() call getRestaurant as follows
async ngOnInit() {
let restaurant = await this.getRestaurant().toPromise();
...
}

AngularFire2 return 'undefined' [object Object] with Angular 6

I try to get details from the firebase database but keep getting undefined
here is my code for getting the Object from the data base:
import { AppUser } from './models/app-user';
import { Injectable } from '#angular/core';
import { AngularFireDatabase, AngularFireObject } from 'angularfire2/database';
import * as firebase from 'firebase';
#Injectable({
providedIn: 'root'
})
export class UserService {
constructor(private db: AngularFireDatabase ) { }
get(uid: string): AngularFireObject<AppUser> {
console.log(this.db.object('/users/' + uid));
return this.db.object('/users/' + uid);
}
}
the console log from this get method is: [object Object]
I can't find how to get the username or other information of this user.
Here is my AppUser:
export interface AppUser {
email: string;
isAdmin: boolean;
name: string;
}
I found some answers, but it is related to older version of Angular, and is didn't help my issue.
I also saw some answer related to async pipe, but this is in the HTML, and I need the data to be available in a service.ts file.
I need to get the result in my component (not in the html).
I tried to extract the data of the user like that:
get appUser$(): Observable<AppUser> {
return this.user$
.pipe<AppUser>(map(user => {
if ( user ) {
this.userService.get(user.uid);
}
}));
}
but again the log say I got [object Object]...
In my final method that need to use this information:
canActivate() {
return this.auth.appUser$
.pipe(map(user => {
console.log(user);
return user.isAdmin;
}));
}
The console log give undefined
Use JSON.stringify
Console.log(JSON.stringify(this.db.object('/users/' + uid)));
You are returning a AngularFireDatabase.
You want to subscribe to AngularFireObject. so, you have to call :
get(uid: string): AngularFireObject<any> { // <----
console.log(this.db.object('/users/' + uid));
return this.db.object('/users/' + uid).valueChanges(); // <---
}
and than, you can subscribe and reveal the object in your component.
--Updated--
The current object type is AngularFireObject for single object and AngularFireList for a list of objects.
You still have to call .valueChanges() to get the observable.
I've managed to solve it with help from you, and other blogs. Thanks a lot!
Here is the full solution:
The user service.ts file:
import { AppUser } from './models/app-user';
import { Injectable } from '#angular/core';
import { AngularFireDatabase, AngularFireObject } from 'angularfire2/database';
import * as firebase from 'firebase';
#Injectable({
providedIn: 'root'
})
export class UserService {
constructor(private db: AngularFireDatabase ) { }
get(uid: string): AngularFireObject<AppUser> {
return this.db.object('/users/' + uid);
}
}
Here is the Authentication service.ts file:
import { AppUser } from './models/app-user';
import { pipe } from 'rxjs';
import { CanActivate } from '#angular/router';
import { Injectable } from '#angular/core';
import { UserService } from './user.service';
import { map } from 'rxjs/operators';
import * as firebase from 'firebase';
#Injectable({
providedIn: 'root'
})
export class AdminAuthGuard implements CanActivate {
fireBuser: firebase.User;
constructor(private userService: UserService) { }
canActivate() {
this.fireBuser = firebase.auth().currentUser;
return this.userService.get(this.fireBuser.uid)
.valueChanges()
.pipe(map((appUser: AppUser) => {
console.log(appUser.isAdmin);
return appUser.isAdmin;
}));
}
}
console.log(appUser.isAdmin) - give the correct property saved in the database.

Categories

Resources