קודקודייל
  • קודקודייל
  • מי אתם קודקודייל?
  • קורסים בחינם
  • צרו קשר
  • בניית אתרים
    • וורדפרס
  • נגישות אתרים
  • כל הקטגוריות
    • אנגולר
    • HTML
    • CSS
    • Javascript
    • Typescript
    • NodeJs
    • בלוקציין
  • קודקודייל
  • מי אתם קודקודייל?
  • קורסים בחינם
  • צרו קשר
  • בניית אתרים
    • וורדפרס
  • נגישות אתרים
  • כל הקטגוריות
    • אנגולר
    • HTML
    • CSS
    • Javascript
    • Typescript
    • NodeJs
    • בלוקציין
קודקודייל
  • קודקודייל
  • מי אתם קודקודייל?
  • קורסים בחינם
  • צרו קשר
  • בניית אתרים
    • וורדפרס
  • נגישות אתרים
  • כל הקטגוריות
    • אנגולר
    • HTML
    • CSS
    • Javascript
    • Typescript
    • NodeJs
    • בלוקציין
  • קודקודייל
  • מי אתם קודקודייל?
  • קורסים בחינם
  • צרו קשר
  • בניית אתרים
    • וורדפרס
  • נגישות אתרים
  • כל הקטגוריות
    • אנגולר
    • HTML
    • CSS
    • Javascript
    • Typescript
    • NodeJs
    • בלוקציין
ראשי ♦ אנגולר ♦ Image file upload with preview in reactive form

Image file upload with preview in reactive form

עידן יצחקי 3 בספטמבר 2021 אין תגובות

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

נוסיף את HttpClientModule ואת ReactiveFormsModule ל- app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http'
import { ReactiveFormsModule } from '@angular/forms';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ImgUploaderComponent } from './components/img-uploader/img-uploader.component';

@NgModule({
  declarations: [
    AppComponent,
    ImgUploaderComponent,
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    ReactiveFormsModule,   
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

ניצור קומפוננטה חדשה בשם ImgUploaderComponent

ונאכלס אותה בקוד הבא:

<form [formGroup]="form" (submit)="onSavePost()">
    <div>
        <button class="file-select" type="button" (click)="filePicker.click()">Pick Image</button>
        <input type="file" #filePicker (change)="onImagePicked($event)">
    </div>
    <div class="image-preview" *ngIf="imagePreview!=='' && imagePreview && form.get('image')!.valid">
        <img [src]="imagePreview" [alt]="form.value.title">
    </div>
    <button color="primary" type="submit">Save File</button>
</form>
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { mimeType } from './mime-type.validator';
import { ImgService } from './img.service';
@Component({
  selector: 'app-img-uploader',
  templateUrl: './img-uploader.component.html',
  styleUrls: ['./img-uploader.component.scss']
})
export class ImgUploaderComponent implements OnInit {
  form: FormGroup = {} as FormGroup;
  imagePreview: string | ArrayBuffer = '';

  constructor(public imgService: ImgService) { }

  ngOnInit(): void {
    this.form = new FormGroup({
      'image': new FormControl(null, { validators: [Validators.required], asyncValidators: [mimeType] }),
    });
  }
  onImagePicked(event: any) {
    const file = event.target!.files[0];
    this.form.patchValue({ image: file });
    this.form.get('image')!.updateValueAndValidity();
    const reader = new FileReader();
    reader.onload = () => {
      this.imagePreview = reader.result!;
    };
    reader.readAsDataURL(file);
  }
  onSavePost() {
    if (this.form.invalid) {
      return;
    }    
    this.imgService.addImg(this.form.value.image);
    this.form.reset();
  }
}
input[type="file"] {
    visibility: hidden;
}

.image-preview {
    height: 5rem;
    margin: 1rem 0;
}

.image-preview img {
    height: 100%;
}

.file-select {
    background: transparent;
    padding: 10px 14px;
    margin: 10px;
    border: 1px solid steelblue;
    border-radius: 20px;
    cursor: pointer;
    &:hover {
        background: tan;
    }
}

[type="submit"] {
    background: transparent;
    padding: 10px 14px;
    margin: 10px;
    border: 1px solid green;
    border-radius: 20px;
    cursor: pointer;
    &:hover {
        background: lightseagreen;
    }
}

מיד נוסיף גם את הבדיקה של סוג הקובץ mimeType ואת הסרויס לשליחה לשרת ImgService.

לפני זה, נראה מה קורה במה שכתבנו עד עכשיו,

אם נסתכל על ה-HTML , נראה טופס עם הגדרות רגילות שמאפיינות טפסים בנוסף ניתן לראות את input מסוג file.

בגלל שלא ממש ניתן לעצב אותו כמו שרוצים, העלמנו אותו בעזרת ה-CSS ע"י visibility: hidden .

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

שימו לב שקשרנו בין הכפתורים על ידי כך שהוספנו משתנה לכפתור המוסתר filePicker עם # לפני.

לבסוף, הוספנו כפתור מסוג submit כדי לשלוח את הטופס.

בצד ה-TS:

יצרנו משתנה מסוג טופס בשם form , אתחלנו אותו והוספנו לו את image.

ל-image יש 2 בדיקות תקינות:

  1. בדיקת חובה – בדיקה סינכרונית.
  2. בדיקת סוג הקובץ – בדיקה לא סינכרונית, בגלל שלוקח זמן לקרוא את הקובץ ואנחנו לא רוצים לתקוע את הדף למשתמש.

בשורה 22 אנחנו מושכים את הקובץ במקום 0 מהסיבה שתמיד חוזר לנו מערך למקרה ונרצה לתת למשתמש את האפשרות לבחירה מרובה של קבצים.

במקרה שלנו יש בחירה יחידה ולכן אנחנו תמיד נמשוך את התא הראשון.

סימון הקריאה (!) נמצא שם מהסיבה ש-event.target יכול להחזיר NULL והקומפיילר מתריע מפני זה,

על ידי הוספת סימן הקריאה, אנחנו אומרים לקומפיילר שאנחנו יודעים ודואגים לזה שלא יהיה שם NULL (מה שנקרה… "האחריות עלינו").

בשורה 23 – אנחנו שומרים את המידע באובייקט פורם שלנו.

בשורה 24 – אנחנו מבקשים לבצע עידכן ובדיקה מחדש למצב הפורם ובדיקות האימות שלו.

כדי שנוכל להציג על הדף את התמונה שבחרנו אנחנו צריכים להפוך את המידע הזה ל- "URL" ולשלוח אותו כ-SRC לתגית IMG.

שורות 26 – 29 מטפלות בזה והופכות את המידע שיש בקובץ ל-DATA שהדף יכול להבין ולהראות.

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

עכשיו אנחנו יכולים להמשיך ולהוסיף את בדיקת סוג הקובץ.

השיטה הפשוטה היא לבדוק את הסיומת של הקובץ😂, אני סתם צוחק,

הרי את הסיומת המשתמש יכול לשנות ואז להעביר לנו קובץ מאיזה סוג שהוא רוצה.

אנחנו בודקים את המידע בתוך הקובץ, את זה יותר קשה לשנות ונעשה את זה כך

import { AbstractControl } from "@angular/forms";
import { Observable, Observer, of } from "rxjs";

export const mimeType = (control: AbstractControl): Promise<{ [key: string]: any }> | Observable<{ [key: string]: any }> | Observable<null> => {
    if (typeof (control.value) === 'string') {
        return of(null)
    }
    const file = control.value as File;
    const fileReader = new FileReader();
    const frObs = new Observable((observer: Observer<{ [key: string]: any }>) => {
        fileReader.addEventListener("loadend", () => {
            const arr = new Uint8Array(fileReader.result as ArrayBuffer).subarray(0, 4);
            let isValid: boolean = false;
            let header = "";
            for (let i = 0; i < arr.length; i++) {
                header += arr[i].toString(16);
            }
            switch (header) {
                case '89504e47':
                    isValid = true;
                    break;
                case "ffd8ffe0":
                case "ffd8ffe1":
                case "ffd8ffe2":
                case "ffd8ffe3":
                case "ffd8ffe8":
                    isValid = true;
                    break;
                default:
                    isValid = false;
                    break;
            }
            if (isValid) {
                observer.next(null);
            } else {
                observer.next({ invalidMineType: true });
            }
            observer.complete();
        });
        fileReader.readAsArrayBuffer(file);
    });
    return frObs;
};

אם הבדיקה יוצאת תקינה, אנחנו מחזירים NULL, כל דבר אחר שנחזיר נחשב לכישלון בבדיקה.

הבדיקה שאנחנו עושים היא לתחילת התוכן של הקובץ, אם הוא מתאים למקרה הראשון אז מדובר ב-png ואם הוא מתאים למקרה השני ועד השישי אז מדובר ב-JPG/JPEG .

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

עכשיו נוסיף את החלק האחרון שהוא הסרויס

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'
import { Router } from '@angular/router';

@Injectable({
  providedIn: 'root'
})
export class ImgService {

  constructor(private http: HttpClient, private router: Router) { }
  apiFile: string = '' //'http://localhost:3000/api/imgFiles'
  addImg(image: File) {
    const imgData = new FormData;
    imgData.append("image", image, 'newImgTitle');
    console.log(imgData);

    this.http.post<{ message: string }>(
      this.apiFile,
      imgData
    ).subscribe((respData) => {
       this.router.navigate(["/"]);
    })
  }
}

השירות די פשוט,

ב-apiFile נאחסן את המיקום שאליו נשמור/נשלח את התמונה.

נבצע POST ונצהיר שאנחנו מצפים לקבל חזרה הודעה מהשרת שתעדכן אותנו מה קרה (לא חייבים).

נוסיף לשליחה את המיקום לשליחה ואת המידע של הקובץ.

אחרי שהתהליך יסתיים ונקבל תשובה, נבצע ניתוב לדף הבית.

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

custom validator for group angularcustom validator for groups – אנגולר ולידטור ייחודי לגרופ ב-reactive form תמונת אווירה של חצים לכל הכיווניםבניה של טופס דינמי על פי מידע מהשרת באנגולר תמונת אווירה של ברווז לוחש לבובת דרקוןאנגולר Rxjs – BehaviorSubject in Service file מדריך אנגולגר טופס ריאקטיבימדריך אנגולר reactive form – יוצאים לדרך.
angular אנגולר

אודות המחבר

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


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

השארת תגובה

ביטול

חיפוש באתר
בחירת העורכים
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 אנגולר בלוקציין בניית אתרים וורדפרס חיפוש עבודה כלים נוספים כללי נגישות קורסים ריאקט תלת מימד תקלות ופתרונות
צור קשר
כל הזכויות שמורות לקודקודייל
ליצירת קשר: @ קודקודייל
גלילה לראש העמוד
דילוג לתוכן
פתח סרגל נגישות כלי נגישות

כלי נגישות

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