5

I am practicing with @Input and @Output and here is the issue.

In my home component I have the following:

<!-- Main Content -->
<div class="container">
    
    <div *ngIf="registered">
        <login></login>
    </div>
    
    <div *ngIf="!registered">
        <register></register>
    </div>
    

</div>

registered is located in the HomeComponent.ts

@Component({
    selector:'home',
    templateUrl:'./home.html',
})

export class HomeComponent {

    registered:Boolean = true;

}

My goal is to change the value registered in the home component depending on what is pressed.

If register is pressed in the navigation bar, the form will change to the register form because the value in the home component has been changed. However, if login is pressed it goes back.

I made this a little more complicated but this is for learning purposes.

In the directory user I have three modules, Login, Register and UserService. UserService imports the components from LoginandRegisterand exports them. I importUserServiceintoNavigationBar` module.

In the home.html page where I want to change the forms I just call <nav-bar></nav-bar>

@NgModule({

    imports:[
        SmartAdminModule,
        UserModule //has Login and Register components
    ],

    declarations: [NavigationBarComponent],
    exports: [NavigationBarComponent]
})
export class NavBarModule {

}

I am trying to figure out a way to use either @Output or @Input to solve this issue.

This is also on GitHub HERE Any advice with be greatly appreciated.

----------------After some advice from this post this is how I did it but is still not working------------------

I made a UserService

        @Injectable()
export class UserService {

    public registered: Boolean;

    @Output() FormUpdate = new EventEmitter();

    public SetUserToRegistered(){
        this.registered = true;
        this.FormUpdate.emit(this.registered);
        
    }

    public SetUserToUnregistered(){
        this.registered = false;
        this.FormUpdate.emit(this.registered);
       
    }

}

Then I injected it into the home.module.ts

@NgModule({

    imports:[
        SmartAdminModule,
        UserModule,
        NavBarModule
    ],

    exports:[HomeComponent],

    declarations: [HomeComponent],

    providers: [UserService]

})
export class HomeModule {


}

HomeComponent:

 @Component({
    selector:'home',
    templateUrl:'./home.html',
})

export class HomeComponent {

    registered: Boolean;

    constructor(private userService: UserService) {

        this.registered = this.userService.registered;
    }

    handleFormUpdate(formUpdate){
        //what to do here
    }
}

Then I had a aha moment and thought I would make the Login and Register buttons and inject the userService in there and then that button would be pressed it would change the value of this.registered = this.userService.registered; in the HomeComponent

NavBar.html

<div>
                <button (click)="showRegister()">Register</button>
                <button (click)="showLogin()">Login</button>
            </div>

NavBarComponent:

@Component({
    selector:'nav-bar',
    templateUrl:'./navigationBar.html',
})

export class NavigationBarComponent{

    constructor (private userService: UserService) { }


    showLogin(){
        this.userService.registered = true;
    }

    showRegister(){
        this.userService.registered = false;
    }


}

home.html

<div class="container">
    
    <div *ngIf="registered">
        <login (formUpdate)="handleFormUpdate($event)"></login>
    </div>
    
    <div *ngIf="!registered">
        <register (formUpdate)="handleFormUpdate($event)"></register>
    </div>
    
</div>

However nothing is happening.

The project is updated on GitHub.

Community
  • 1
  • 1
Mike3355
  • 11,305
  • 24
  • 96
  • 184
  • Rule of thumb. Input and Output work in parent-children interaction. Input is used communication from parent TO child. Output is used in communication from child TO parent. Just as a sidenote ;) – AT82 Jan 20 '17 at 20:11
  • here is info about component interaction: https://angular.io/docs/ts/latest/cookbook/component-communication.html You might get some ideas there. – AT82 Jan 20 '17 at 20:20

2 Answers2

5

The UserService you have set up is spot on and the best way to manage this variable, I would even go as far as to put the methods that change registered from true to false in the service. That way you know exactly where it is controlled and if you use the flag elsewhere you are not changing that variable on random components.

@Component({
    selector:'nav-bar',
    templateUrl:'./navigationBar.html',
})
export class NavigationBarComponent{

    constructor (private userService: UserService) { }

    showLogin(){
        this.userService.SetUserToRegistered();
    }

    showRegister(){
        this.userService.SetUserToUnregistered();
    }
}

Perhaps the methods could be named different and you can work them in better as you expand your application but it feels like a better approach.

Now you need an observable to monitor that variable, which I wrote in the second part of my answer.

You could even use some rxjs and set the variable up in the service as an observable so that the home component can subscribe to it.

export class HomeComponent implements OnInit {

    public registered: boolean = true;

    public ngOnInit(): void {
        this.userService.RegisteredObservable().subscribe((registered) => {
           this.registered = registered;
        });
    }
}

Then the service could look something like this

import { Injectable } from "@angular/core";
import { BehaviourSubject } from "rxjs/BehaviourSubject";
import { Observable } from "rxjs/Observable";

@Injectable()
export class UserService {

    private registered: BehaviorSubject<boolean>;

    constructor() {
        this.registered = new BehaviourSubject<boolean>();
    }

    public RegisteredObservable(): Observable<boolean> {

         return this.registered;
    }

    public ChangeRegistered() {
         //do stuff

         this.registered.next(newRegisteredValue);
    }
}
Oliver Cooke
  • 1,059
  • 1
  • 8
  • 22
  • Great advice but could you look at my updated post when you have time? – Mike3355 Jan 23 '17 at 16:06
  • @Drew1208 I forgot a few things on the observable side of things, let me know if it all works ok – Oliver Cooke Jan 24 '17 at 19:36
  • Thank you for your efforts. I tried you solution but I `BehaviorSubject;` gave me an error. The goal of this exercise was ti practice `@Input` and `@Output`. Please see update and thank you for your time and sorry for the late response. – Mike3355 Jan 25 '17 at 13:33
4

This approach will use input and output approach but I would have another go at implementing my other answer as this is by far the best solution.

The fix should be like this

home.html

<div class="container-fluid">
    <nav-bar (emitRegistered)="onButtonClicked($event)"></nav-bar>
</div>

home.ts

export class HomeComponent implements OnInit {

    public registered: boolean;

    constructor(private userService: UserService) { }

    public ngOnInit() {
        this.registered = true;
    }

    protected onButtonClicked(event: boolean) {

        this.registered = event;
    }
}

NavigationBar.ts

export class NavigationBarComponent{

    @Output() emitRegistered: EventEmitter<boolean> = new EventEmitter();

    constructor (private userService: UserService) { }

    showLogin(){
        this.emitRegistered.emit(true);
    }

    showRegister(){
       this.emitRegistered.emit(false);
    }
}

Hope I've been a help

PS On the pull fixed an issue with a div not being closed

Oliver Cooke
  • 1,059
  • 1
  • 8
  • 22
  • This is right now but is it not working still. I did exactly what you did. You're right maybe the other solution was better but I want to fully understand `@Output` and `@Input` even if making it more complex than necessary. I updated it on GitHub. I know you are getting annoyed by this point. I am sure I will figure it out eventually. – Mike3355 Jan 25 '17 at 15:12
  • @Drew1208 don't worry drew you'll get it, i made a slight mistake on my code which I have updated. Change clickEvent in the home.component to emitRegister as this is the output you are binding too. – Oliver Cooke Jan 25 '17 at 15:18
  • Still not working but the in the `RegisterComponent` it is saying `onButtonClick()` is not being used. Same for `LoginComponent` – Mike3355 Jan 25 '17 at 15:54
  • @Drew1208 I have updated again, getting mixed up with the two options you can pick from, which one are you trying to implement? On the register and home template make sure the input has the (click) method for onButtonClick() like this. – Oliver Cooke Jan 25 '17 at 16:06
  • I do have that one my templates. If you look at my GitHub here `https://github.com/drewjocham/AngularIssue.git` you will see I am doing just what you are saying. Thank you for your time even if you decide not to look at it. – Mike3355 Jan 25 '17 at 19:41
  • @Drew1208 I have had a look at your source code, and changed the answer to the approach you are taking, let me know if there are any more errors. – Oliver Cooke Jan 25 '17 at 20:05
  • No change. Now the form is not even displaying and the buttons do not render the form. Thanks for your effort. – Mike3355 Jan 25 '17 at 20:32
  • @Drew1208 all done drew made a pull request on your git and updated the answer, looks like I was looking at it the wrong way. cheers. – Oliver Cooke Jan 25 '17 at 21:56
  • I do not see the pull request. I will give you a bounty yes. – Mike3355 Jan 29 '17 at 16:51
  • @Drew1208 I reopened the pull request – Oliver Cooke Jan 29 '17 at 19:44