من و کیبردم یهویی

وبسایت شخصی سید معین حسینی (مستر دیمان)، برنامه نویس و نیمچه طراح تشنه پیشرفت!

Profile

آپلود در انگولار ۶+، آموزش ساخت آپلودر و نمایش درصد آپلود image

آموزش ساخت آپلودر انگولار

ساخت یک کامپوننت آپلود در انگولار(Uploader Component) می‌تواند کار سختی باشد. به این دلیل که با فایل ها در جاوا اسکریپت سر و کار داریم. در این آموزش یک روش خوب را برای آپلود فایل‌ها آموزش می‌دهم. به علاوه این که که چطور از روند آپلود در لحظه خبر داشته باشیم و با نمایش درصد داده‌های آپلود شده تجربه‌ی خوبی را برای کاربران فراهم کنیم.

نکته: در این آموزش من به مباحث سمت سرور و دریافت فایل توسط آن نمی‌پردازم و به این شکل در نظر میگیرم که شما سروری آماده‌ی دریافت فایل دارید. حالا در سمت کلاینت که انگولار ۶ یا بالاتر است، می‌خواهیم این فایل را با یک درخواست از نوع POST به سمت سرور بفرستیم و در حین آپلود گزارشی از روند آپلود در انگولار را نمایش دهیم. در این مورد من از کامپوننتی (Component) از انگولار متریال به نام نوار پیشرفت (Progress Bar) استفاده می‌کنم. می‌توانید نمونه نهایی این پروژه که برای آموزش ساخته شده را در مخزن گیت‌هاب مشاهده کنید.

ساخت پروژه انگولار

برای شروع، در دایرکتوری (Directory) مد نظر، پروژه‌ی انگولار خود را با استفاده از رابط خط فرمان انگولار (Angular CLI) به نام آپلودر می‌سازیم. پس دستور زیر را وارد می‌کنیم:

ng new uploader

وابستگی‌های خارجی

از آنجایی که ما نیاز به عناصر پیچیده‌ی رابط کاربری (UI)، مثل نوار پیشرفت داریم؛ تصمیم گرفتم تا از کتاب‌خانه‌ی انگولار متریال استفاده کنیم. برای نصب این کتاب‌خانه همچنین انیمیشن‌ها از دو دستور زیر استفاده کنید:

npm install --save @angular/material @angular/cdk
npm install --save @angular/animations

برای این که بتوانیم از سی‌اس‌اس (CSS) این ماژول استفاده کنیم، لازم است که آن را در فایل استایل سراسری خود وارد (Import) کنیم:

@import '~@angular/material/prebuilt-themes/deeppurple-amber.css';

ساخت ماژول (Module) امکانات

برای این که کامپوننت خوب ما تا حد ممکن قابلیت استفاده‌ی دوباره را داشته باشد، تصمیم گرفتم آن را در یک ماژول جدا بسته‌بندی کنم. برای ساخت این ماژول دستور زیر را استفاده کنید:

ng generate module upload

ساخت کامپوننت آپلود در انگولار

برای ساخت کامپوننت آپلود کافی است از دستور زیر استفاده کنید. همانطور که از نام این کامپوننت مشخص است، برای آپلود فایل‌های (File) خود از یک دیالوگ استفاده خواهیم کرد.

ng generate component upload/dialog

افزودن ماژول‌های خارجی

بعد، نیاز خواهیم داشت که تعداد زیادی ماژول خارجی را در ماژول جدید خود یعنی upload.module.ts وارد کنیم. برای مثال، نیاز داریم تا تمام عناصر رابط کاربری‌ای که استفاده خواهیم کرد را به این شکل وارد کنیم:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { DialogComponent } from './dialog/dialog.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import {
  MatButtonModule,
  MatDialogModule,
  MatListModule,
  MatProgressBarModule
} from '@angular/material';

@NgModule({
  imports: [
    CommonModule,
    HttpClientModule,
    BrowserAnimationsModule,
    MatButtonModule,
    MatDialogModule,
    MatListModule,
    MatProgressBarModule
  ],
  declarations: [DialogComponent],
  exports: [DialogComponent],
  entryComponents: [DialogComponent]
})
export class UploadModule { }

توجه کنید در صورتی که دقیقاً دستور‌های گفته شده را وارد کردید، کامپوننت شما توسط CLI در ماژول upload تعریف خواهد شد. درصورتی که شما از ماژولی استفاده نمی‌کنید، کامپوننت جدید شما در فایل app.module.ts تعریف می‌شود.

همچنین برای این که کامپوننت دیالوگ ما به خوبی کار کند لازم است تا آن را به عنوان EntryComponent در ماژول خود اضافه کنیم. این کار در ماژول نمونه بالا انجام شده است.

حالا برای این که بتوانیم از کامپوننت جدید خود استفاده کنیم باید ماژول تعریف شده را در app.module.ts وارد کنیم تا به  AppComponent اضافه شود.

سرویس آپلود در انگولار

قبل از ساخت محتوای نمایشی کامپوننت آپلودر، لازم است ابتدا منطق آپلود را پیاده‌سازی کنیم. این منطق در سرویس آپلود قرار خواهد گرفت. آن سرویس را با استفاده از دستور زیر ایجاد می‌کنیم:

ng generate service upload/upload

داخل این سرویس نیاز داریم تا از HttpClient استفاده کنیم. این سرویس تنها دارای یک تابع به نام upload خواهد بود و برای هر فایل یک Observable برمی‌گرداند. این Observable ها حاوی روند آپلود و درصد آن خواهد بود.

/**
* @description A Http request to upload files
* @param file
*/
upload(file: File): Observable<any> {

  const formData: FormData = new FormData();
  let url = '';
  
  formData.append('video', file, file.name);
  url = 'http://localhost:1500/api/upload/videos';

  const req = new HttpRequest('POST', url, formData, {
    reportProgress: true,
    responseType: 'text'
  });

  const progress = new Subject<any>();

  this.http.request(req).subscribe(event => {
    if (event.type === HttpEventType.UploadProgress) {
      const percentDone = Math.round(100 * event.loaded / event.total);
      progress.next({ percent: percentDone, loaded: event.loaded });
    } else if (event instanceof HttpResponse) {
      progress.complete();
    }
  });
  return progress.asObservable();
}

توجه داشته باشید که برای استفاده از این تابع باید موارد زیر را وارد کنید:

import { HttpClient, HttpEventType, HttpResponse, HttpRequest } from '@angular/common/http';
import { Observable, Subject } from 'rxjs';
همچنین نمونه‌ای از HttpClient را در سازنده (Constructor) کلاس تعریف می‌کنیم تا بتوانیم درخواست http ارسال کنیم:
constructor(private http: HttpClient) { }

در تابع آپلود، فایل دریافتی را در یک فرم بسته بندی کرده، یک درخواست http از نوع POST ساخته و آن درخواست را همراه با فرم به عنوان داده‌ی همراه درخواست ارسال می‌کنیم. سپس به روند آپلود فایل گوش می‌دهیم، درصد آپلود را محاسبه می‌کنیم و مقدار آن را به جریان داده پاس می‌دهیم. حالا برای فایل مورد نظر observable ای از روند آپلود را در اختیار داریم.

سرانجام نیاز داریم تا آن سرویس را به عنوان provider در ماژول خود اضافه کنیم. برای این کار کافی است سرویس خود را در فایل ماژول وارد (Import) کنیم (اگر توسط CLI وارد نشده بود) و آن را به این صورت در قسمت providers ماژول معرفی کنیم:

@NgModule({
  imports: [
    CommonModule,
    HttpClientModule,
    BrowserAnimationsModule,
    MatButtonModule,
    MatDialogModule,
    MatListModule,
    MatProgressBarModule
  ],
  declarations: [DialogComponent],
  exports: [DialogComponent],
  entryComponents: [DialogComponent],
  providers: [UploadService]
})
export class UploadModule { }

نکته: در پروژه نمونه برای این که در App Component از دکمه‌های متریال استفاده می‌شود، MatButtonModule را در قسمت exports هم قرار دادم.

باز کردن دیالوگ آپلودر

برای باز کردن دیالوگ در فایل ts کامپوننت اصلی یعنی App ماژول دیالوگ و کامپوننت دیالوگ خود را وارد ( import) می‌کنیم:

import { MatDialog } from '@angular/material';
import { DialogComponent } from './upload/dialog/dialog.component';

حالا در سازنده کلاس (constructor) نمونهٰ‌ای از MatDialog تعریف می‌کنیم:

constructor(public dialog: MatDialog) { }

با استفاده از تابع زیر می‌توانید دیالوگ ساخته شده توسط خود را باز کنید. این تابع را در کلاس کامپوننت App قرار می‌دهیم:

openDialog() {
  this.dialog.open(DialogComponent, {
    width: '720px'
  });
}

برای استفاده از این تابع تنها کافی است آن را به رویداد (event) کلیک روی دکمه بایند (Bind) کنیم. پس عبارت زیر را روی تگ دکمه‌ای که برای باز کردن دیالوگ در نظر گرفته‌ایم قرار می‌دهیم:

(click)="openDialog()"

اضافه کردن فایل‌ها

اولین کاری که باید در این قسمت انجام دهیم اضافه کردن یک عنصر input از نوع فایل به دیالوگ است. این عنصر input تنها راه تحریک منوی انتخاب فایل سیستم عامل برای باز شدن است.

<input type="file" #file style="display: none" (change)="onFilesAdded()" multiple />

اما برای این که ظاهر زشتی دارد قرار است که آن را با استفاده از CSS مخفی کنیم. سپس آن را با استفاده از رویداد کلیک، از طریق منطق کامپوننت باز می‌کنیم. برای این کار در فایل ts کامپوننت یک ارجاع به آن عنصر نیاز داریم. برای این کار از دایرکتیوی (directive) به نام ViewChild استفاده می‌کنیم. همچنین متغیری نیاز داریم تا فایل را در آن ذخیره کنیم.  همانطور که می‌بینید برای استفاده از سرویس آپلود نمونه‌ای از آن را در سازنده کلاس تعریف کرده‌ام.

import { Component, OnInit, ViewChild } from '@angular/core';
import { UploadService } from '../upload.service';
@Component({
  selector: 'app-dialog',
  templateUrl: './dialog.component.html',
  styleUrls: ['./dialog.component.scss']
})
export class DialogComponent implements OnInit {
  @ViewChild('file') file;
  addedFile;

  constructor(private uploadService: UploadService) { }
  ngOnInit() {
  }
}

حالا با شبیه‌سازی یک کلیک منوی انتخاب فایل سیستم عامل را باز می‌کنیم.

addFiles() {
  this.file.nativeElement.click();
}

طبق عنصر input که بالاتر نوشته‌ام، هنگامی که انتخاب فایل توسط کاربر انجام شد تابعی به نام onFilesAdded صدا زده می‌شود که شکل زیر را دارد:

 onFilesAdded() {
  this.addedFile = this.file.nativeElement.files[0];
  console.log('Added File:', this.addedFile);
}

حالا فایل ذخیره شده را داریم که آماده‌ی آپلود است برای آپلود تابعی را تعریف کرده و از سرویسی که قبلا تهیه کردیم استفاده می‌کنیم. برای آن که درصد فایل آپلود شده و مقدار داده‌ی آپلود شده را داشته باشیم دو متغیر جدید تعریف می‌کنیم و به شکل زیر درصد آپلود و مقدار داده آپلود شده را بدست می‌آوریم:

upload() {
  const progress = this.uploadService.upload(this.addedFile);

  progress.subscribe((result) => {
  this.percent = result.percent;
  this.loaded = result.loaded;
  });
}

حالا برای مثال می‌توانیم روند پیشرفت آپلود را به شکل زیر به نمایش بگذاریم:

<mat-progress-bar mode="determinate" [value]="percent"></mat-progress-bar>

نمونه‌ی پروژه‌ی آماده شده برای این آموزش را می‌توانید از اینجا دریافت کنید.

امیدوارم آموزش مفیدی بوده باشه. خوشحال می‌شوم نظرات شما را درباره این آموزش بدانم.

دسته بندی: آموزش، انگولار تاریخ: 2018/07/07 ساعت: 11:17

این پست ۳ دیدگاه دارد:

فرزاد کمالی میگه:

خیلی خوب بود, مرسی

hamid میگه:

پرچم بالاس !

hamid میگه:

chokhsan!

دیدگاه خود را بنویسید: