سازماندهی برنامههای Angular توسط ماژولها
ویژگی جدیدی به Angular 6 به نام tree-shakable providers اضافه شدهاست که نمونهای از کاربرد آن به صورت پیشفرض در حین «ایجاد پروژهی «کتابخانه» توسط Angular CLI 6.0» ارائه میشود. به این معنا که با استفادهی از آن دیگر نیازی نیست تا سرویسهای سراسری برنامه را در قسمت providers مربوط به CoreModule ثبت کرد. همینقدر که یک سرویس سراسری را به صورت ذیل تعریف کنید:
import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class MyCoreService { }
در این حالت تزریق وابستگیهایی که در مطلب «تزریق وابستگیها فراتر از کلاسها در برنامههای Angular» نیز بحث شدند، پشتیبانی میشود:
@Injectable({ providedIn: 'root', useClass: LazyFlightCancellingService, deps: [NgModuleFactoryLoader, Injector] }) export class FlightCancellingService { }
به عنوان مثال کدهای مخزن کد «اعتبارسنجی کاربران در برنامههای Angular» جهت استفادهی از این قابلیت به روز شد.
کلاسهای CSS اعتبارسنجی در Angular
زمانیکه Angular فرمی را تحت نظر قرار میدهد، کلاسهای CSS خاصی را نیز بر اساس حالات عناصر مختلف آن، به آنها متصل خواهد کرد. بر این اساس میتوان ظاهر این المانها را سفارشی سازی نمود. این کلاسها به شرح زیر هستند:
کلاس CSS اعتبارسنجی | توضیحات |
ng-untouched | زمانیکه فرمی برای بار اول رندر میشود، تمام فیلدهای آن با کلاس CSS ایی به نام ng-untouched علامتگذاری میشوند. |
ng-touched | همینقدر که کاربر با یک Tab از فیلدی عبور کند، با کلاس ng-touched مزین خواهد شد. بنابراین مهم نیست که حتما دادهای وارد شده باشد یا خیر. حتی عبور از یک فیلد نیز به معنای لمس آن نیز میباشد. |
ng-pristine | مربوط به زمانیاست که یک فیلد نه تغییر کردهاست و نه لمس شدهاست. |
ng-dirty | همینقدر که کاربر، تغییری را در فیلدی ایجاد کند، آن المان با کلاس ng-dirty مشخص خواهد شد. |
ng-valid | برای حالت موفقیت آمیز بودن اعتبارسنجی، به آن المان انتساب داده میشود. |
ng-invalid | برای حالت غیر موفقیت آمیز بودن اعتبارسنجی، به آن المان انتساب داده میشود. |
برای اینکه بتوانیم این موارد را در عمل مشاهده کنیم، به ابتدای فرم مثال این سری، تغییرات ذیل را اعمال خواهیم کرد:
<div class="form-group"> <label>First Name</label> <input #firstName required type="text" class="form-control" name="firstName" [(ngModel)]="model.firstName"> </div> <h3>Classes</h3> <h4>{{ firstName.className }}</h4>
تصویر فوق کلاسهایی را نمایش میدهد که در اولین بار نمایش فرم، به المان firstName متصل شدهاند. برای مثال در این حالت کلاس ng-pristine قابل مشاهدهاست و هنوز تغییری در آن حاصل نشدهاست.
در ادامه اگر حرفی را به آن اضافه کنیم:
هنوز هم ng-untouched آن برقرار است؛ اما ng-pristine آن به ng-dirty تبدیل شدهاست. در اینجا حتی اگر کل اطلاعات فیلد را نیز حذف کنیم و آنرا خالی کنیم یا به حالت اول بازگردانیم نیز کلاس ng-dirty قابل مشاهدهاست. بنابراین اگر حالت فیلدی dirty شد، همواره به همین حالت باقی میماند.
در این لحظه اگر با Tab به فیلد دیگری در فرم مراجعه کنیم:
در اینجا است که کلاس ng-untouched به ng-touched تبدیل میشود. بنابراین کلاسهای مختلف لمس یک فیلد، ارتباطی به افزوده شدن یا حذف کاراکتری از یک فیلد ندارند و فقط به از دست رفتن focus و مراجعهی به فیلد دیگری مرتبط میشوند.
اگر به المان تغییر یافتهی فوق دقت کنید، ویژگی required نیز به آن اضافه شدهاست (علاوه بر template reference variable ایی که تعریف کردیم). در این حالت کل فیلد را خالی کنید:
همانطور که مشاهده میکنید، اکنون کلاس ng-valid به کلاس ng-invalid تغییر یافتهاست.
ارتباط بین کلاسهای CSS اعتبارسنجی و خواص ngModel
تمام کلاسهای -ng ایی که در بالا معرفی شدند، معادلهای خواص ngModel ایی نیز دارند. فقط کافی است -ng آنها را حذف کنید، باقیماندهی آن، نام خاصیت متناظری در ngModel خواهد بود. برای مثال کلاس ng-untouched به خاصیت untouched نگاشت میشود و به همین ترتیب برای مابقی.
template reference variable ایی را که تا به اینجا به المان اضافه کردیم (firstName#) به خواص همان المان دسترسی دارد (مانند className آن). اما در ادامه میخواهیم این متغیر به ngModel و خواص آن دسترسی داشته باشد و میدان دید آن تغییر کند. به همین جهت تنها کافی است تا ngModel را به این متغیر انتساب دهیم:
<div class="form-group"> <label>First Name</label> <input #firstName="ngModel" required type="text" class="form-control" name="firstName" [(ngModel)]="model.firstName"> </div> <h4>dirty: {{ firstName.dirty }}</h4>
یک نکته: در حالت خواص valid و invalid که مرتبط با اعتبارسنجی هستند، خاصیت سومی نیز به نام errors وجود دارد که حاوی اطلاعات بیشتری در مورد خطای اعتبارسنجی رخ دادهاست. بنابراین وجود این خاصیت و نال نبودن آن نیز دلالت بر وجود یک خطای اعتبارسنجی است. از خاصیت errors در ادامهی بحث در قسمت «مدیریت چندین خطای همزمان اعتبارسنجی» استفاده خواهیم کرد.
نمایش بهتر خطاهای اعتبارسنجی با بررسی خواص ngModel
یکی از مزایای کار با خواص ngModel، امکان استفادهی از آنها در عبارات شرطیاست که نسبت به کلاسهای CSS معرفی شدهی در ابتدای بحث، انعطاف پذیری بیشتری را به همراه خواهند داشت.
<div class="form-group"> <label>First Name</label> <input #firstName="ngModel" #firstNameElement required type="text" class="form-control" name="firstName" [(ngModel)]="model.firstName"> <div *ngIf="firstName.invalid && firstName.touched" class="alert alert-danger"> First Name is required. </div> </div> <h4>className: {{ firstNameElement.className }}</h4> <h4>dirty: {{ firstName.dirty }}</h4> <h4>invalid: {{ firstName.invalid }}</h4>
نمایش بهتر خطاهای اعتبارسنجی با مزین ساختن المانهای ورودی
علاوه بر نمایش یک alert بوت استرپی متناظر با یک فیلد غیرمعتبر، میتوان خود المانهای ورودی را نیز با شیوهنامههایی مزین ساخت.
این کار را در بوت استرپ با افزودن کلاس has-error در کنار form-group انجام میدهند. همچنین label نیز باید به کلاس control-label مزین شود تا hass-error بر روی آن نیز تاثیرگذار شود. برای پیاده سازی پویای آن در Angular به روش ذیل عمل میشود:
<div class="form-group" [class.has-error]="firstName.invalid && firstName.touched"> <label class="control-label">First Name</label>
بنابراین اگر المان firstName خالی باشد و همچنین با یک Tab از روی آن عبور کرده باشیم، کلاس has-error در کنار کلاس form-group اضافه میشود.
روش دوم: همانطور که در ابتدای بحث نیز عنوان شد، Angular بر اساس حالات مختلف یک فیلد، کلاسهای CSS خاصی را به آنها انتساب میدهد. یک چنین کاری را با مقدار دهی این کلاسها در فایل src\styles.css نیز میتوان انجام داد که دقیقا معادل بررسی خواص invalid و touched با کدنویسی است:
.ng-touched.ng-invalid{ border: 1px solid red; }
سایر ویژگیهای اعتبارسنجی HTML 5
تا اینجا ویژگی استاندارد required را به المان ورودی فرم ثبت اطلاعات کاربران، اضافه کردیم.
<input #firstName="ngModel" #firstNameElement required maxlength="3" minlength="2" pattern="^V.*" type="text" class="form-control" name="firstName" [(ngModel)]="model.firstName">
برای نمونه minlength همهجا پشتیبانی نمیشود؛ اما آنرا میتوان برای مثال با الگویی مساوی "+..." جایگزین کرد.
مشکل! ذکر چند ویژگی اعتبارسنجی با هم، تداخل ایجاد میکنند!
در اینجا چون چهار ویژگی مختلف را به صورت یکجا به یک المان متصل کردهایم، اکنون div ذیل به هر کدام از این ویژگیها به صورت یکسانی واکنش نشان خواهد داد؛ زیرا خاصیت invalid را true میکنند:
<div *ngIf="firstName.invalid && firstName.touched" class="alert alert-danger"> First Name is required. </div>
<div class="form-group" [class.has-error]="firstName.invalid && firstName.touched"> <label class="control-label">First Name</label> <input #firstName="ngModel" #firstNameElement required maxlength="3" minlength="2" pattern="^V.*" type="text" class="form-control" name="firstName" [(ngModel)]="model.firstName"> <div *ngIf="firstName.invalid && firstName.touched"> <div class="alert alert-info"> errors: {{ firstName.errors | json }} </div> <div class="alert alert-danger" *ngIf="firstName.errors.required"> Name is required. </div> <div class="alert alert-danger" *ngIf="firstName.errors.minlength"> Name should be minimum {{firstName.errors.minlength.requiredLength}} characters. </div> <div class="alert alert-danger" *ngIf="firstName.errors.maxlength"> Name should be max {{firstName.errors.maxlength.requiredLength}} characters. </div> <div class="alert alert-danger" *ngIf="firstName.errors.pattern"> Name pattern: {{firstName.errors.pattern.requiredPattern}} </div> </div> </div>
همانطور که در تصویر ملاحظه میکنید، محتوای خاصیت errors به صورت JSON، جهت دیباگ نیز درج شدهاست.
بنابراین وجود خاصیت firstName.errors.minlength و یا firstName.errors.pattern به این معنا است که این خطاهای خاص وجود دارند (خاصیت firstName.errors به همراه اضافاتی است) و برعکس نال بودن آنها مؤید عدم وجود خطایی است. به همین جهت میتوان به ازای هر کدام، یک div جداگانه را تشکیل داد.
مرحلهی بعد، استخراج اطلاعات بیشتری از همین شیء و محتوای خاصیت errors است. برای مثال زمانیکه در آن خاصیت minlength ظاهر شد، این خاصیت نیز دارای خاصیتی مانند requiredLength است که از آن میتوان جهت درج عدد واقعی مورد نیاز این اعتبارسنج استفاده کرد.
بهبود اعتبارسنجی drop down
در حالت فعلی تعریف drop down مثال این سری، نیازی به اعتبارسنجی نیست؛ چون لیست مشخصی از طریق کامپوننت در اختیار این المان قرار میگیرد و همواره دقیقا یکی از این عناصر انتخاب خواهند شد. اما اگر گزینهی دیگری را مانند:
<option value="default">Select a Language...</option>
<div class="form-group" [class.has-error]="hasPrimaryLanguageError"> <label class="control-label">Primary Language</label> <select class="form-control" name="primaryLanguage" #primaryLanguage (blur)="validatePrimaryLanguage(primaryLanguage.value)" (change)="validatePrimaryLanguage(primaryLanguage.value)" [(ngModel)]="model.primaryLanguage"> <option value="default">Select a Language...</option> <option *ngFor="let lang of languages"> {{ lang }} </option> </select> </div>
export class EmployeeRegisterComponent { hasPrimaryLanguageError = false; validatePrimaryLanguage(value) { if (value === 'default'){ this.hasPrimaryLanguageError = true; } else{ this.hasPrimaryLanguageError = false; } } }
اعتبارسنجی در سطح کل فرم
تا اینجا بررسیهایی را که انجام دادیم، در سطح فیلدها بودند. اکنون اگر کاربر به طور کامل تمام این فیلدها را تغییر ندهد و بر روی دکمهی ارسال کلیک کند چطور؟
<form #form="ngForm" novalidate>
<h3> form.valid: {{ form.valid }}</h3>
<button class="btn btn-primary" type="submit" [disabled]="form.invalid">Ok</button>
در قسمت بعد نحوهی ارسال این فرم را به سرور، بررسی میکنیم.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: angular-template-driven-forms-lab-04.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کردهاید. سپس از طریق خط فرمان به ریشهی پروژه وارد شده و دستور npm install را صادر کنید تا وابستگیهای آن دریافت و نصب شوند. در آخر با اجرای دستور ng serve -o برنامه ساخته شده و در مرورگر پیش فرض سیستم نمایش داده خواهد شد.
npm install @angular/cdk
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { DragDropModule } from '@angular/cdk/drag-drop'; import { FormsModule } from '@angular/forms'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, FormsModule, DragDropModule ], bootstrap: [ AppComponent ] }) export class AppModule {}
<div cdkDrag> I'm Draggable </div>
<div cdkDrag cdkDragLockAxis="x"> I'm Draggable </div>
در ابتدا یک مدل را ایجاد میکنیم:
export interface Todo { title: string; type?: string; }
فایل app.component.ts
export class AppComponent implements OnInit { public title = 'Darg and drop'; public model: Todo; public todo: Todo[]; public done: Todo[]; public cancelled: Todo[]; ngOnInit(): void { this.setDefalutValue(); } addItem(form, $event: Event) { $event.preventDefault(); if (form.valid) { if (this.model.type === 'todo') { this.todo.push({ title: this.model.title }); } else { this.done.push({ title: this.model.title }); } } else { alert('فرم معتبر نمیباشد . عنوان را وارد نمایید'); } } drop(event: CdkDragDrop<Todo[]>) { if (event.previousContainer === event.container) { moveItemInArray(event.container.data, event.previousIndex, event.currentIndex); } else { transferArrayItem(event.previousContainer.data, event.container.data, event.previousIndex, event.currentIndex); } } private setDefalutValue() { this.todo = [ { title: 'خرید مواد غذایی' }, { title: 'رفتن به خانه' }, { title: 'خوابیدن' } ]; this.done = [ { title: 'بیدار شدن' }, { title: 'مسواک زدن' }, { title: 'دوش گرفتن' }, { title: 'چک کردن ایمیل' } ]; this.cancelled = []; this.model = { title: null, type: 'todo' }; } }
در اینجا 3 آرایه یکی برای to-do و یکی برای Done و دیگری برای Cancelled ایجاد میکنیم و به هر کدام تعدادی آیتم را اضافه میکنیم.
addItem : زمانیکه فرم را submit میکنیم اجرا میشود .
drop : زمانی اجرا میشود که یک آیتم را Drag and Drop کنیم. در ایجا شرط event.previousContainer === event.container زمانی درست است که جابجایی در درون یک لیست باشد و هدف در این صورت، مرتب سازی است .
moveItemInArray : ایندکس آیتمها را در همان لیست تغیر میدهد (مرتب سازی).
transferArrayItem: آیتم را از یک لیست حذف و به لیست دیگری اضافه میکند.
فایل app.component.html
<div> <!-- فرم --> <div> <fieldset> <legend> اضافه کردن آیتم جدید </legend> <form #form="ngForm" (submit)="addItem(form,$event)"> <label></label> <input type="text" required name="title" #name="ngModel" [(ngModel)]="this.model.title"> <label></label> <select required name="type" #type="ngModel" [(ngModel)]="this.model.type"> <option value="todo"> انجام دادن </option> <option value="done"> انجام شده </option> </select> <input type="submit" value="ذخیره"> </form> </fieldset> </div> <!-- آیتمها --> <div> <fieldset> <legend> لیست آیتمها </legend> <div> <!-- انجام دادن --> <div> <p> انجام دادن </p> <div cdkDropList #todoList="cdkDropList" [cdkDropListData]="todo" [cdkDropListConnectedTo]="[doneList, cancelledList]" (cdkDropListDropped)="drop($event)"> <div *ngFor="let item of todo" cdkDrag> <p> {{ item.title | titlecase }} </p> </div> </div> </div> <!-- انجام شده --> <div> <p> انجام شده </p> <div cdkDropList #doneList="cdkDropList" [cdkDropListData]="done" [cdkDropListConnectedTo]="[todoList, cancelledList]" (cdkDropListDropped)="drop($event)"> <div *ngFor="let item of done" cdkDrag> <p> {{ item.title | titlecase }} </p> </div> </div> </div> <!-- انجام نشده --> <div> <p> انجام نشده </p> <div cdkDropList #cancelledList="cdkDropList" [cdkDropListData]="cancelled" [cdkDropListConnectedTo]="[todoList, doneList]" (cdkDropListDropped)="drop($event)"> <div *ngFor="let item of cancelled" cdkDrag> <p> {{ item.title | titlecase }} </p> </div> </div> </div> </div> </fieldset> </div> </div>
در ابتدا یک فرم داریم که در اینجا همه چیز مشخص است ( فرمهای مبتنی بر قالبها در Angular )
در ادامه 3 container را ایجاد میکنیم یکی برای to-do و یکی برای Done و در آخر یکی برای Cancelled
container ایجاد شده برای to-do :
<div cdkDropList #todoList="cdkDropList" [cdkDropListData]="todo" [cdkDropListConnectedTo]="[doneList, cancelledList]" (cdkDropListDropped)="drop($event)"> <div *ngFor="let item of todo" cdkDrag> <p> {{ item.title | titlecase }} </p> </div> </div>
توضیحات:
cdkDropList : یک container میباشد، برای آیتمهایی که قرار است Drag and Drop شوند.
todoList #:
id مربوط به container را مشخص میکند.
cdkDropListConnectedTo:
id مربوط به container های دیگری که میتواند آیتم های container جاری را بپذیرد.
cdkDropListData: مشخص کنند منبع داده است.
cdkDropListDropped: این رویداد زمانی اجرا میشود که Drag and Drop برای یک آیتم انجام شود.
cdkDrag: برای اینکه آیتمهای درون یک container قابلیت Drag and Drop را داشته باشند، این دایرکتیو را اضافه میکنیم.
تمام !
استفاده از Chart.js در Blazor
This is a Blazor library that wraps Chart.js. You can use it in both client- and server-side projects.
Hello! I work for the Angular core team on internationalization (i18n) and we are trying to get a better idea of the things that we need to improve/focus on in Angular regarding i18n. I created this survey: https://goo.gl/forms/0BIXXVP58RqbzzBM2 If you care about internationalization, please take 5mn to do it! Thanks!
نگاهی به map و set در ECMAScript 6
پیشنیازهای کار با کامپوننت ng2-file-upload
برای شروع به کار با کامپوننت ng2-file-upload، ابتدا نیاز است بستهی npm آنرا نصب کرد:
>npm install ng2-file-upload --save
همچنین یک کامپوننت آزمایشی را هم به برنامه (دقیقا همان مثال مطلب قبلی) جهت اعمال آن اضافه میکنیم:
>ng g c UploadFile/ng2-file-upload-test
پس از آن نیاز است به ماژولی که این کامپوننت جدید در آن قرار دارد، مدخل FileUploadModule کامپوننت ng2-file-upload را افزود:
import { FileUploadModule } from "ng2-file-upload"; @NgModule({ imports: [ FileUploadModule ]
تکمیل Ng2FileUploadTestComponent جهت اعمال ng2-file-upload
اکنون به کلاس کامپوننت جدیدی که ایجاد کردیم، مراجعه کرده و تغییرات ذیل را اعمال میکنیم:
import { FileUploader, FileUploaderOptions } from "ng2-file-upload"; import { Ticket } from "./../ticket"; export class Ng2FileUploadTestComponent implements OnInit { fileUploader: FileUploader; model = new Ticket();
وهله سازی از کامپوننت ng2-file-upload و انجام تنظیمات اولیهی آن
پس از تعریف خاصیت عمومی fileUploader، اکنون نوبت به وهله سازی آن است:
this.fileUploader = new FileUploader( <FileUploaderOptions>{ url: "api/SimpleUpload/SaveTicket", headers: [ { name: "X-XSRF-TOKEN", value: this.getCookie("XSRF-TOKEN") }, { name: "Accept", value: "application/json" } ], isHTML5: true, // allowedMimeType: ["image/jpeg", "image/png", "application/pdf", "application/msword", "application/zip"] allowedFileType: [ "application", "image", "video", "audio", "pdf", "compress", "doc", "xls", "ppt" ], removeAfterUpload: true, autoUpload: false, maxFileSize: 10 * 1024 * 1024 } );
- اگر برنامه از نکات anti-forgery token استفاده میکند، این کامپوننت برخلاف روش مطرح شدهی در مطلب مشابه قبلی، هیچ هدری را به سمت سرور ارسال نمیکند. بنابراین نیاز است کوکی مرتبط را خودمان یافته و سپس به لیست هدرها اضافه کنیم. در اینجا روش استخراج یک کوکی را توسط کدهای جاوا اسکریپتی مشاهده میکنید:
getCookie(name: string): string { const value = "; " + document.cookie; const parts = value.split("; " + name + "="); if (parts.length === 2) { return decodeURIComponent(parts.pop().split(";").shift()); } }
- برای محدود سازی فایلهای ارسالی توسط این کامپوننت، دو روش وجود دارد:
الف) مشخص سازی مقدار خاصیت allowedMimeType
همانطور که مشاهده میکنید، در اینجا باید mime type فایلهای مجاز را مشخص کرد.
ب) مشخص سازی مقدار خاصیت allowedFileType
برخلاف تصور، در اینجا از پسوند فایلها استفاده نمیکند و از یک لیست از پیش مشخص که نمونهای از آنرا در اینجا مشاهده میکنید، کمک گرفته میشود. بنابراین اگر برای مثال تنها نیاز به ارسال تصاویر بود، مقدار image را نگه داشته و مابقی را از لیست حذف کنید.
- removeAfterUpload به این معنا است که آیا لیست نهایی که نمایش داده میشود، پس از آپلود باقی بماند یا خیر؟
- توسط خاصیت maxFileSize میتوان حداکثر اندازهی قابل قبول فایلهای ارسالی را مشخص کرد.
مدیریت رخدادهای کامپوننت ng2-file-upload
اکنون که وهلهای از این کامپوننت ساخته شدهاست، میتوان رخدادهای آنرا نیز مدیریت کرد. برای مثال:
الف) نحوهی ارسال اطلاعات اضافی به همراه یک فایل به سمت سرور
this.fileUploader.onBuildItemForm = (fileItem, form) => { for (const key in this.model) { if (this.model.hasOwnProperty(key)) { form.append(key, this.model[key]); } } };
ب) اطلاع یافتن از رخداد خاتمهی کار
رخداد onCompleteAll پس از ارسال تمام فایلها به سمت سرور فراخوانی میشود:
this.fileUploader.onCompleteAll = () => { // clear the form // this.model = new Ticket(); };
ج) در حین وهله سازی fileUploader، تعدادی محدودیت نیز قابل اعمال هستند. این محدودیتها سبب نمایش هیچگونه پیام خطایی نمیشوند. فقط زمانیکه کاربر فایلی را انتخاب میکند، این فایل در لیست ظاهر نمیشود. اگر علاقمند به مدیریت این وضعیت باشید، میتوان از رخداد onWhenAddingFileFailed استفاده کرد:
this.fileUploader.onWhenAddingFileFailed = (item, filter, options) => { // msg: `You can't select ${item.name} file because of the ${filter.name} filter.` };
د) اگر ارسال فایلی به سمت سرور با شکست مواجه شود، در رخدادگردان onErrorItem میتوان به نام این فایل و اطلاعات بیشتری که از سمت سرور دریافت شدهاست، دسترسی یافت:
this.fileUploader.onErrorItem = (fileItem, response, status, headers) => { // };
ه) اگر از سمت سرور اطلاعات JSON مانندی یا هر اطلاعات دیگری به سمت کلاینت پس از آپلود ارسال میشود، این اطلاعات را میتوان در رخدادگردان onSuccessItem دریافت کرد:
this.fileUploader.onSuccessItem = (item, response, status, headers) => { if (response) { const ticket = JSON.parse(response); console.log(`ticket:`, ticket); } };
ارسال نهایی فرم و فایلها به سمت سرور
در پایان، با فراخوانی متد uploadAll شیء fileUploader جاری، میتوان اطلاعات فرم و تمام فایلهای آنرا به سمت سرور ارسال کرد:
submitForm(form: NgForm) { this.fileUploader.uploadAll(); // NOTE: Upload multiple files in one request -> https://github.com/valor-software/ng2-file-upload/issues/671 }
کدهای کامل کامپوننت ng2-file-upload-test.component.ts را در اینجا میتوانید مشاهده کنید.
تکمیل قالب کامپوننت Ng2FileUploadTestComponent
اکنون که کار تکمیل کامپوننت آزمایشی ارسال فایلها به سمت سرور به پایان رسید، نوبت به تکمیل قالب آن است.
افزودن فیلد اضافی توضیحات به فرم
<div class="container"> <h3>Support Form(ng2-file-upload)</h3> <form #form="ngForm" (submit)="submitForm(form)" novalidate> <div class="form-group" [class.has-error]="description.invalid && description.touched"> <label class="control-label">Description</label> <input #description="ngModel" required type="text" class="form-control" name="description" [(ngModel)]="model.description"> <div *ngIf="description.invalid && description.touched"> <div class="alert alert-danger" *ngIf="description.errors.required"> description is required. </div> </div> </div>
تعریف ویژهی فیلد ارسال فایلها به سمت سرور
<div class="form-group"> <label class="control-label">Screenshot(s)</label> <input required type="file" multiple ng2FileSelect [uploader]="fileUploader" class="form-control" name="screenshot"> </div>
ذکر ویژگی استاندارد multiple را نیز در اینجا مشاهده میکنید. وجود آن سبب خواهد شد تا کاربر بتواند چندین فایل را با هم انتخاب کند. اگر نیازی به ارسال چندین فایل نیست، این ویژگی را حذف کنید.
نمایش لیست فایلها و نمایش درصد پیشرفت آپلود آنها
جدولی را که در تصویر ابتدای بحث مشاهده کردید، به صورت ذیل شکل میگیرد (کدهای آن در همان صفحهی توضیحات کامپوننت نیز موجود هستند):
<div style="margin-bottom: 10px" *ngIf="fileUploader.queue.length"> <h3>Upload queue</h3> <p>Queue length: {{ fileUploader?.queue?.length }}</p> <table class="table"> <thead> <tr> <th width="50%">Name</th> <th>Size</th> <th>Progress</th> <th>Status</th> <th>Actions</th> </tr> </thead> <tbody> <tr *ngFor="let item of fileUploader.queue"> <td><strong>{{ item?.file?.name }}</strong></td> <td nowrap>{{ item?.file?.size/1024/1024 | number:'.2' }} MB</td> <td> <div class="progress" style="margin-bottom: 0;"> <div class="progress-bar" role="progressbar" [ngStyle]="{ 'width': item.progress + '%' }"></div> </div> </td> <td class="text-center"> <span *ngIf="item.isError"><i class="glyphicon glyphicon-remove"></i></span> </td> <td nowrap> <button type="button" class="btn btn-danger btn-xs" (click)="item.remove()"> <span class="glyphicon glyphicon-trash"></span> Remove </button> </td> </tr> </tbody> </table> <div> <div> Queue progress: <div class="progress"> <div class="progress-bar" role="progressbar" [ngStyle]="{ 'width': fileUploader.progress + '%' }"></div> </div> </div> <button type="button" class="btn btn-danger btn-s" (click)="fileUploader.clearQueue()" [disabled]="!fileUploader.queue.length"> <span class="glyphicon glyphicon-trash"></span> Remove all </button> </div> </div>
در اینجا از progress-bar بوت استرپ برای نمایش درصد آپلود فایلها استفاده شدهاست:
<div class="progress" style="margin-bottom: 0;"> <div class="progress-bar" role="progressbar" [ngStyle]="{ 'width': item.progress + '%' }"></div> </div>
غیرفعال کردن دکمهی ارسال، در صورت عدم انتخاب یک فایل
<button class="btn btn-primary" [disabled]="form.invalid || !fileUploader.queue.length" type="submit">Submit</button> </form>
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.
Angular
1.3 might be the best Angular yet, but there's still lots of high impact work
to be done on the 1.x branch. We still have Material Design, our new router,
and improved internationalization of Angular apps still to come ...
The Angular 1.x project needs someone to be fully committed to keeping v1 moving forward, and supporting the Angular ecosystem that has grown around it.
That's why I'm delighted that Pete Bacon Darwin has agreed to take over the leadership role for Angular v1.
But Pete can't do this
alone. He needs Brian, Caitlin, and Chirayu to help make v1 even more awesome.
Jeff will help out with google3 sync and releases, and familiar faces like
Matias Niemela and Pawel Kozlowski (who co-wrote one of the first books on Angular with
Pete) will be ongoing contributors. We're also looking forward to meeting new
faces. Over time, some of these folks will shift their focus towards Angular 2,
but for the immediate future, what's most important is that Angular 1.x is and
continues to be well taken care of.