Angular 2 - Update view when variable changes - html

I have a button on my nav bar (app.component.html) that I want to only show when the user is logged in.
This is my current approach that does not work for obvious reasons explained later. I want to find out how I can modify it to work.
Inside my app.component.html, I have the following button
<button *ngIf="isCurrentUserExist">MyButton</button>
Inside my app.component.ts, I am trying to bound the variable isCurrentUserExist to a function that returns true if the user exists.
I believe this is the problem because this code is only executed once at OnInit as oppose to somehow keeping the view updated
ngOnInit() {
this.isCurrentUserExist = this.userService.isCurrentUserExist();
}
For reference, inside my UserService.ts
export class UserService {
private currentUser: User
constructor(private http: Http,private angularFire: AngularFire) { }
getCurrentUser(): User {
return this.currentUser
}
setCurrentUser(user: User) {
this.currentUser = user;
}
isCurrentUserExist(): boolean {
if (this.currentUser) {
return true
}
return false
}
}
A bit more information about my app...
Upon start up when the user does not exist, I have a login screen (login component).
When the user logs in, it goes to firebase and grab the user information (async) and store it to my user service via
setCurrentUser(user: User)
So at this point, I like to update the button in my nav bar (which exists in app.component.html) and show the button.
What can I do to achieve this?

let's try this:
using BehaviorSubject
UserService.ts
import { Subject, BehaviorSubject} from 'rxjs';
export class UserService {
private currentUser: User;
public loggedIn: Subject = new BehaviorSubject<boolean>(false);
constructor(private http: Http,private angularFire: AngularFire) { }
getCurrentUser(): User {
return this.currentUser
}
setCurrentUser(user: User) { // this method must call when async process - grab firebase info - finished
this.currentUser = user;
this.loggedIn.next(true);
}
isCurrentUserExist(): boolean {
if (this.currentUser) {
return true
}
return false
}
}
app.component.ts
ngOnInit() {
this.userService.loggedIn.subscribe(response => this.isCurrentUserExist = response);
}

in app.component.ts you are assigned value from function once. So it will never change. To resolve this problem and to real time update use assign function instance of boolean variable this.isCurrentUserExist = this.userService.isCurrentUserExist;. And in view change change *ngIf expression as function isCurrentUserExist().

Related

Clear RouteReuseStrategy on certain page

I implement RouteReuseStrategy on my project, then I specifically stated certain component can be reuse. Now my question is when I am in certain page, I wish to clear all the snapshot and reset all the reuse component. What is the best approach I can do?
Sample flow of my page
page A --> page B (cache for reuse) --> page C
page C --> page A (I wish to clear cache here)
I know we can implement a function to clear handlers in this CustomReuse class but whenever I call the function on other component I will hit NullInjectorError: StaticInjectorError(AppModule)
Below are my RouteReuseStrategy code
export class CustomRouteReuseStrategy implements RouteReuseStrategy {
private handlers = new Map<string, DetachedRouteHandle>();
constructor() {
}
shouldDetach(route: ActivatedRouteSnapshot): boolean {
return true;
}
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
this.handlers[route.url.join("/") || route.parent.url.join("/")] = handle;
}
shouldAttach(route: ActivatedRouteSnapshot): boolean {
return !!this.handlers[route.url.join("/") || route.parent.url.join("/")];
}
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
return this.handlers[route.url.join("/") || route.parent.url.join("/")];
}
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
return future.routeConfig === curr.routeConfig;
}

How to implement delete function in Angular

I'm in Angular group project and I'm trying to implement delete functions for current workspace but I'm not sure how to do that. Any suggestion or help on how to do that?
Inside add-workspace.HTML file, I have a button that display a dialog box (delete-workspace-dialog).
Inside delete-workspace-dialog components file and there is a delete button. I'm trying use that button to delete current workspace.
Inside delete-workspace.dialog.ts file aka delete dialog box
export class DeleteWorkspaceDialogComponent implements OnInit {
constructor(
public dialogRef: MatDialogRef<DeleteWorkspaceDialogComponent>,
#Inject(MAT_DIALOG_DATA) public data: any) { }
ngOnInit(): void {
}
onNoClick(): void {
this.dialogRef.close();
}
onDeleteClick(): void{
// Delete workspace here
}
}
I'm trying to use that "Yes,Delete it" button to delete the current workspace
Hope the below snippet might help:
In Delete pop up component:
import { WorkspaceService } from 'src/app/core/services/workspace.service';
export class DeleteWorkspaceDialogComponent implements OnInit {
constructor(
public dialogRef: MatDialogRef<DeleteWorkspaceDialogComponent>,
#Inject(MAT_DIALOG_DATA) public data: any,
private workspaceService: WorkspaceService) { }
ngOnInit(): void {
}
onNoClick(): void {
this.dialogRef.close();
}
onDeleteClick(): void{
// Delete workspace here
this.workspaceService.deleteWorkspace(data.workspace).subscribe(response=>{
// Do some logic and close the popup
this.dialogRef.close();
},error=>{
// Error handling and close the popup
this.dialogRef.close();
})
}
}
From parent component to open delete popup:
HTML:
<button (click)="delete()">Delete</button>
TS:
delete(){
this.dialog.open(DeleteWorkspaceDialogComponent,{
data: {
workspace: this.workspace
}
});
}
Here you are passing the work space to be deleted and in delete popup, you are calling the service to delete the work space.
Hope it will help.

Send Component Dropdown Values two levels up in Angular with Event Emitter

I am trying to send data between two unrelated components. I am trying to utilize Event Emitter Output and Address Service.
How to get the first Address Dropdown Event Emitter, to Pass Data to the Service? The service can then send data to the Receiver.
export class AddressDropdownComponent implements OnInit {
addresses: any[] = [];
#Input() addressDefaultItem: AddressDto;
#Input() selectedAddress: any;
#Input() TxtField: string = 'addressDescription';
#Output() selectedItemOutput = new EventEmitter();
constructor(private addressService:AddressServiceProxy ) { }
ngOnInit() {
}
statusSelectedItemChanged(e) {
this.selectedAddress = e;
}
Still Working on This Address Service
export class AddressService {
private messageSource = new BehaviorSubject("default message");
currentMessage = this.messageSource.asObservable();
constructor() { }
changeMessage(message: string) {
this.messageSource.next(message)
}
}
Resource: following is only for parent-child, looking for unrelated 'grandfather' or 'sibling' cases
What is the best way to use nested components in Angular 4 and calling a method of a parent to child and vice versa?
So you have a source let's say ComponentA and some component on top of it several levels let's say ComponentV.
To connect them you need first to connect them via a service. If there is only one instance of each of them you can use a singletone service (has providedIn: 'root' in #Injectable decorator). However, if you can have multiple ComponentV which contain ComponentA you need to provide this service at top-level of hierarchy. In this case ComponentV must have providers: [SomeService] to create a new service instance when ComponentV is created.
Then you define some prop in SomeService to share data.
For example you end up with
// SomeService contents
...
// create a source
private dataSource$ = new Subject();
// Expose as observable so it's value can be changed
// only via exposed method (dataChanged)
data$ = this.dataSource$.asObservable();
// public API to pass new data
dataChanged(data) {
this.dataSource$.next(data);
}
...
Then you inject this service in ComponentA and define a function to emit data change.
// ComponentA contents
...
constructor(private readonly someService: SomeService) {}
onDataChange(data) {
// here we notify about data change
this.someService.dataChanged(data);
}
...
Then you need to subscribe to the observable in top level component
// CompoentV contents
...
constructor(private readonly someService: SomeService) {
// here we subsribe to changes and use whatever handler we need
this.someService.data$.subscribe(data => {
// some logic goes here or pass this data to a method
});
}
...
This allows one to share some state or events between unrelated or loosely related components.

Property getter calling function can't be binded?

I have a model class with a property that returns a value by calling a method, but when i try to bind that property, there is result on the page, but also no error occuring.
export class TestClass {
testProperty: string = this.getString();
getString() {
return 'hello';
}
}
in html:
{{model.testProperty}}
Does Typescript / Angular not support this? What is the common way to do it?
This is a simple enough class. What you could do is initialize testProperty as null or by a default value and in ngOnInit() assign the returned value from the function.
import { Component, OnInit } from '#angular/core';
export class TestClass implements OnInit {
testProperty: string = null;
ngOnInut() {
this.testProperty = this.getString();
}
getString() {
return 'hello';
}
}
The ngOnInit() is a lifecycle hooks and runs when the component is initialized.
public get testProperty(): string {
return 'hello'
}
If you use the 'get' after the public, it allow the function to be called as it would be a normal variable. I think you shouldnt even need the 'model.'

Angular View binding not updating with simple boolean

Goodday, This is probably a nooby question but I can't get it to work.
I have a simple service which toggles an boolean, if the boolean is true the class active should appear on my div and if false no class.. Simple as that. But the boolean gets updated, but my view doesn't react to it. Do I somehow have to notify my view that something has changed ?
Service:
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class ClassToggleService {
public menuActive = false;
toggleMenu() {
this.menuActive = !this.menuActive;
}
}
View (left menu component):
<div id="mainContainerRightTop" [class.active]="classToggleService.menuActive == true">
Toggle point (top menu component):
<a id="hamburgerIcon" (click)="classToggleService.toggleMenu()">
This because you are changing a value on a service not on the component, so Angular don't need to update the component, because it did not change. If you want to update the view of your component when a service element is modified, you have to use an Observables and Subjects, and subscribe to them. In this way when the element is changed, it automatically notify all the subscribed components.
#Injectable({
providedIn: 'root'
})
export class ClassToggleService {
public menuSubject: Subject<boolean> = new BehaviorSubject<boolean>(false);
public menuActive = this.menuSubject.asObservable();
toggleMenu(val : boolean) {
this.menuSubject.next(val);
}
}
And in your component just implement OnInit interface and subcribe to the observable in the your service:
public localBool = false;
ngOnInit() {
this._myService.menuActive.subscribe(value => this.localBool = value);
}
ComponentToggleMenu() {
this._myService.toggleMenu(!this.localBool);
}
Then your html:
<div id="mainContainerRightTop" [class.active]="localBool">
<a id="hamburgerIcon" (click)="ComponentToggleMenu()">
Why we need service, this should be integrated with component class. As a general rule, you are not supposed to call service method in template file.
export class TestComponent implements OnInit{
public menuActive = false;
toggleMenu() {
this.menuActive = !this.menuActive;
}
}
Template:
<div id="mainContainerRightTop" [class.active]="menuActive">