Angular Behavior Subject multiple arguments - javascript

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$"

Related

Why is my Angular Service not assigning a filtered result to my property?

I setup a service called 'bankService' which is being used by my 'user' component. The 'user' component is receiving the data from the service correctly but I am unable to assign a filtered result to my 'currentAccount' property. I am filtering by 'id' from the list of 'accounts' that is being returned from my service. Any help with an explanation would be appreciated!
Model
export interface Account {
id: number;
accountHolder: string;
checking: number;
savings: number;
}
Service
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { elementAt, Observable } from 'rxjs';
import { Account } from '../models/Account';
import { Transaction } from '../models/Transaction';
#Injectable({
providedIn: 'root',
})
export class BankServiceService {
private apiUrl = 'http://localhost:5000/accounts';
constructor(private http: HttpClient) {}
getAccounts(): Observable<Account[]> {
return this.http.get<Account[]>(this.apiUrl);
}
}
Component
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { Account } from 'src/app/models/Account';
import { BankServiceService } from 'src/app/services/bank-service.service';
#Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.css'],
})
export class UserComponent implements OnInit {
currentAccount: Account[] = [];
accountId: number;
accountHolder: string;
checkingAmount: number;
savingsAmount: number;
constructor(
private route: ActivatedRoute,
private bankService: BankServiceService
) {}
ngOnInit(): void {
// gets the parameter for the url (the param is the account id)
this.accountId = this.route.snapshot.params['id'];
console.log('id: ', this.accountId);
// pulls in all accounts
this.bankService
.getAccounts()
.subscribe(
(accounts) =>
(this.currentAccount = accounts.filter(
(account) => account.id === this.accountId
))
);
console.log('current account: ', this.currentAccount);
}
}
If I'm not mistaking, your issue is this one:
the account id received from the backend is a number
the account id pulled from the url is a string
In the filter function you are using strict equality, and that's why no account id passes the condition in the filter callback
You can switch from strict equality to loose equality (==) or do something like this for the filter callback:
(account) => account.id.toString() === this.accountId

Property '' does not exist on type 'Object'

I was working all day in angular. Everything worked fine all day.
Restarted the server (ng serve). And now suddenly there are alot of errors.
I managed to fix most but I am stuck with this one.
This is the main part of the component .ts file:
import { Component, OnInit } from '#angular/core';
import { HttpService } from '../http.service';
#Component({
selector: 'app-playboard',
templateUrl: './playboard.component.html',
styleUrls: ['./playboard.component.scss']
})
export class PlayboardComponent implements OnInit {
brews: Object;
constructor(private _http: HttpService) { }
ngOnInit() {
this._http.myMethod().subscribe(data => {
this.brews = data;
this.dices = this.brews.myBox;
this.diceSeed = this.brews.boxID;
console.log(this.brews);
});
}
And this is the http.service.ts file:
import { Injectable } from '#angular/core';
import {HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class HttpService {
constructor(private http: HttpClient) { }
myMethod() {
return this.http.get<Object>('https://localhost:44398/api/boggle');
}
myWordMethod(word) {
var url = 'https://localhost:44398/api/wordpoints/' + word;
return this.http.get<Object>(url);
}
}
It was working all day and suddenly these strange errors appear.
Does anyone have a clue of what could be wrong? Thanks alot!
Remove Object from your http calls. Using the generic is optional, especially if you haven't typed out your response.
import { Injectable } from '#angular/core';
import {HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class HttpService {
constructor(private http: HttpClient) { }
myMethod() {
return this.http.get('https://localhost:44398/api/boggle');
}
myWordMethod(word) {
var url = 'https://localhost:44398/api/wordpoints/' + word;
return this.http.get(url);
}
}
On brews, declare it as any:
import { Component, OnInit } from '#angular/core';
import { HttpService } from '../http.service';
#Component({
selector: 'app-playboard',
templateUrl: './playboard.component.html',
styleUrls: ['./playboard.component.scss']
})
export class PlayboardComponent implements OnInit {
brews: any;
constructor(private _http: HttpService) { }
ngOnInit() {
this._http.myMethod().subscribe(data => {
this.brews = data;
this.dices = this.brews.myBox;
this.diceSeed = this.brews.boxID;
console.log(this.brews);
});
}
You can ignore these by simply delcaring them as the any type. For instance;
myWordMethod(word: any) {
..
}
this._http.myMethod().subscribe(data: any => {
..
});
That said declaring the actual type for TypeScript is often preferred. For Instance if your API sends back a common object with particular properties then declare it as such;
interface MyMethodResponse {
someProperty: string;
someNumber: number;
someArray: string[];
}
this._http.myMethod().subscribe((myMethodResponse: MyMethodResponse) => {
// TypeScript now knows that these properties exists on the response object
console.log(myMethodResponse.someArray);
console.log(myMethodResponse.someNumber);
console.log(myMethodResponse.someProperty);
});

How to call custom service inside another services in angular 6

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();
}

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