Subscribers to BehavorSubject not deteciting chanfes - html

I'm working with localforage service who works asynchronous. I have created my own service to manage all about localforage dat. This service is added to appmodule to be global.
The problema that I have is that even I've been subscribed to er,ew,eu,ed BehavorSubject vars, the subscriptors to this vars not detecting changes or there are not notified for changes. This vars are privileges of the user in app (boolean values) in order to show/hide some thins according to this privileges
My app.module.ts:
providers: [appRoutingProviders, AuthGuard, BnNgIdleService, IndexedDBService, {
provide: HTTP_INTERCEPTORS,
useClass: TokenInterceptorService,
multi: true
}],
My IndexedDBService:
import { AfterViewInit, Injectable, OnInit } from "#angular/core";
import { LocalForageService } from "ngx-localforage";
import { BehaviorSubject, Observable, ReplaySubject, Subject } from "rxjs";
//#Injectable({ providedIn: 'root' })
export class IndexedDBService implements OnInit{
private isLoading: BehaviorSubject<boolean>
private er: BehaviorSubject<boolean> = new BehaviorSubject(false);
private ew: BehaviorSubject<boolean> = new BehaviorSubject(false);
private eu: BehaviorSubject<boolean> = new BehaviorSubject(false);
private ed: BehaviorSubject<boolean> = new BehaviorSubject(false);
constructor(
private localforage: LocalForageService
){
this.isLoading = new BehaviorSubject(false);
}
ngOnInit(): void {
this.getUsrPrivileges();
}
public showSpinner(){
this.isLoading.next(true);
}
public hideSpinner(){
this.isLoading.next(false);
}
public isPageLoading(){
return this.isLoading;
}
public setToken(token: string){
this.localforage.setItem('token',token);
}
public getToken(){
return this.localforage.getItem('token')
}
public clearLocalForage(){
return this.localforage.clear().toPromise();
}
public getER(): Observable<boolean>{
return this.er.asObservable();
}
public setER(er: boolean){
this.er.next(er);
this.localforage.setItem('er',er);
}
public setEW(ew: boolean){
this.ew.next(ew);
this.localforage.setItem('ew',ew);
}
public getEW(): Observable<boolean>{
return this.ew.asObservable();
}
public setEU(eu: boolean){
this.eu.next(eu);
this.localforage.setItem('eu',eu);
}
public getEU(): Observable<boolean>{
return this.eu.asObservable();
}
public setED(ed: boolean){
this.ed.next(ed);
this.localforage.setItem('ed',ed);
}
public getED(): Observable<boolean>{
return this.ed.asObservable();
}
getUsrPrivileges(){
this.localforage.getItem('er').subscribe(value => this.er.next(value));
this.localforage.getItem('ew').subscribe(value => this.ew.next(value));
this.localforage.getItem('eu').subscribe(value => this.eu.next(value));
this.localforage.getItem('ed').subscribe(value => this.ed.next(value));
}
}
In my LoginAppComponent I get and fill the user privileges:
...
constructor(
...
private _indexedDBService: IndexedDBService,
...
) {
}
setUsrPrivileges(privilegios: string){
let er = privilegios.substring(0,1) === '1';
let ew = privilegios.substring(1,2) === '1';
let eu = privilegios.substring(2,3) === '1';
let ed = privilegios.substring(3,4) === '1';
this._indexedDBService.setER(er);
this._indexedDBService.setEW(ew);
this._indexedDBService.setEU(eu);
this._indexedDBService.setED(ed);
}
...
In my app menu I have subscribers to this er,ew,eu,ed values of IndexedDBService who have to be notified for any change. But they always keep with false value.
My AppMenuComponent
import { AfterViewInit, Component, OnDestroy, OnInit } from '#angular/core';
import { Subject } from 'rxjs-compat';
import { IndexedDBService } from 'src/app/services/indexeddb.service';
#Component({
selector: 'app-appmenu',
templateUrl: './appmenu.component.html',
styleUrls: ['./appmenu.component.css']
})
export class AppMenuComponent implements OnInit, OnDestroy {
unsubscribe: Subject<boolean> = new Subject();
public er: boolean;
public ew: boolean;
public eu: boolean;
public ed: boolean;
constructor(
private _indexedDBService: IndexedDBService
) {
this.er = false;
this.ew = false;
this.eu = false;
this.ed = false;
}
ngOnDestroy(): void {
this.unsubscribe.next(true);
this.unsubscribe.complete();
}
ngOnInit(): void {
this.getUsrPrivileges();
}
getUsrPrivileges(){
this._indexedDBService.getER().subscribe(value => this.er = value);
this._indexedDBService.getEW().subscribe(value => this.ew = value);
this._indexedDBService.getEU().subscribe(value => this.eu = value);
this._indexedDBService.getED().subscribe(value => this.ed = value);
}
}
In my AppMenu html I show/hide the item depending of the er value ([hidden]="!er"):
...
<!-- Gestionar Alumnos -->
<ul class="nav nav-treeview" [hidden]="!er">
<li class="nav-item">
<a [routerLink]="['/alumnos/gestionalumno']" class="nav-link">
<i class="fas fa-user-graduate"></i>
<p style="padding-left: 7px;">Alumnos</p>
</a>
</li>
</ul>
<!-- Fin Gestionar Alumnos -->
<!-- Gestionar Tutores -->
<ul class="nav nav-treeview" [hidden]="!er">
<li class="nav-item">
<a [routerLink]="['/']" class="nav-link">
<i class="fas fa-user-tie"></i>
<p style="padding-left: 7px;">Tutores</p>
</a>
</li>
</ul>
<!-- Fin Gestionar Tutores -->
<!-- Gestionar Profesores -->
<ul class="nav nav-treeview" [hidden]="!er">
<li class="nav-item">
<a [routerLink]="['/']" class="nav-link">
<i class="fas fa-chalkboard-teacher"></i>
<p style="padding-left: 7px;">Profesores</p>
</a>
</li>
</ul>
<!-- Fin Gestionar Profesores -->
...
When I make the login, I fill the privileges who are value of true, but the Alumno, Tutor and Profesor always keep hidden, because they not detecting that the value changed to true;
In my indexeddb I see that the value of these vars are true.
I suspect the the BehavorSubject vars are emitting there values befor subscribers are subscribed. It's possible? If is the case ... what can I do to sole it?

Finally I found the problem!
The problem it was occurring because I was working with different instances of IndxedDBService.
All the components of the app was working with IndexedDBService who is provided in appModule, but accidentally I added this provider in my login component. So, for this reason, all component was working for one hand and the login component with another hand.
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css'],
providers: [AuthenticationService,IndexedDBService]
})
export class LoginComponent implements OnInit {
So, removing IndexedDBService of login component providers. The problem was solved
providers: [AuthenticationService]

Related

Subscribe a local variable Value to an Observable of variable in another Component in Angular

I want to change an HTML view via *ngIf, based on a local variable, which should change based on a variable delivered through an observable from a shared service.
HTML
<div class="login-container" *ngIf="!isAuthenticated">
TypeScript of same component:
export class LoginComponent implements OnInit {
authenticationsSubscription;
isAuthenticated: boolean;
constructor(
private authService: AuthServiceService,
private router: Router,
private route: ActivatedRoute){}
getAuth(): Observable<boolean>{
return this.authService.validation();
}
ngOnInit() {
this.authenticationsSubscription = this.authService.validation().subscribe(auth => this.isAuthenticated = auth);
}
}
TypeScript of shared service AuthService:
export class AuthServiceService {
isAuthenticated: boolean;
validation(): Observable<boolean>{
return of(this.isAuthenticated);
}
}
While debugging I found out, the variable isAuthenticated in the LoginComponent does not change, on changes of the variable isAuthenticated of the AuthService. I also tried using pipe() and tap(), which did not change anything.
What am I doing wrong?
Convert your AuthServiceService to have the authentication state as a BehaviorSubject and return it as Observable as described below.
import { Observable, BehaviorSubject } from "rxjs";
export class AuthServiceService {
private isAuthenticatedSub: BehaviorSubject<boolean> = new BehaviorSubject(false);
set isAuthenticated(isAuthenticated: boolean) {
this.isAuthenticatedSub.next(isAuthenticated);
}
get isAuthenticated(): boolean {
return this.isAuthenticatedSub.value;
}
validation(): Observable<boolean> {
return this.isAuthenticatedSub.asObservable();
}
}
The actual subscription of your observable will only happens once, when the OnInit lifecycle hook is triggered when the component is initialized.
You can subscribe to a BehaviorSubject in order to catch value changes.
Stackblitz example
AuthService
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs';
#Injectable()
export class AuthService {
isAuthenticated: BehaviorSubject<boolean>;
constructor() {
this.isAuthenticated = new BehaviorSubject<boolean>(false);
}
}
Component
import { Component, OnInit } from '#angular/core';
import { AuthService } from './auth.service';
import { Observable } from 'rxjs';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
isAuthenticated: Observable<boolean>;
constructor(private authService: AuthService) {}
ngOnInit() {
this.isAuthenticated = this.authService.isAuthenticated;
}
login() {
this.authService.isAuthenticated.next(true);
}
logout() {
this.authService.isAuthenticated.next(false);
}
}
Template
<div *ngIf="isAuthenticated | async; else notAuthenticated">
User is authenticated
</div>
<ng-template #notAuthenticated>
<div>User isn't authenticated</div>
</ng-template>
<button (click)="login()">Login</button>
<button (click)="logout()">Logout</button>

Angular: cannot receive value from other component

I try to create reusable component for input form. The page will call this tag and get data to form group.
CALL PAGE:
I want to call tag and get value into "test" which I create in FormGroup
<div class="row">
<proj-form-row-input [lable]="'Input 1'" [required]="true" formControlName="test"></proj-form-row-input>
</div>
HTML: I create form in this component
<div class="col-sm-6 col-xs-12">
<form [formGroup]="setupForm" class="form-horizontal">
<div class="form-group">
<label class="col-sm-5">
{{lable}}
<span *ngIf="required" class="required">*</span>
</label>
<div class="col-sm-7">
<input type="text" class="form-control" formControlName="data" />
</div>
</div>
</form>
</div>
TS: for sent data back
import { Component, OnInit, Input, forwardRef, Output, EventEmitter } from '#angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor, FormGroup, FormBuilder } from '#angular/forms';
import { ValidateHelper } from '../../helper/validate-helper';
#Component({
selector: 'proj-form-row-input',
templateUrl: './form-row-input.component.html',
styleUrls: ['./form-row-input.component.css'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => FormRowInputComponent),
multi: true
}
]
})
export class FormRowInputComponent implements OnInit, ControlValueAccessor {
#Input() public lable: string;
#Input() public required: boolean = false;
#Output() public data = new EventEmitter();
public setupForm: FormGroup;
public inputData: string;
constructor(
private fb: FormBuilder,
) { }
ngOnInit() {
this.initsetupForm();
}
writeValue(obj: string): void {
if (ValidateHelper.isNotEqualString(obj, this.inputData)) {
this.setInputData();
} else {
this.data.emit(obj);
}
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
}
private initsetupForm(): void {
this.setupForm = this.fb.group({
data: [''],
});
this.setupForm.controls['data'].valueChanges.subscribe((val) => {
this.writeValue(val);
});
}
private setInputData() {
this.inputData = this.setupForm.controls['data'].value;
this.data.emit(this.inputData);
}
private onChange(_: any) { }
private onTouched() { }
}
FormControlName "test" still got no value, how an I out the value that emitted back into "test"
Currently, I do not see that you are subscribing to the data: EventEmitter anywhere?
If you declare an EventEmitter to transfer data between two components, the other component will need to subscribe() to the event to receive the data.
In your other components contructor, if you have an instance of your FormRowInputComponent component, you subscribe as follow:
formRowInputInstance.data.subscribe(response => { this.someProperty = response });
Here is four different approaches of passing values between components

HTML doesn't display when navigating to another view

I have an issue where i'm displaying a webview on one view and then once the user logs in i'm navigating to another view but the html for that view won't display, only the action bar. Help appreciated!
This is the login component navigating to secure--
this.router.navigate(["/secure"], { queryParams: { accessToken: accessToken } }).then((success)=>{
console.log(success);
});
}
This is the secure component-
#Component({
moduleId: module.id,
selector: "ns-secure",
templateUrl: "secure.component.html",
})
export class SecureComponent implements AfterViewInit{
public accessToken: string;
public onceLoggedInSrc: string; //TODO
public htmlString: string;
public constructor(private _router:Router, private _activatedRoute: ActivatedRoute, private cdRef:ChangeDetectorRef) {
this.htmlString = '<span><h1>HtmlView demo in <font color="blue">NativeScript</font> App</h1></span>';
this._activatedRoute.queryParams.subscribe(params => {
this.accessToken = params["accessToken"];
console.log('accessToken2');
console.log(this.accessToken);
this.ngAfterViewInit();
});
}
ngAfterViewInit() {
console.log('accessToken');
}

How can I solve the translation of promises?

I'm newbie angular2. I'm making a website to start playing this platform. The problem is that when I query the API news can not transform objects correctly. Although I think there is a problem with the promises.
The code component is as follows:
import { Component } from '#angular/core';
import { Router } from '#angular/router';
import { AuthenticationService } from '../Service/Authentication.service';
import { NoticiesService } from '../Service/Noticies.service';
import { Logger } from '../Service/Logger.service';
import { Noticia } from '../Model/Noticia';
#Component({
selector: 'my-home',
templateUrl: 'app/View/html/Home.component.html',
styleUrls: ['app/Content/css/app.component.css'],
})
export class HomeComponent {
llistaNoticies: Noticia[];
missNoticies = 'Noticies Actuals:';
authenticatedUser = localStorage.getItem('usuari');
sessionUp = this._serviceAuthentication.isSession;
constructor(
private _logger: Logger,
private _serviceAuthentication: AuthenticationService,
private _noticiesService: NoticiesService) { }
getNoticies(): void {
this._noticiesService.getNoticies()
.then(noticies => this.llistaNoticies = noticies);
console.log('getNoticies() passat!')
}
ngOnInit(): void {
this.getNoticies()
console.log('ngOnInit() passat!')
}
logOut() {
this._serviceAuthentication.logOut();
}
}
The Code of Service is:
import { Injectable } from '#angular/core';
import { Router } from '#angular/router';
import { Http, Response,
Headers, RequestOptions } from '#angular/http';
import 'rxjs/add/operator/toPromise';
import { Noticia } from '../Model/Noticia';
#Injectable()
export class NoticiesService {
private URLNoticies = 'http://localhost:50541/api/Noticies';
constructor(
private _router: Router,
private _http: Http) { }
getNoticies(): Promise<Noticia[]> {
return this._http.get(this.URLNoticies)
.toPromise()
.then(response => response.json().data as Noticia[])
.catch(this.handleError);
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error);
return Promise.reject(error.message || error);
}
}
The component of HTML code:
<div class="block-general m-t-1 subTitolPag">
<h2 class="titolPag">Notícies d'actualitat:</h2>
</div>
<div class="row p-a-1">
<div id="block-central" class="block-sub col-md-6 offset-md-2">
<div *ngFor="let noticia of llistaNoticies">
<hr />
<div class="card">
<img class="card-img-top imgNoticia" src="{{noticia.PathImatge}}" alt="Card image cap" style="max-width: 100%;">
<div class="card-block">
<h4 class="card-title titolNoticia">{{noticia.Titol}}</h4>
<p class="card-text">{{noticia.Cos}}</p>
<div class="clearfix">
<h5 class="pull-xs-right">Autor: <span class="label label-default pull-xs-right">{{noticia.Autor}}</span></h5>
</div>
<div>
<h6 class="pull-xs-right">Data de: {{noticia.Data}}</h6>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3 block-sub m-x-1 p-a-1">
<p>Usuari registrat: <span class="text-uppercase">{{authenticatedUser}}</span></p>
<div class="clearfix m-t-1">
<button type="submit" class="btn btn-primary pull-xs-right" (click)="logOut()">Desconecta</button>
</div>
</div>
</div>
And the class:
export class Noticia {
public Id: number;
public Titol: string;
public Cos: string;
public Autor: string;
public PathImage: string;
public Data: Date;
}
If someone finds where is the error and tell me where I can find clear promises would be very grateful.
Thank you!!
I recommend to use Observables.
#Injectable()
export class NoticiesService {
private URLNoticies = 'http://localhost:50541/api/Noticies';
constructor(
private _router: Router,
private _http: Http) { }
getNoticies(): Observable<Noticia[]> {
return this._http.get(this.URLNoticies)
.map(response => response.json().data as Noticia[])
.catch(this.handleError);
}
private handleError(error: any): Observable<any> {
console.error('An error occurred', error);
return Observable.of(error.message || error);
}
}
And consume your service:
getNoticies(): void {
this._noticiesService.getNoticies()
.subscribe(
noticies => this.llistaNoticies = noticies,
err => console.log(err)
);
console.log('getNoticies() passat!')
}

Getting data from Web API in Angular 2

Thanks to tutorial on Angular 2 page called "Tour of Heroes", I managed to create a simple Angular 2 application. Then using Enitity Framework I decided to create a database. And fill the list of heroes from it (not from the file). I created Web Api Controller and added simple get method.
Then in hero.service.ts I call this method in order to get list of heroes. When I lunch my app it shows the list of heroes but without any values (name and id are blank). When I debug my application in the browser I can see this.heroes object in heroes.component.ts contains right data. So what is going on? Why aren't name and id showing?
hero.service.ts:
import {Injectable} from 'angular2/core';
import {HEROES} from './mock-heroes';
import {Hero} from './hero';
import {Http, Response} from 'angular2/http';
import 'rxjs/Rx';
import {Observable} from 'rxjs/Observable';
#Injectable()
export class HeroService {
public values: any;
constructor(public _http: Http) { }
private _heroesUrl = 'http://localhost:61553/api/values'; // URL to web api
getHeroes() {
return this._http.get(this._heroesUrl)
.map(res => <Hero[]>res.json())
.catch(this.handleError);
}
private handleError(error: Response) {
console.error(error);
return Observable.throw(error.json().error || 'Server error');
}
}
heroes.component.ts:
import {Component, OnInit} from 'angular2/core';
import {Router} from 'angular2/router';
import {Hero} from './hero';
import {HeroDetailComponent} from './hero-detail.component';
import {HeroService} from './hero.service';
#Component({
selector: 'my-heroes',
templateUrl: 'templates/heroes.component.html',
styleUrls: ['styles/heroes-component.css'],
directives: [HeroDetailComponent]
})
export class HeroesComponent implements OnInit {
constructor(private _heroservice: HeroService, private _router: Router) { }
errorMessage: string;
public heroes: Hero[];
selectedHero: Hero;
ngOnInit() {
this.getHeroes();
}
onSelect(hero: Hero)
{
this.selectedHero = hero;
}
getHeroes() {
this._heroservice.getHeroes()
.subscribe(
value => this.heroes = value,
error => this.errorMessage = <any>error);
}
gotoDetail() {
this._router.navigate(['HeroDetail', { id: this.selectedHero.Id }]);
}
}
heroes.component.html:
<h2>My Heroes</h2>
<ul class="heroes">
<li *ngFor="#hero of heroes" [class.selected]="hero === selectedHero" (click)="onSelect(hero)">
<span class="badge">{{hero.Id}}</span> {{hero.Name}}
</li>
</ul>
<div *ngIf="selectedHero">
<h2>
{{selectedHero.Name | uppercase}} is my hero
</h2>
<button (click)="gotoDetail()">View Details</button>
</div>
hero.ts:
export class Hero {
Id: number;
Name: string;
}
Web API Controller:
using Microsoft.AspNet.Mvc;
using System.Collections.Generic;
using TestApplicationDataAccess;
using TestApplicationDataAccess.Entities;
namespace WebApplication2.Controllers
{
[Route("api/[controller]")]
public class ValuesController : Controller
{
private readonly TestAppDbContext _dbContext;
public ValuesController(TestAppDbContext dbContext)
{
_dbContext = dbContext;
}
// GET: api/values
[HttpGet]
public IEnumerable<Hero> Get()
{
return _dbContext.Heroes;
}
}
}
Hero Entity:
namespace TestApplicationDataAccess.Entities
{
public class Hero
{
public int Id { get; set; }
public string Name { get; set; }
}
}
JSON retrieved from WEB API Controller:
[{"Id":1,"Name":"Superman"}]
getHeroes() {
this._heroservice.getHeroes()
.subscribe(res=>{
this.heroes=res;
console.log(this.heroes); // make sure you get data here.
},
(err)=>console.log(err),
()=>console.log("Done")
);
}
Try this :public heroes: Hero[] = [];
In my case the issue was related to the visual studio 2015 bug. There was nothing wrong with the code itself. Sometimes changes made in vs were not refreshed in the browser. Updating vs to the latest version helped.