Angular2 : How to communicate from parent componen

2019-06-21 08:11发布

问题:

I'm loading some of div in between tag. Its as bellow.

Here is my index.html

<html>
<script>
System.import('app').catch(function(err){ console.error(err); });
</script>
</head>

<!-- 3. Display the application -->

<body>
<my-app>Loading...</my-app>
</body>
</html>

app.module.ts

@NgModule({
imports: [
    BrowserModule,
    FormsModule,
    AppRoutingModule
],
declarations: [
    AppComponent,
    LoginComponent,
    HomeComponent,
    NewsfeedComponent,
    TopBarComponent,
    SideMenuComponent
],
providers : [
    AuthGaurd
],
bootstrap: [
    AppComponent
] })
export class AppComponent {}

home.component.ts

@Component({
    selector: 'home',
    moduleId: module.id,
    templateUrl: 'home.component.html',
    providers : [
        LoginService
    ]
})

export class HomeComponent implements OnInit{
isLoggedin : boolean;
constructor (private loginService : LoginService) { }
ngOnInit(): void {
    this.loginService.getLogged().subscribe((isLoggedIn: boolean) => {
        this.isLoggedin = isLoggedIn;
    }); }
}

home.component.html

<side-menu *ngIf='isLoggedin'></side-menu>
<top-bar *ngIf='isLoggedin'></top-bar>
<router-outlet></router-outlet>

auth.gaurd.ts

@Injectable()
export class AuthGaurd implements CanActivate{
    constructor(private router : Router) {
    }
    canActivate(){
        if (localStorage.getItem('isLogin')){
            return true;
        }
        this.router.navigate(['/login'])
        return false;
    }
}

login.service.ts

@Injectable()
export class LoginService {
    private subject: Subject<boolean> = new Subject<boolean>();
    constructor(private router : Router) {
    }
    login(){
        this.setLogged(true);
        localStorage.setItem("isLogin","true");
        this.router.navigate(['/news-feed']);
    }
    logout(){
        this.setLogged(false);
        localStorage.removeItem("isLogin");
        this.router.navigate(['/login']);
    }
    getLogged(): Observable<boolean> {
        return this.subject.asObservable();
    }
    setLogged(val : boolean): void {
        this.subject.next(val);
    }
}

login.component.ts

@Component({
    selector: 'login',
    moduleId: module.id,
    templateUrl: 'login.component.html'
})

export class LoginComponent {
    constructor (private loginService : LoginService) {
    }

    login(){
        this.loginService.login()
    }
}

login.component.html

<input type="number” #mobileNumber />
<input type="password" #password />
<input type="button" (click)="login()">

newsfeed.component.ts

@Component({
    selector: 'newsfeed',
    moduleId: module.id,
    templateUrl: 'newsfeed.component.html',
})

export class NewsfeedComponent {

}

newsfeed.component.html

some html text....!!!!

app-routing.module.ts

@NgModule({
imports: [
    RouterModule.forRoot([
        {
            path : 'login',
            component : LoginComponent
        },
        {
            path : 'news-feed',
            component : NewsfeedComponent,
            canActivate : [AuthGaurd]
        },
        {
            path : '',
            redirectTo : '/news-feed',
            pathMatch : 'full'
        }
        {
            path: '**',
            component: LoginComponent
        }
    ])
],
exports: [
    RouterModule
]
})
export class AppRoutingModule {}

Actually it's working fine when i'm going with clicks. like its launching perfect than on click of login button it forwards to newsfeed and shows the expected result. but when I'm going from the browser url, its not loading side bar and top bar component from the home.html

回答1:

I'm not sure if this fixes everything but I think you want to read the value from localstorage first to get the recently stored status and if you use BehaviorSubject listeners also get the last status if this.subject.emit() was called before a subscriber was subscribing.

@Injectable()
export class LoginService {
    //private subject: Subject<boolean> = new Subject<boolean>(false);
    private subject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(); // <<< changed
    constructor(private router : Router) {
      this.sublect.next(logalStorage.getItem('isLogin')); // <<< added
    }
    login(){
        this.setLogged(true);
        localStorage.setItem("isLogin","true");
        this.router.navigate(['/news-feed']);
    }
    logout(){
        this.setLogged(false);
        localStorage.removeItem("isLogin");
        this.router.navigate(['/login']);
    }
    getLogged(): Observable<boolean> {
        return this.subject.asObservable();
    }
    setLogged(val : boolean): void {
        this.subject.next(val);
    }
}


回答2:

I encountered with that problem. Here is how I solve this situation;

  • Create a shared service with observable fields as explained in official angular documents
  • In your navigation bar component, subscribe to value from shared service to display navigation bar
  • In login and logout pages, update value. Since you already subscribe the value, subscriber handle this situation by itself
  • Create an authentication service. Add a method similar to this to ask your backend, is request authenticated;
//method parameters depend on what you want
isAuthorized(url: string, errorCallback: (any) => void) {
        let body = JSON.stringify(url)
        return this.http.post('account/isauthorized', body)
            .map((response: Response) => {
                //update value to display navigation bar if user is authenticated
                yourSharedService.observableField.next(true);
                return true;
            })
            .catch((response: Response) => {
                errorCallback(response.status);
                return Observable.of(false);
            });
    }
  • Create an authentication guard and call isAuthorized method in canActivate or canLoad or CanActivateChild.

  • In your callback, handle unauthorized requests. You can redirect user to error pages or remove the navigation bar, and whatever you want.

I hope it helps!