How to set default value in RxJs with BehaviorSubject - javascript

I wonder how to set default value in RxJs with BehaviorSubject, so I would like to have default value 15 images. From my interface I would like take only 15 urls, and how to subscibe them. I would be very grateful if someone could explain to me how to make it work.
app component
import { Component, OnInit } from '#angular/core';
import { BehaviorSubject } from 'rxjs';
import { DogInfo } from './interface/dogInfo';
import { HttpService } from './service/http.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
constructor(private httpService: HttpService) { }
items$: any = new BehaviorSubject<DogInfo[]>();
ngOnInit() {
this.items$.subscribe((item: any) => this.httpService.fetchDogsFromApi());
}
}
interface
export interface DogInfo{
id: number;
name?: string;
breadGroup?: string;
lifeSpan?: string;
breadFor?: string;
temperament?: string[];
url: string;
}

You can use like below code:
items$: any = new BehaviorSubject<DogInfo[]>([{id: 0, url: 'your-url'}]);

Probably there is a better way, you could use take and grab the first 15 items from dogs, and set that value to your custom Subject as:
items$ = new BehaviorSubject<DogInfo[]>([]); // initialize as an empty array
private dogsArray: DogInfo[] = [];
constructor(private http: HttpService) {}
ngOnInit(): void {
this.http.fetchDogsFromApi().pipe(
take(15),
tap((dog) => this.dogsArray.push(dog))
).subscribe({
// after the 15 dogs are taken, it will emit that data to the template
complete: () => this.items$.next(this.dogsArray)
});
}
HTML
<ng-container *ngIf="(items$ | async) as dogs">
<div *ngFor="let dog of dogs; let i = index">
<p>Dog #: {{ i }}</p>
<p>Name: {{ dog.name }}</p>
<p>...</p>
</div>
</ng-container>

Related

How to get ID of collection in Firestore with angular Firestore

I'm not able to get the ID of the document when I query a Firestore Database this way :
Could you give me some help ?
import { Component, OnInit } from '#angular/core';
import { AngularFirestore, AngularFirestoreCollection } from '#angular/fire/firestore';
import { Observable } from 'rxjs';
export interface Item { name: string; }
#Component({
selector: 'app-annonces-contactees',
templateUrl: './annonces-contactees.page.html',
styleUrls: ['./annonces-contactees.page.scss'],
})
export class AnnoncesContacteesPage implements OnInit {
private annoncesCollection: AngularFirestoreCollection<Item>;
annonces: Observable<Item[]>;
constructor(private afs: AngularFirestore) {
this.annoncesCollection = afs.collection('annonces', ref => ref.where('prix', '>=', 1000000))
this.annonces = this.annoncesCollection.valueChanges();
}
ngOnInit() {
}
}
I am going to give you an example of how I dot it:
Let us suppose I have collection of hospitals and each hospital has its name,phone and location.
constructor(private firestore:AngularFirestore){}
hospitalsArray=[];
ngOnInit(){
this.firestore.collection("hospitals").snapshotChanges().subscribe((data) => {
this.hospitalsArray = data.map(e => {
return { id: e.payload.doc.id, location: e.payload.doc.data()["location"], number: e.payload.doc.data()["phone"], name: e.payload.doc.data()["name"]}
})
}
"hospitals" is the name of the collection and this id is the id of the document.
So if you want to display in the html file
<ion-item *ngFor="let hospital of hospitalsArray">
<ion-label>{{hospital.name}}</ion-label>
</ion-item>

How to transfer variables from a ts fie to another, angular

I defined a property here in my function
evs: string
...
openArticle(url){
this.evs = url
console.log(this.evs)
this.navCtrl.navigateForward('/url-page')
}
And I a trying to pass the value of 'this.evs' to another ts file and use its value but I do not know how to do this. I tried exporting it like this.
export const webpage = this.evs
but this.evs has no value until someone performs the openArticle function ad so I keep getting the error. "Cannot read property 'evs' of undefined"
What i need to do is tranfer the variable to the 'url-page' page and use the value of this.evs only after the openArticle function has bee called. How do I go about this?
As per my understanding you are trying to share data between two components.
So choose one of them as per your requirements.
Parent to Child: Sharing Data via Input().
Child to Parent: Sharing Data via Output() and EventEmitter.
Unrelated Components: Sharing Data with a Service.
This link will be helpful.
If the components have a parent/child relationship, You can share data between them via #Inpput() and #Output() decorators.
Sharing data from Parent to Child using #Input() :
<h3>Parent Component</h3>
<label>Parent Component</label>c
<input type="number" [(ngModel)]='parentValue'/>
<p>Value of child component is: </p>
<app-child [value]='parentValue'></app-child>
And in the child component, the 'parentValue' can be received as :
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit {
#Input() value: number;
constructor() { }
ngOnInit() {
}
}
Now, in the case of sending data from Child to Parent, we can use an #Output() event emitter. So the parent would have a function to receive the emitted data from child as :
parent-app.component.html
<app-child [value]="parentValue" (childEvent)="childEvent($event)"></app-child>
parent-app.component.ts
childEvent(event) {
console.log(event);
}
And, the child.component.ts would look like :
import { Component, OnInit, Input, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit {
#Input() PData: number;
#Output() childEvent = new EventEmitter();
constructor() { }
onChange(value) {
this.childEvent.emit(value);
}
ngOnInit() {
}
}
If the components do not have a parent/child relationship, a shared service can be used, say, SharedService which has a BehavioralSubject, that emits value from either component, and the other component can then catch the changed value.
Eg:
import { Injectable } from '#angular/core';
import { BehaviorSubject } from "rxjs/BehaviorSubject";
#Injectable()
export class SharedService {
comp1Val: string;
_comp1ValueBS = new BehaviorSubject<string>('');
comp2Val: string;
_comp2ValueBS = new BehaviorSubject<string>('');
constructor() {
this.comp1Val;
this.comp2Val;
this._comp1ValueBS.next(this.comp1Val);
this._comp2ValueBS.next(this.comp2Val);
}
updateComp1Val(val) {
this.comp1Val = val;
this._comp1ValueBS.next(this.comp1Val);
}
updateComp2Val(val) {
this.comp2Val = val;
this._comp2ValueBS.next(this.comp2Val);
}
And component1 as follows :
import { Injectable } from '#angular/core';
import { BehaviorSubject } from "rxjs/BehaviorSubject";
#Injectable()
export class SharedService {
comp1Val: string;
_comp1ValueBS = new BehaviorSubject<string>('');
comp2Val: string;
_comp2ValueBS = new BehaviorSubject<string>('');
constructor() {
this.comp1Val;
this.comp2Val;
this._comp1ValueBS.next(this.comp1Val);
this._comp2ValueBS.next(this.comp2Val);
}
updateComp1Val(val) {
this.comp1Val = val;
this._comp1ValueBS.next(this.comp1Val);
}
updateComp2Val(val) {
this.comp2Val = val;
this._comp2ValueBS.next(this.comp2Val);
}
Component 2 :
import { Component, AfterContentChecked } from '#angular/core';
import { SharedService } from "../../common/shared.service";
#Component({
selector: 'app-component2',
templateUrl: './component2.component.html',
styleUrls: ['./component2.component.css']
})
export class Component2Component implements AfterContentChecked {
comp1Val: string;
comp2Val: string;
constructor(private sharedService: SharedService) {
this.sharedService.comp2Val = "Component 2 initial value";
}
ngAfterContentChecked() {
this.comp1Val = this.sharedService.comp1Val;
}
addValue(str) {
this.sharedService.updateComp2Val(str);
}
}
You can find more on different types of subjects here

Processing a two-dimensional array in Angular 7. ngFor

How to handle a two-dimensional array using ngFor?
I receive here such array
As a result, I need to get the blocks in which the data from the array is displayed in order. That is, in the case of an array that is represented on the screen, there would be 10 blocks.
Example:
<div>
<span>Yandex</span>
<span>Yandex.N.V....</span>
<span>https://en.wikipedia.org/wiki/Yandex</span>
</div>
<div>
<span>Yandex Browser</span>
<span>IPA:...</span>
<span>https://en.wikipedia.org/wiki/Yandex_Browser</span>
</div>
etc.
I do it that way.
<h3>Get Articles</h3>
<div>
<div *ngIf="articles">
<div *ngFor="let article of articles">
<span>{{ article[1] }}</span>
<span>{{ article[2] }}</span>
<span>{{ article[3] }}</span>
</div>
</div>
</div>
I understand that this is wrong, but I can not find my stupid mistake.
The output is either an error or a strange conclusion.
search.component.ts
import { Component, OnInit } from '#angular/core';
import { Article, ArticlesService } from '../../services/articles.service';
#Component({
selector: 'app-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.css'],
providers: [ArticlesService]
})
export class SearchComponent implements OnInit {
constructor(private articlesServices: ArticlesService) { }
searchQuery: string;
limit: number;
error: any;
articles: { };
// noinspection JSMethodCanBeStatic
getUrl(searchQuery: string) {
return 'https://en.wikipedia.org/w/api.php?action=opensearch&search='
+ searchQuery + '&limit=10&namespace=0&format=json&origin=*';
}
showArticles() {
this.articlesServices.getArticles(this.getUrl(this.searchQuery))
.subscribe(
(data: Article) => this.articles = Object.values({
title: data[0],
collection: data[1],
description: data[2],
links: data[3]
}),
error => this.error = error
);
console.log(this.articles);
}
ngOnInit() {
}
}
article.component.ts
import { Component, OnInit, Input } from '#angular/core';
import {Article, ArticleInfo, ArticlesService} from '../../services/articles.service';
#Component({
selector: 'app-articles',
templateUrl: './articles.component.html',
styleUrls: ['./articles.component.css'],
})
export class ArticlesComponent implements OnInit {
#Input() articles: Article;
#Input() searchQuery: string;
constructor(private articlesServices: ArticlesService) { }
information: ArticleInfo;
getUrl(searchQuery: string) {
return 'https://ru.wikipedia.org/w/api.php?action=query&list=search&srsearch=' +
searchQuery + '&utf8=&format=json&origin=*';
}
showArticlesInformation() {
this.articlesServices.getArticlesInfo(this.getUrl(this.searchQuery))
.subscribe(
(data: ArticleInfo) => this.information = {
query: data.query.search
}
);
console.log(this.information);
}
ngOnInit() {
}
}
article.service.ts
import { Injectable } from '#angular/core';
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
import { throwError } from 'rxjs';
import { retry, catchError } from 'rxjs/operators';
export interface Article {
title: string;
collection: string[];
description: string[];
links: string[];
}
export interface ArticleInfo {
query: {
search
};
}
#Injectable({
providedIn: 'root'
})
export class ArticlesService {
constructor(private http: HttpClient) { }
getArticles(url) {
return this.http.get(url)
.pipe(
retry(3),
catchError(this.handleError)
);
}
getArticlesInfo(url) {
return this.http.get<ArticleInfo>(url);
}
// noinspection JSMethodCanBeStatic
private handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
console.error('An error occurred:', error.error.message);
} else {
console.error(
`Backend returned code ${error.status}, ` +
`body was: ${error.error}`);
}
return throwError(
'Something bad happened; please try again later.');
}
}
Come 2D array
Then it should turn out like this
Try this,
<div>
{{articles[0]}}
</div>
<div *ngFor="let article of articles[1]; let i=index">
<span>
{{article}}
</span>
<span *ngFor="let info1 of articles[2]; let j=index" [hidden]="i!=j">
{{info1}}
</span>
<span *ngFor="let info2 of articles[3]; let k=index" [hidden]="i!=k">
{{info2}}
</span>
</div>
Try storing the result into Observable and into the html file use async pipe.
<div *ngFor="let article of articles | async">
In your search.component.ts
articles : Observable<Article>;
...
this.articles = this.articlesServices.getArticles(this.getUrl(this.searchQuery)).catch(error => this.error = error );

Angular4: cannot read the property of undefined

I am trying to learn angular4 with the tutorial they provided in website
Here is the code
hero.ts
export class Hero{
constructor(
public id: number,public name: string
){}
}
in component.ts
import { Component } from '#angular/core';
import {Hero } from './hero';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title : string;
hero : string;
selectedHero: Hero;
heroes = [
new Hero(1, 'Windstorm'),
new Hero(13, 'Bombasto'),
new Hero(15, 'Magneta'),
new Hero(20, 'Tornado')
]
myHero = this.heroes[0];
constructor(){
this.title = 'Tour of heros';
}
onSelect(hero: Hero): void {
this.selectedHero =hero;
}
}
html
<ul>
<li *ngFor="let hero of heroes" (click)="onSelect(hero)">
{{ hero.name }}
</li>
</ul>
<p>{{selectedHero.name}}</p>
when click on each li i would like to display details in selected object but i got the following error
selectedHero.name is undefined
Check in the template if selectedHero is set before access any of its property
<p *ngIf="selectedHero">{{selectedHero.name}}</p>
or create an empty instance in the component (updated answer)
selectedHero: Hero = new Hero(12, 'somename');

Can I get a variable into ngFor using async pipe?

There my (normalized) state:
export class State {
chatIDs: string[];
chats: { [chatID:string]: Chat };
}
Trying to loop through chats (state as Observable) in some component template:
<div *ngFor="let chatID of (state$ | async).chatIDs;
let currentChat = (state$ | async).chats[chatID]">
<!---->
</div>
The string let currentChat = (state$ | async).chats[chatID] throws error:
unexpected token (, expected identifier, keyword, or string
How can I get a reference to the current chat into the loop? It's possible, for e.g., as child component with the input (state$ | async).chats[chatID]. But is there way more elegant (without creating any new components)?
Angular v2.4
You can use the *ngIf directive to first check then you use *ngFor to iterate the chats.
import { Component, OnInit } from '#angular/core';
import { Observable } from "rxjs/Observable";
import { Subscriber } from "rxjs/Subscriber";
export class Chat {
message: string
constructor(message: string) {
this.message = message;
}
}
export class State {
chatIDs: string[];
chats: {
[chatID: string]: Chat
};
}
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
state: any;
ngOnInit(): void {
this.state = new Observable<State>((observer: Subscriber<State>) => {
setInterval(() => {
let state = new State();
state.chatIDs = ['1', '2', '3'];
state.chats = {
'1': new Chat('Chat 1'),
'2': new Chat('Chat 2'),
'3': new Chat('Chate 3')
}
observer.next(state)
}, 1000);
});
}
}
Put variable s in scope using *ngIf:
<div *ngIf="state | async; let s">
<div *ngFor="let id of s?.chatIDs">
{{ s?.chats[id] | json }}
</div>
</div>

Categories