3

I'm trying to use AngularFire2 to create a simple chat app. The user logs into facebook and saves their profile information to users/uid. Then each time a user sends a chat it sends their UID along with the message. I'm trying to map the UID to the user's information, which is currently working. However when I add/delete messages, some of the data flashes and seems like it is rerendering rather than just updating with the new info. More specifically any profile information that is not my own refreshes. I'm not even sure why mine doesn't flash since it's all coming from Firebase the same way. Anyone have a clue on how to fix the flashing?

chat.service.ts

import { Injectable } from '@angular/core';
import { AngularFire, FirebaseListObservable } from 'angularfire2';
import { Observable } from 'rxjs';
import 'rxjs/Rx';

@Injectable()
export class ChatService {
  chat_list$: Observable<any>;
  items$: FirebaseListObservable<any>;
  constructor(public af: AngularFire) {
    this.items$ = af.database.list('/items', {
      query: { limitToLast: 15, orderByKey: true}
    });
    this.chat_list$ = this.items$.map(chats => {
      console.log('chats: ', chats);
      return chats.map(chat => {
        if ( chat.sender !== af.database.object(`users/${chat.uid}`) ) {
          chat.sender = af.database.object(`users/${chat.uid}`);
        }
        return chat;
      });
    });
  }
}

home.component.ts

import { Component } from '@angular/core';
import { AuthService } from '../services/auth.service';
import { ChatService } from '../services/chat.service';


@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss']
})
export class HomeComponent {
  name: string;
  message: string;
  constructor(public auth: AuthService, public chat: ChatService) {
    auth.isLoggingIn = false;
  }
  onMessageSend(form){
    console.log('form submit: ', form);
    this.chat.items$.push(form);
    this.message = '';
  }
  removeChat(chat_key){
    this.chat.items$.remove(chat_key);
  }
}

home.component.html

<div class="container m-t-1">
<div class="row">
  <div class="col-xs-12">
  <h1>Quick Chat App</h1>
  <button class="btn btn-danger pull-right" (click)="auth.signout()">Sign Out</button>
</div>
</div>
<form (ngSubmit)="onMessageSend(sendForm.value)" #sendForm="ngForm">
<ul class="m-t-1">
  <li *ngFor="let chat of chat.chat_list$ | async">
    <img src="{{ (chat.sender | async)?.photo }}" alt="{{ (chat.sender | async)?.name }}'s profile picture">
    <span class="message">{{ chat.message }}</span> <span class="tag tag-primary m-l-1">{{ (chat.sender | async)?.name }}</span>
    <span class="tag tag-danger" (click)="removeChat(chat.$key)">X</span>
  </li>
</ul>
<div class="input-group m-t-1">
  <input name="message" [(ngModel)]="message" type="text" class="form-control" placeholder="Enter message..." autocomplete="off" required>
  <span class="input-group-btn">
    <button class="btn btn-primary" type="submit"><i class="fa fa-send-o"></i></button>
  </span>
</div>
<label for="message" class="pull-right posting-as">Posting as {{auth.user.name}}</label>
<input type="hidden" name="uid" [(ngModel)]="auth.user.uid">
</form>
</div>

2 Answers 2

1

My second attempt at simplifying my first answer is even better. Check it out, I'm still going to see if it can be optimized any.

chat.service.ts

import { Injectable } from '@angular/core';
import { AngularFire, FirebaseListObservable, FirebaseObjectObservable } from 'angularfire2';
import { Observable } from 'rxjs';
import 'rxjs/Rx';

@Injectable()
export class ChatService {
  chat_list: Array<any>;
  chat_list$: Observable<any>;
  chat_senders$: any = {};
  items$: FirebaseListObservable<any>;
  constructor(public af: AngularFire) {
    this.items$ = af.database.list('/items', {
      query: { limitToLast: 15, orderByKey: true}
    });
    this.chat_list$ = this.items$.map(chats => {
      chats.map(chat => {
        this.chat_senders$[chat.uid] = af.database.object(`users/${chat.uid}`);
        this.chat_senders$[chat.uid].subscribe((sender) => {
          chat.sender = sender;
        });
        return chat;
      });
      return chats;
    });
  }
}

home.component.html

<div class="container m-t-1">
<div class="row">
  <div class="col-xs-12">
  <h1>Quick Chat App</h1>
  <button class="btn btn-danger pull-right" (click)="auth.signout()">Sign Out</button>
</div>
</div>
<form (ngSubmit)="onMessageSend(sendForm.value)" #sendForm="ngForm">
<ul class="m-t-1">
  <li *ngFor="let chat of chat.chat_list$ | async">
    <img src="{{ chat.sender?.photo }}" alt="{{ chat.sender?.name }}'s profile picture">
    <span class="message">{{ chat.message }}</span> <span class="tag tag-primary m-l-1">{{ chat.sender?.name }}</span>
    <span class="tag tag-danger" (click)="removeChat(chat.$key)">X</span>
  </li>
</ul>
<div class="input-group m-t-1">
  <input name="message" [(ngModel)]="message" type="text" class="form-control" placeholder="Enter message..." autocomplete="off" required>
  <span class="input-group-btn">
    <button class="btn btn-primary" type="submit"><i class="fa fa-send-o"></i></button>
  </span>
</div>
<label for="message" class="pull-right posting-as">Posting as {{auth.user.name}}</label>
<input type="hidden" name="uid" [(ngModel)]="auth.user.uid">
</form>
</div>
Sign up to request clarification or add additional context in comments.

Comments

0

Here is my first attempt at a solution involving an extra layer between Firebase and my content. Does anyone else have a better solution?

chat.service.ts

import { Injectable } from '@angular/core';
import { AngularFire, FirebaseListObservable, FirebaseObjectObservable } from 'angularfire2';
import { Observable } from 'rxjs';
import 'rxjs/Rx';

@Injectable()
export class ChatService {
  chat_list: Array<any>;
  chat_list$: Observable<any>;
  chat_senders$: any = {};
  items$: FirebaseListObservable<any>;
  constructor(public af: AngularFire) {
    this.items$ = af.database.list('/items', {
      query: { limitToLast: 15, orderByKey: true}
    });
    this.chat_list$ = this.items$.map(chats => {
      chats.forEach(chat => {
        this.chat_senders$[chat.uid] = af.database.object(`users/${chat.uid}`);
      });
      return chats;
    });
    this.chat_list$.subscribe(chat_list => {
      this.chat_list = chat_list.map(chat => {
        this.chat_senders$[chat.uid].subscribe((sender) => {
          chat.sender = sender;
        });
        return chat;
      });
      console.log(this.chat_list);
    });
  }
}

home.component.html

<div class="container m-t-1">
<div class="row">
  <div class="col-xs-12">
  <h1>Quick Chat App</h1>
  <button class="btn btn-danger pull-right" (click)="auth.signout()">Sign Out</button>
</div>
</div>
<form (ngSubmit)="onMessageSend(sendForm.value)" #sendForm="ngForm">
<ul class="m-t-1">
  <li *ngFor="let chat of chat.chat_list">
    <img src="{{ (chat.sender)?.photo }}" alt="{{ (chat.sender)?.name }}'s profile picture">
    <span class="message">{{ chat.message }}</span> <span class="tag tag-primary m-l-1">{{ (chat.sender)?.name }}</span>
    <span class="tag tag-danger" (click)="removeChat(chat.$key)">X</span>
  </li>
</ul>
<div class="input-group m-t-1">
  <input name="message" [(ngModel)]="message" type="text" class="form-control" placeholder="Enter message..." autocomplete="off" required>
  <span class="input-group-btn">
    <button class="btn btn-primary" type="submit"><i class="fa fa-send-o"></i></button>
  </span>
</div>
<label for="message" class="pull-right posting-as">Posting as {{auth.user.name}}</label>
<input type="hidden" name="uid" [(ngModel)]="auth.user.uid">
</form>
</div>

Comments

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.