0

I'm new using typescript, I'm working in an angular 2 project and I'm trying to do a chatbox where the new messages should be in the bottom and the old ones go one line up instead of this: enter image description here

Also, I wanted them to don't leave the div and when it's full and instead of that, I wanted just to use the scroll to view the old messages...

This is what I have:

chatroom.component.html:

<h2>Player Notifications</h2>
  <p *ngFor="let m of playersChannel">{{m}}</p>

<h2>Chat history</h2>
<div class='chatbox'>
  <p *ngFor="let m of chatChannel">{{m}}</p>
</div>

chatroom.component.css:

.chatbox{
    width: 100%;
    height: 500px;
}

.chatbox p{ 
    text-align: bottom;

}

chatroom.component.ts:

import { Component, OnInit } from '@angular/core';

import {WebSocketService } from './websocket.service';

@Component({
    moduleId: module.id,
    selector: 'chat',
    styleUrls: [ 'chatroom.component.css' ],
    templateUrl: 'chatroom.component.html'
})
export class ChatroomComponent implements OnInit {
    playersChannel: string[] = [];
    chatChannel: string[] = [];    

    constructor(private websocketService: WebSocketService){
    }

    ngOnInit() {
        this.websocketService
            .getChatMessages()
            .subscribe((m:any) => this.chatChannel.push(<string>m));
        this.websocketService
            .getPlayersMessages()
            .subscribe((m:any) => this.playersChannel.push(<string>m));
    }

}

websocket.service.ts:

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';

import {Observable} from 'rxjs/Observable';

import * as io from 'socket.io-client';

@Injectable()
export class WebSocketService {
    private socket: SocketIOClient.Socket;
    constructor() {
        if (!this.socket) {
            this.socket = io(`http://${window.location.hostname}:${window.location.port}`);
        }
    }

    sendChatMessage(message: any) {
        this.socket.emit('chat', this.getUserTime() + message);
    }

    getPlayersMessages(): Observable<any> {
        return this.createObservable('players');
    }

    getChatMessages(): Observable<any> {
        return this.createObservable('chat');
    }

     getUserTime(){
         let now = Date.now();
         let date = new Date(now);
         let hours = date.getHours();
         let mins = date.getMinutes();
         let secs = date.getSeconds();
        return hours + ":" + mins + ":" + secs + ": ";
    } 

    private createObservable(channel: string): Observable<any> {
        return new Observable((observer:any) => {
            this.socket.on(channel, (data:any) => {
                observer.next(data);
            });
            return () => this.socket.disconnect();
        });

    }
}

server.websocket.ts:

const io = require('socket.io');

export class WebSocketServer {
    public io: any;




    public init = (server: any) => {
        this.io = io.listen(server);
        this.io.sockets.on('connection', (client: any) => {
            client.emit('players', Date.now() + ': Welcome to battleship');
            client.broadcast.emit('players', Date.now() + ': A new player has arrived');
            client.on('chat', (data) => this.io.emit('chat', data));

        });
    };
   public notifyAll = (channel: string, message: any) => {
        this.io.sockets.emit(channel, message);
    };


};
João Silva
  • 531
  • 4
  • 21
  • 40

2 Answers2

2

You can invert the order of the messages by simply sorting the array using something like added date, array index e.t.c or replacing push with unshift to add new messages to the beginning of the array.

See this for a simple order pipe: angular 2 sort and filter

Aligning the messages themselves to the bottom of screen or container (use css)

.chatbox{
  display:table;
}
.chatbox p{
  display:table-cell;
  vertical-align:bottom;
}
Community
  • 1
  • 1
Evans M.
  • 1,791
  • 16
  • 20
  • Thank you... and any ideia to pin them to the bottom? – João Silva Jan 04 '17 at 16:28
  • can be done in css using display table and verticle align to bottom.
    Content here
    #parent {display: table} #child { display: table-cell; vertical-align: bottom; }
    – Evans M. Jan 04 '17 at 17:12
  • M, I tried it but the cells get all in the same row... I managed to make them one per row but there was also a problem... The table auto size itself so the first messages got a lot of space between them until there are enought message to make it look just like a decent

    – João Silva Jan 04 '17 at 17:25
  • 1
    This all can be fixed with plain css. see this JSFiddle for an example. Also avoid using the p tag to display a list of items. https://jsfiddle.net/4o07aqj3/5/ – Evans M. Jan 04 '17 at 18:30
  • I just noticed a problem now with this solution... It worked but the overflow got messed... if there are to many msgs instead of there is a scroll it just keep getting the chat bigger – João Silva Jan 05 '17 at 12:11
  • We are only supposed to help you when you are stuck, not create the solution for you. You should adapt the code to work for your scenario https://jsfiddle.net/evansmwendwa/4o07aqj3/9/ – Evans M. Jan 06 '17 at 16:58
1

This should be a plain CSS solution. Set a fixed height to div.chatbox and add overflow-y: auto. As for the messages to stay at the bottom, you can add position: relative to div.chatbox and position: absolute; bottom: 0 to div.chatbox > p.

Like this:

.chatbox {
    height: 400px; /* reasonable estimate */
    overflow-y: auto; /* show scrollbar only when text overflows vertically */
    position: relative;
}

.chatbox > p {
    position: absolute;
    bottom: 0;
}

EDIT: To reverse the order, you can either create a custom pipe or use a prebuilt pipe from the ng-pipes module (Angular 2 ngFor - reverse order of output using the index)

Custom (courtesy of http://www.devsplanet.com/question/35703258):

@Pipe({
  name: 'reverse'
})
export class ReversePipe {
  transform(value) {
    return value.slice().reverse();
  }
}

Cheers!

Community
  • 1
  • 1
Technohacker
  • 735
  • 5
  • 19