קודקודייל
  • קודקודייל
  • מי אתם קודקודייל?
  • קורסים בחינם
  • צרו קשר
  • בניית אתרים
    • וורדפרס
  • נגישות אתרים
  • כל הקטגוריות
    • אנגולר
    • HTML
    • CSS
    • Javascript
    • Typescript
    • NodeJs
    • בלוקציין
  • קודקודייל
  • מי אתם קודקודייל?
  • קורסים בחינם
  • צרו קשר
  • בניית אתרים
    • וורדפרס
  • נגישות אתרים
  • כל הקטגוריות
    • אנגולר
    • HTML
    • CSS
    • Javascript
    • Typescript
    • NodeJs
    • בלוקציין
קודקודייל
  • קודקודייל
  • מי אתם קודקודייל?
  • קורסים בחינם
  • צרו קשר
  • בניית אתרים
    • וורדפרס
  • נגישות אתרים
  • כל הקטגוריות
    • אנגולר
    • HTML
    • CSS
    • Javascript
    • Typescript
    • NodeJs
    • בלוקציין
  • קודקודייל
  • מי אתם קודקודייל?
  • קורסים בחינם
  • צרו קשר
  • בניית אתרים
    • וורדפרס
  • נגישות אתרים
  • כל הקטגוריות
    • אנגולר
    • HTML
    • CSS
    • Javascript
    • Typescript
    • NodeJs
    • בלוקציין
ראשי ♦ nodeJs ♦ Chat – angular, nodejs and socket.io

Chat – angular, nodejs and socket.io

עידן יצחקי 29 באפריל 2022 אין תגובות

נראה איך בונים צ'ט פשוט עם חלוקה לחדרים כאשר הפרסום הודעה רלונטי רק לאותו החדר.

התהליך:

  1. הקמה של פרוייקט אנגולר
  2. הוספה של צד שרת NODEJS
  3. התקנה של socket.io בצד השרת ובצד הלקוח.
  4. הוספה של פונקציות בצד השרת ובצד הלקוח שיטפלו בצ'ט שלנו
ng new chat

אני מייצר את הפרויקט עם routing ועם scss לשם הנוחות, אבל אין חובה.

נכנס לספרית הפרויקט ונפתח אותה ב-VSCODE או בכל IDE שמתאים לכם.

בואו נוסיף את צד השרת

npm i express

אחרי התקנה, נוסיף לספרית בסיס שלנו (C:\chat) ספריה נוספת שהיא תאכלס את השרת ונקרא לה server.

בתוכה ניצור קובץ app.js (זה השרת שלנו).

נוסיף את socket.io לצד שרת ולצד לקוח בצורה הזו:

npm i socket.io
npm i socket.io-client

רק כדי שיהיה נוח לעבוד עם השרת, נתקין גם את nodemon

npm i -g nodemon

מה שהיא עושה זה דבר מאוד פשוט אבל מאוד נוח, כל פעם שאנחנו משנים משהו בקבצי השרת ושומרים, nodemon מרענן אוטומטית את השרת במקום שאנחנו נצטרך לעצור אותו ולהפעיל מחדש.

נעשה שינוי קטן בקובץ package.json

 "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "dev": "nodemon ./server/app.js",
    "watch": "ng build --watch --configuration development",
    "test": "ng test"
  },

אנחנו נוסיף את שורה מספר 5, כך שנריץ את הפקודה npm run dev השרת שלנו יעלה עם nodemon.

ועכשיו לחלק האומנותי 😎

השרת

const express = require('express');

const app = express();

// ------------ CHAT ------------
const server = require('http').createServer(app);
const io = require('socket.io')(server, { cors: { origin: "*" } });
io.on('connection', (socket) => {
    socket.on('join', (data) => {
        socket.join(data.room);
        socket.broadcast.to(data.room).emit('user joined');
    });
    socket.on('message', (data) => {
        io.in(data.room).emit('update', { user: data.user, message: data.message })
    });
})
// ------------ CHAT ------------

const port = 3080;
server.listen(port, () => {
    console.clear();
    console.log(`Server listening on the port::${port}`);
})

בשורה 7:

אנחנו קוראים ל-socket.io ומעבירים לו את השרת שלנו וכדי למנו שגיאות CORS, אנחנו אומרים לו שמכל מקום אפשר לגשת לכאן.

** בכל מקום שרואים ON, יש להתייחס לשם שבא אחריו כשם ה-API או ה-EVENT שאליו אנחנו ניגש מצד הלקוח **

שורות 8 – 12:

אם מופעל המקרה של connection , יש אפשרות להצטרף לחדר ואפשרות לשלוח הודעה.

במקרה של הצטרפות לחדר, נדרש מספר חדר. במקרה של שליחת הודעה, מאתרים את החדר המתאים ומעדכנים שם את המשתמש וההודעה שלו.

שורות 19 -23:

ניצור מספר פורט קבוע ונפעיל את השרת שלנו עם הפורט שיצרנו.

בתוך הפונקציה, אני מנקה את הלוג ושולח עדכון של הפורט שהשרת מקשיב בו.

סיימנו עם צד שרת 🤩

אנגולר

ניצור קובץ שירות לתקשר עם צד שרת

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { io, Socket } from 'socket.io-client';

@Injectable({
  providedIn: 'root'
})
export class ChatService {
  private socket: Socket;
  private url = 'http://localhost:3080'; // server path

  constructor() {
    this.socket = io(this.url);
  }
  joinRoom(data: IChatJoinRoom): void {
    this.socket.emit('join', data);
  }
  sendMessage(data: IChatSendMessage): void {
    this.socket.emit('message', data);
  }
  getMessage(): Observable<IChatMessage> {
    return new Observable<IChatMessage>(observer => {
      this.socket.on('update', (data) => {
        observer.next(data);
      });
      return () => {
        this.socket.disconnect();
      }
    });
  }
}
interface IChatMessage {
  user: string,
  message: string,
}
interface IChatSendMessage {
  user: string,
  room: string,
  message: string,
}
interface IChatJoinRoom {
  user: string,
  room: string,
}

שימו לב ששורה 10 פנה לשרת עם הפורט הקבוע שמעודכן גם בשרת.

בנוסף, ניתן לראות ששורות 16,19 פונות ל-"API" ברמת השם שנתנו בשרת. אם בשרת במקום join היינו נותנים שם אחר, אז גם כאן בשורה 16 היינו צריכים לשנות לאותו שם.

התקשורות עובדת לשני הכיוונים ולכן ניתן לראות שהשרת מחזיר תשובה עם update וזה גורם להפעלה של שורה 23 אצלנו בשירות.

פחות או יותר, אפשר להגיד שזה הלב של הצ'ט, אני אוסיף גם את הלוגיקה של הדף והנראות שלו אבל את זה תוכלו לממש באיזו צורה שתרצו.

ניצור דף בית שיכיל את הצ'ט שלנו

<section class="section-login">
    <label class="label-block">
        <span>User name:</span>
        <input type="text" #user [disabled]="userId!==''">
        <button type="button" (click)="onUserId()" *ngIf="userId===''">login</button>
    </label>
    <label class="label-block">
        <span>Room number:</span>
        <input type="text" #room [disabled]="roomId!==''">
        <button type="button" (click)="onRoomId()" *ngIf="roomId===''">select</button>
    </label>
</section>
<section class="chat-wrapper">
    <div class="chat-title-wrapper">
        <h3 class="chat-title">Chat</h3>
    </div>
    <div class="chat" aria-live="polite">
        <ul class="chat-list">
            <li *ngFor="let mess of messagesArr" class="chat-item" [ngClass]="{'me':mess.user===userId}">
                <div class="message-wrapper">
                    <b *ngIf="mess.user">{{mess.user===userId?'Me':mess.user}}: </b>
                    <span>{{mess.message}}</span>
                </div>

            </li>
        </ul>
    </div>
    <div class="chat-input-wrapper">
        <input type="text" #messageText class="chat-input" placeholder="Type your message here">
        <button type="button" class="chat-btn" (click)="sendMessage()">Send</button>
    </div>
</section>
$radius-xs: 5px;
$lighter-color: #ededed;
$blue: #66aaff;
$blue-1: #8c8cd5;
$font-size-xs: 16px;
$space-xs: 5px;
$space-s: 10px;
$space-m: 15px;
$green: #44ff44;
$green-1: #44aa44;
$color: #332233;
$color-rev: #ffffff;
.section-login {
    max-width: 300px;
    .label-block {
        display: block;
        & + .label-block {
            margin-top: $space-m;
        }
        span {
            padding: $space-xs $space-s;
            display: block;
        }
        input {
            padding: $space-xs $space-s;
            border-radius: $radius-xs;
        }
        button {
            padding: $space-xs $space-s;
            border-radius: $radius-xs;
            font-size: $font-size-xs;
        }
    }
}
.chat-wrapper {
    max-width: 300px;
    margin-top: $space-m;
    border-radius: $radius-xs;
    border: 1px solid $lighter-color;
    overflow: hidden;
    .chat-title-wrapper {
        background-color: $blue-1;
        .chat-title {
            font-size: $font-size-xs;
            margin: 0;
            padding: $space-s;
        }
    }
    .chat {
        height: 600px;
        overflow: auto;
        .chat-list {
            padding: 0;
            list-style-type: none;
            .chat-item {
                margin: $space-s;
                .message-wrapper {
                    padding: $space-s;
                    border-radius: 0 $radius-xs $radius-xs $radius-xs;
                    background-color: $green;
                    display: inline-block;
                    margin-inline-end: $space-m;
                    color: $color;
                }
                &.me {
                    text-align: end;
                    .message-wrapper {
                        border-radius: $radius-xs 0 $radius-xs $radius-xs;
                        background-color: $green-1;
                        text-align: start;
                        margin-inline-start: $space-m;
                        margin-inline-end: 0;
                        color: $color-rev;
                    }
                }
            }
        }
    }
    .chat-input-wrapper {
        padding: $space-s;
        display: flex;
        .chat-input {
            border-radius: $radius-xs;
            padding: $space-xs;
            box-sizing: border-box;
            margin-inline-end: $space-xs;
            flex: 1 0 0;
        }
        .chat-btn {
            border-radius: $radius-xs;
            padding: $space-xs;
            box-sizing: border-box;
            background-color: $blue;
            color: $color-rev;
            flex: 0 0 60px;
        }
    }
}
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { Subscription } from 'rxjs';
import { ChatService } from 'src/app/services/chat.service';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
  @ViewChild('messageText') messageText: ElementRef = {} as ElementRef;
  @ViewChild('user') user: ElementRef = {} as ElementRef;
  @ViewChild('room') room: ElementRef = {} as ElementRef;
  public messagesArr: { user: string, message: string }[] = [];
  public userId: string = '';
  public roomId: string = '';
  private subs: Subscription = {} as Subscription;

  constructor(private chatService: ChatService) { }

  ngOnInit(): void {
    this.subs = this.chatService.getMessage().subscribe((data: IChatMessage) => {
      this.messagesArr.push(data);
    });
  }
  onUserId() {
    this.userId = this.user.nativeElement.value;
    if (this.userId !== '' && this.roomId !== '') {
      this.join(this.userId, this.roomId);
    }
  }
  onRoomId() {
    this.roomId = this.room.nativeElement.value;
    if (this.userId !== '' && this.roomId !== '') {
      this.join(this.userId, this.roomId);
    }
  }
  join(username: string, roomId: string): void {
    this.chatService.joinRoom({ user: username, room: roomId })
  }
  sendMessage(): void {
    if (this.roomId) {
      this.chatService.sendMessage({
        user: this.userId,
        room: this.roomId,
        message: this.messageText.nativeElement.value
      });
    }

    this.messagesArr
    this.messageText.nativeElement.value = '';
  }
  ngOnDestroy(): void {
    this.subs.unsubscribe();
  }
}
interface IChatMessage {
  user: string,
  message: string
}

זה הכל, יש לנו צ'ט בסיס ביותר שעובד 😁.

אתם מוזמנים לפתח אותו ולשנות אותו למשהו הרבה יותר מתקדם (ויפה 🐱‍👤 ).

פוסטים קשורים:

מדריך nodeJS Express – חלק 2 – controllers מדריך nodeJS Express – חלק 4 – middleware תמונת אווירה של דאטהאיך לשמור או לעדכן מערך של אובייקטים ב-mongoDb בשימוש nodeJs Express מיניון מחזיק בננהangular input and output in the same variable
angular express nodejs אנגולר

אודות המחבר

עידן יצחקי להציג את כל הפוסטים של עידן יצחקי


« פוסט קודם
פוסט הבא »

השארת תגובה

ביטול

חיפוש באתר
בחירת העורכים
29 בדצמבר 2023 עידן יצחקי

שדה טקסט עשיר עם תמונות

אתם הולכים להיות מופתעים עד כמה HTML יכול להיות חכם ולבצע משהו כל כך מורכב, שאם אנחנו היינו רוצים ליצור

1 באוקטובר 2021 עידן יצחקי

איך למשוך דינמית favicon של אתרים אחרים ב-JS

בפוסט זה נראה איך אפשר על פי לינקים בדף למשוך את ה-favicon מהדומיין שלהם באופן דינמי, בדיקה של תקינות התמונה

פופולרי
Javascript functions – היכרות עם סוגי פונקציות
Javascript
21 בדצמבר 2024 אין תגובות
Nested routing in angular standalone component
Typescript
15 בנובמבר 2024 אין תגובות
בחרו לפי תגיות
angular blockchain css ethers express front-end fullstack GQL html javascript next js nextjs nodejs react hooks reactjs solidity webgl אנגולר בלוקציין וורדפרס לימודי אנגולר לימודי וורדפרס לימוד ריאקט מדריך front-end מדריך GQL מדריך אנגולר מדריך וורדפרס מדריך חינם react מדריך ריאקט מפתח בלוק מפתח בלוקציין מתכנת front-end מתכנת בלוקציין מתכנת פרונט סולידיטי קורס front end קורס fullstack קורס nextjs קורס אנגולר קורס בלוקציין קורס בלוקציין בחינם קורס סולידיטי קורס ריאקט קורס תכנות קורס תכנות בחינם
סינון על פי קטגוריות
CSS fullstack HTML IIS Javascript nodeJs SEO Typescript אנגולר בלוקציין בניית אתרים וורדפרס חיפוש עבודה כלים נוספים כללי נגישות קורסים ריאקט תלת מימד תקלות ופתרונות
צור קשר
כל הזכויות שמורות לקודקודייל
ליצירת קשר: @ קודקודייל
גלילה לראש העמוד
דילוג לתוכן
פתח סרגל נגישות כלי נגישות

כלי נגישות

  • הגדל טקסטהגדל טקסט
  • הקטן טקסטהקטן טקסט
  • גווני אפורגווני אפור
  • ניגודיות גבוההניגודיות גבוהה
  • ניגודיות הפוכהניגודיות הפוכה
  • רקע בהיררקע בהיר
  • הדגשת קישוריםהדגשת קישורים
  • פונט קריאפונט קריא
  • איפוס איפוס