How to make a Chat App using Laravel 11 , Angular 17 and Web Sockets
Midhun Manu
Posted on September 2, 2024
Hello Developers I am Midhun and in this small blog I will show you how to create a simple real time chat app using Laravel 11and Angular 17.
Let's start with out Back End
Create a Laravel project
composer create-project laravel/laravel
serverphp artisan install:api
-
Install Broadcasting
php artisan install:broadcasting
since we are making a server - client application do no install Reverb
Install Pusher
composer require pusher/pusher-php-server
In your .env add Pusher configs
PUSHER_APP_ID="your-pusher-app-id"
PUSHER_APP_KEY="your-pusher-key"
PUSHER_APP_SECRET="your-pusher-secret"
PUSHER_HOST=
PUSHER_PORT=443
PUSHER_SCHEME="https"
PUSHER_APP_CLUSTER="mt1"
ensure that you set your Broadcast Connection as pusher instead of logs in your .envNow we will make an event
php artisan make event:ChatMessageEvent
In our ChatMessageEvent
<?php
namespace App\Events;
use App\Models\Chat\Message;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class ChatMessageEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public Message $message;
public function __construct($message)
{
$this->message = $message;
}
public function broadcastOn(): array
{
return [
new Channel('chat'),
];
}
public function broadcastAs(): string
{
return 'message';
}
}
For our web sockets we need the ShouldBroadcast interface.
The BroadCastOn method gives the channel and
the BroadCastAs method gives the event.
- Create a Controller
php artisan make:controller ChatController
<?php
namespace App\Http\Controllers;
use App\Events\ChatMessageSent;
use App\Events\UserJoined;
use Illuminate\Http\Request;
class ChatController extends Controller
{
public function sendMessage(Request $request)
{
$username = $request->input("username");
$message = $request->input("message");
event(new ChatMessageSent($username, $message));
return response()->json(["status" => "Message Sent"], 200);
}
}
- Add the route in api.php
Route::post("/send", [ChatController::class, "sendMessage"]);
- Start the server
php artisan serve
- Run the queue
php artisan queue:work
Let's Start the Angular Front End Now
- make an Angular App
ng new client --standalone false --strict false
- install pusher-js
npm i pusher-js
- create the components
ng g c chat
ng g c login
- let's connect the client to sockets
in chat.component.ts
this is what we are going to use
this.pusherInstance = new Pusher("PUSHER_KEY", {
cluster: "ap2"
});
this.channel = this.pusherInstance.subscribe("chat");
this.channel.bind("message", (data:any) => {
console.log(JSON.stringify(data))
})
- so this is how out chat component be:
import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import Pusher from 'pusher-js';
interface Message {
username: string;
message:string;
}
@Component({
selector: 'app-chat',
templateUrl: './chat.component.html',
styleUrl: './chat.component.css'
})
export class ChatComponent implements OnInit {
username = "username";
message = "";
messages: Message[] = [];
users: string[] = [];
pusherInstance:any;
channel: any;
currentUser = "";
ngOnInit(): void {
this.username = history.state.currentUser;
this.users.push(this.username);
this.pusherInstance = new Pusher("PUSHER_KEY", {
cluster: "ap2"
});
this.channel = this.pusherInstance.subscribe("chat");
this.channel.bind("message", (data:any) => {
console.log(JSON.stringify(data))
this.messages.push(data)
})
}
constructor(private http: HttpClient) {}
isCurrentUser(username:string): boolean {
return username === this.username;
}
send() {
this.http.post("http://localhost:8000/api/send", {
"username": this.username,
"message": this.message
}).subscribe((res)=> {
this.message = "";
}, (error) => {
console.error(error);
})
}
}
now let's write our html part
<div class="card mx-4">
<div class="card-body d-flex flex-column" style="height: 500px;">
<div class="d-flex align-items-center mb-3">
<h5 class="card-title mb-0 me-3">{{username}}</h5>
</div>
<hr>
<ul class="list-unstyled d-flex flex-column flex-grow-1 mb-3 overflow-auto">
<li *ngFor="let msg of messages"
class="d-flex mb-2"
[ngClass]="{'justify-content-end': msg.username === username, 'justify-content-start': msg.username !== username}">
<div class="d-flex flex-column">
<div [ngClass]="msg.username === username ? 'message-right' : 'message-left'">
{{msg.message}}
</div>
</div>
</li>
</ul>
</div>
<div class="d-flex p-3 border-top">
<input
type="text"
class="form-control me-2"
placeholder="Start typing..."
[(ngModel)]="message"
/>
<button (click)="send()" class="btn btn-success">Send</button>
</div>
</div>
we will need a simple login component to get current user
login.component.ts
import { Component } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrl: './login.component.css'
})
export class LoginComponent {
username: string = "";
constructor(private router: Router) {}
login() {
console.log(this.username)
this.router.navigate(["chat"], {state: {currentUser: this.username}});
}
}
login.component.html
<div class="d-flex justify-content-center align-items-center vh-100 bg-light">
<div class="card shadow" style="width: 100%; max-width: 400px;">
<div class="card-body">
<h5 class="card-title text-center mb-4">Let's Chat</h5>
<div class="mb-3">
<input type="text" [(ngModel)]="username" class="form-control" placeholder="Username" aria-label="Username">
</div>
<button (click)="login()" class="btn btn-danger w-100">Let's Go</button>
</div>
</div>
</div>
Posted on September 2, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.