3

While making some edits to my Angular 2 app, I was able to get something working BEFORE I assumed it should be working. In other words, I'm a little perplexed as to why it's working in it's current configuration. Specifically, I have an authentication.service that handles my login auth. And I have both a login component and a chat component, both of which have a private instance of the authentication.service in their respective constructors. What I wanted to do was be able to pass the username from the login functionality down to the chat view, so I could display the logged-in user's username in the chatbox. I've got that working, but, strangely enough, I got it working by adding "this.authenticationService.username = this.model.username;" in the login.component, rather than in the authentication.service. So I'm perplexed as to how/why the chat.component even has access to that info. If I remove that one line of code from my login.component, the username will not be passed through to the chat.component. But I never actually import the login component into the chat component, so how is it working as is? Curious to see if someone can help me understand this. Here are the files in question. First, my authentication.service file:

import { ContextMenu } from './../ui/context-menu.component';
import { Router, RouterLinkActive } from '@angular/router';
import { Injectable } from '@angular/core';
import { Http, Headers, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';

@Injectable()
export class AuthenticationService {

    username;

    constructor(private http: Http) {}

    login(username: string, password: string) {
        return this.http.post('/api/authenticate', JSON.stringify({ username: username, password: password }))
            .map((response: Response) => {
                // login successful if there's a jwt token in the response
                let user = response.json();
                if (user && user.token) {
                    // store user details and jwt token in local storage to keep user logged in between page refreshes
                    localStorage.setItem('currentUser', JSON.stringify(user));
                }
            });
    }


    isAuthenticated() {
        if (localStorage.getItem('currentUser')) {
            //console.log('User successfully authenticated...');
            return true;
        } else {
            // console.log('User is not authenticated...');
            return false;
        }
    }

    logout() {
        // remove user from local storage to log user out
        localStorage.removeItem('currentUser');
        console.log('User successfully logged out');
    }
}

And here's my login.component file:

import { UserService } from './../../data/user.service';
import { AuthenticationService } from './../../data/authentication.service';
import { AlertService } from './../../data/alert.service';
import { Component, OnInit, Input } from '@angular/core';
import { Router } from '@angular/router';

@Component({
    selector: 'app-login',
    templateUrl: 'app/views/login/login.component.html',
    styleUrls: ['app/views/login/login.component.css']
})

export class LoginComponent implements OnInit {

    model: any = {};
    loading = false;
    username;
    password;

    constructor(
        private router: Router,
        private authenticationService: AuthenticationService,
        private alertService: AlertService,
        private userService: UserService) { }

    ngOnInit() {
        // reset login status
        this.authenticationService.logout();
    }

    login() {
        this.loading = true;
        this.authenticationService.login(this.model.username, this.model.password)
            .subscribe(
                data => {
                    this.router.navigate(['/']);
                    console.log('User logged in as: ' + this.model.username);
                },
                error => {
                    this.alertService.error(error);
                    this.loading = false;
                });
                this.authenticationService.username = this.model.username;
    }

}

Here's my chat.component file:

import { AuthenticationService } from './../../data/authentication.service';
import { Router, ActivatedRoute } from '@angular/router';
import { ChatService } from './chat.service';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { TabPage } from '../../ui/tab-navigation/tab-page';

@Component({
  templateUrl: './chat.component.html',
  styleUrls: ['./chat.component.less'],
})
export class ChatComponent extends TabPage implements OnInit, OnDestroy {

  messages = [];
  users = [];
  routes;
  connection;
  userbase;
  route;
  message;
  user;

  constructor(private chatService:ChatService,
              router: Router,
              route: ActivatedRoute,
              private authenticationService: AuthenticationService) {

        super(router, route);

        this._title = 'Chat Room';

        this.addEventListener('paramsChange', function(params) {

            this._title = 'Chat Room';

        }.bind(this));
   }

  sendMessage() {
    this.chatService.sendMessage(this.message);
    this.message = '';
  }

  sendUser() {
    this.chatService.sendUser(this.user);
    this.user = '';
  }

    trackUser() {
    this.chatService.trackUser(this.route);
    console.log('A user just navigated to ' + this.route);
  }

  // For when user clicks "enter/return" to send message
  eventHandler(event: KeyboardEvent): void {
    if (event.key === 'Enter') {
        this.chatService.sendMessage(this.message);
        this.message = '';
    }
  }

  ngOnInit() {
    this.connection = this.chatService.getMessages().subscribe(message => {
      this.messages.push(message);
    });
    this.userbase = this.chatService.getUsers().subscribe(user => {
      this.users.push(user);
    });
    this.routes = this.chatService.getRoutes().subscribe(route => {
      this.routes.push(route);
    });
  }

  ngOnDestroy() {
    this.connection.unsubscribe();
    this.userbase.unsubscribe();
  }

    public getTabId(params): string {
        return 'Chat Room';
    }
}

Lastly, my chat.component.html file looks like this (this is where I'm actually using the string interpolation to display the username in the chat):

<div class="centered-display" align="center">
  <h3>User: {{authenticationService.username}}</h3>
  <div *ngFor="let message of messages" class="message">
    {{authenticationService.username}}: {{message.text}}
  </div>
  <input class="form-group" [(ngModel)]="message" (keypress)="eventHandler($event)">
  <div class="spacing">
    <button class="submit-btn" md-button (click)="sendMessage()">SEND</button>
  </div>
</div>

Can someone help me understand why this is working as is? The one line that makes this work is the last line in my login.component login function: "this.authenticationService.username = this.model.username;". But, again, my chat component doesn't have access to my login component (as as I understand). So why is this working?

1 Answer 1

1

Your chat component and login component may not directly relate to each other, but they do both have access to the authenticationService. Specifically, they both have a reference to the same instance of that service injected.

This means that when you do this.authenticationService.username = this.model.username in your login component, you are setting the username property on the same authenticationService object that you are accessing with your interpolation in the chat component view ({{authenticationService.username}}).

Sign up to request clarification or add additional context in comments.

3 Comments

Ah, I see. So it's like a singleton, then. Both components reference the same instance of the auth service.
@user2584924 Exactly. This style of coding can be tricky to debug and maintain, because anything can change properties from anywhere and it is difficult to find who made what changes when something goes wrong. But it does work and explains why it works for you in this particular situation.
Perfect. Thanks for clarifying that for me.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.