اشتراک‌ها
معرفی پیش نمایش TypeScript 2.0

For TypeScript 2.0, the current road map consists of:
    • Non-nullable types
    • Control flow based typed analysis
    • Async/await downlevel support
    • Readonly properties
    • Declared 'this' type in functions
    • Improved typings acquisition

معرفی پیش نمایش TypeScript 2.0
اشتراک‌ها
Ember.js 2.0 منتشر شد

Ember 2.0 is not a traditional major release. After thirteen point releases in almost two years, we're taking a turn to focus entirely on sweeping out built-up cruft as a foundation for continued progress.

Ember.js 2.0 منتشر شد
اشتراک‌ها
دوره 3 ساعته NET MAUI.

.NET MAUI Course for Beginners – Create Cross-Platform Apps with C#

Learn how to use .NET MAUI for native cross-platform desktop and mobile development! You will learn the essentials of building mobile applications with .NET MAUI and C# while creating a Contacts app.

⭐️ Contents ⭐️
⌨️ (0:00:00) Introduction
⌨️ (0:03:42) What is .Net Maui - .Net Maui vs Xamarin Forms
⌨️ (0:06:52) Prepare Development Environment _ Create first project
⌨️ (0:12:29) Project Structure
⌨️ (0:20:28) Three elements of stateful .Net Maui
⌨️ (0:23:51) Page, Layout _ View, Namespaces
⌨️ (0:33:02) URL based navigation
⌨️ (0:51:10) Basics of ListView and Data Binding
⌨️ (1:05:58) Events Handling of ListView
⌨️ (1:16:54) Parameters in URL based Navigation _ Static Repository
⌨️ (1:35:35) Stacklayout for Edit Contact page
⌨️ (1:52:47) View Contact Details _ Update Contact
⌨️ (2:06:40) Observable Collection
⌨️ (2:14:58) Field Validation with .Net Maui CommunityToolkit
⌨️ (2:27:08) Reusable Control
⌨️ (2:40:37) Grid Layout and  Use reusable control
⌨️ (2:53:23) ContextActions _ MenuItems in ListView
⌨️ (3:03:44) SearchBar in .NetMaui 

دوره 3 ساعته NET MAUI.
اشتراک‌ها
سری آموزشی Blazor C# Tutorials

Blazor C# Tutorials
30 videos

In this playlist, I am going through all the fundamentals and sharing my journey to be a full stack Blazor developer. This is the future of web development in ASP.NET world. If you want to learn Blazor this is the best place to start.

1. Build Your First App - EP01
2. Getting Started - EP02
3. #Routing - EP03
4. Dependency #Injection - EP04
5. Forms & #Validations - EP05
6. JavaScript #Interop - EP06
7. #Razor #Components | Re-usability - EP07
8. Razor Components | #Lifecycle Methods - EP08
9. Razor Component #Libraries - EP09
10. Call #REST #API - #CRUD Methods - EP10
11. #Authentication | Out of the box- EP11
12. Custom AuthenticationStateProvider - EP12
13. Layouts | Login Pages - EP13
14.  HttpClient | Login User
15. IHttpClientFactory | Login User
16. Sending JWT token & Request Middleware
17. Handling Exceptions 

سری آموزشی Blazor C# Tutorials
نظرات اشتراک‌ها
معرفی کتابخانه‌ی DNTCaptcha.Core
بعد از آپدیت پروژه به .NET 5 مشکلی که بوجود اومد این بود که کپچا نمایش داده میشه ولی همیشه پیغام کد امنیتی اشتباه رو بر می‌گردونه در صورتیکه کد درسته. برای حالت توسعه و localhost این مشکل بوجود اومد و مربوط به اینه که کوکی مربوط به کپچا داخل مرورگر ایجاد نمیشه. البته بیشتر بررسی کردم و مشخص شد مربوط به ویژگی same-site مرورگر هستش و ظاهرا در آپدیت‌های اخیر کروم این ویژگی بصورت پیش فرض روی بالاترین حالت سخت گیری تنظیم میشه. برای حل مشکل یا باید ویژگی same-site برای مرورگر غیرفعال بشه یا از ssl برای localhost استفاده بشه. البته اگر راه حلی دیگه ای هم هست لطفا بفرمایید. پروژه من webapi و Angular هستش.
اشتراک‌ها
تفاوت بین NET Core 2.0. و NET Standard 2.0. در چیست؟

NET Standard‌. در حقیقت یک قرار داد است که سکوهای کاری مختلف دات نتی مانند Full .NET Framework ، Xamarin ، Mono ، UWP و غیره می‌توانند آن‌را پیاده سازی کنند. یک نمونه‌ی دیگر این پیاده سازی‌ها نیز NET Core. است. برای مثال دات نت 4.6.1، استاندارد و قرار داد شماره‌ی 2 دات نت را پیاده سازی می‌کند. به همین صورت NET Core 2.0. نیز پیاده سازی کننده‌ی این استاندارد شماره 2 است.
«اما» باید درنظر داشت که دات نت استاندارد، بیش از یک قرار داد چیزی نیست و پیاده سازی کنندگان آن می‌توانند سطح بیشتری را نسبت به این قرار داد نیز لحاظ کنند. برای مثال دات نت 4.6.1 شامل سطح API بیشتری از دات نت استاندارد 2 است.
 
 با تغییرات اخیر، اکنون NuGet می‌تواند کتابخانه‌های مبتنی بر NET Standard 2. را در برنامه‌های مبتنی بر سکوهای کاری که آن‌را پیاده سازی می‌کنند، بدون مشکل اضافه کند. برای مثال می‌توان اسمبلی‌های دات نت 4.6.1 را به برنامه‌های ASP.NET Core 2.0 اضافه کرد (کاری که در نگارش 1x آن به صورت مستقیم میسر نیست) و یا می‌توان اسمبلی‌های کامپایل شده‌ی برای دات نت استاندارد 2 را به برنامه‌های مبتنی بر دات نت 4.6.1 اضافه کرد.
 
«اما» باید درنظر داشت که امکان اضافه کردن یک بسته‌ی نیوگت از یک کتابخانه‌ی نوشته شده‌ی برای دات نت کامل در برنامه‌های دات نت Core به معنای تضمینی برای کار کردن آن در زمان اجرا نخواهد بود. از این جهت که دات نت کامل، به همراه قسمت‌هایی است که در NET Standard. وجود خارجی ندارند. بنابراین اگر کتابخانه‌ی استفاده شده صرفا این API مشترک را هدف قرار داده‌است، هم قابلیت اتصال و هم قابلیت اجرا را خواهد داشت؛ اما اگر برای مثال کسی بسته‌ی NServiceBus را به پروژه‌ی ASP.NET Core 2.0 اضافه کند، بدون مشکل کامپایل خواهد شد. اما از آنجائیکه این کتابخانه از MSMQ استفاده می‌کند که خارج از میدان دید این استاندارد است، در زمان اجرا با شکست مواجه خواهد شد.

تفاوت بین NET Core 2.0. و NET Standard 2.0. در چیست؟
مطالب
Angular Material 6x - قسمت سوم - طرحبندی برنامه
پس از نصب بسته‌ی Angular Material و آشنایی با سیستم Angular Flex Layout برای پوشش کمبود سیستم طرحبندی آن، در این قسمت طرح ابتدایی دفترچه تلفن این سری را پیگیری می‌کنیم تا به طراحی زیر برای حالت‌های دسکتاپ و موبایل برسیم:



در اینجا توسط کامپوننت sidenav، کار نمایش لیست تماس‌ها صورت می‌گیرد و نمایش این کامپوننت واکنشگرا است. به این معنا که در اندازه‌های صفحات نمایشی بزرگ، نمایان است و در صفحات نمایشی کوچک، مخفی خواهد شد. در بالای صفحه یک Toolbar قرار دارد که همیشه نمایان است و از آن برای نمایش گزینه‌های منوی برنامه استفاده می‌کنیم. همچنین ناحیه‌ی main content را هم مشاهده می‌کنید که با انتخاب هر شخص از لیست تماس‌ها، جزئیات او در این قسمت نمایش داده خواهد شد.


ایجاد ماژول مدیریت تماس‌ها

در قسمت اول، برنامه را به همراه تنظیمات ابتدایی مسیریابی آن ایجاد کردیم که نتیجه‌ی آن تولید فایل src\app\app-routing.module.ts می‌باشد:
 ng new MaterialAngularClient --routing
در ادامه ماژول مخصوص مدیریت تماس‌ها را ایجاد می‌کنیم که به آن feature module هم گفته می‌شود. برای این منظور دستور زیر را اجرا کنید:
 ng g m ContactManager -m app.module --routing


این دستور ماژول جدید contact-manager را به همراه تنظیمات ابتدایی مسیریابی و همچنین به روز رسانی app.module، برای درج آن، ایجاد می‌کند. البته در این حالت نیاز است به app.module.ts مراجعه کرد و محل درج آن‌را تغییر داد:
import { ContactManagerModule } from "./contact-manager/contact-manager.module";

@NgModule({
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    CoreModule,
    SharedModule.forRoot(),
    ContactManagerModule,
    AppRoutingModule
  ],
})
export class AppModule { }
به صورت پیش‌فرض ContactManagerModule، پس از AppRoutingModule ذکر می‌شود و چون مسیر catch all را در ادامه در AppRoutingModule قرار می‌دهیم، دیگر هیچگاه مسیریابی‌های ContactManagerModule پردازش نخواهند شد. به همین جهت باید آن‌را پیش از AppRoutingModule قرار داد.
سپس دستور زیر را اجرا می‌کنیم تا کامپوننت contact-manager-app در ماژول contact-manager ایجاد شود:
 ng g c contact-manager/ContactManagerApp --no-spec
با این خروجی:
CREATE src/app/contact-manager/contact-manager-app/contact-manager-app.component.html (38 bytes)
CREATE src/app/contact-manager/contact-manager-app/contact-manager-app.component.ts (319 bytes)
CREATE src/app/contact-manager/contact-manager-app/contact-manager-app.component.css (0 bytes)
UPDATE src/app/contact-manager/contact-manager.module.ts (436 bytes)
همانطور که ملاحظه می‌کنید این دستور کار به روز رسانی contact-manager.module را نیز جهت معرفی این کامپوننت جدید انجام داده‌است.
این کامپوننت به عنوان میزبان سایر کامپوننت‌هایی که در مقدمه‌ی بحث عنوان شدند، عمل می‌کند. این کامپوننت‌ها را به صورت زیر در پوشه‌ی components ایجاد می‌کنیم:
ng g c contact-manager/components/toolbar --no-spec
ng g c contact-manager/components/main-content --no-spec
ng g c contact-manager/components/sidenav --no-spec
با این خروجی:



تنظیمات مسیریابی برنامه

در ادامه به src\app\app-routing.module.ts مراجعه کرده و این ماژول جدید را به صورت lazy load معرفی می‌کنیم:
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";

const routes: Routes = [
  { path: "contactmanager", loadChildren: "./contact-manager/contact-manager.module#ContactManagerModule" },
  { path: "", redirectTo: "contactmanager", pathMatch: "full" },
  { path: "**", redirectTo: "contactmanager" }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }
در اینجا تنظیمات صفحه‌ی پیش‌فرض برنامه و همچنین not found و یا catch all را نیز مشاهده می‌کنید که هر دو به contactmanager تنظیم شده‌اند.

سپس تنظیمات مسیریابی ماژول مدیریت تماس‌ها را در فایل src\app\contact-manager\contact-manager-routing.module.ts به صورت زیر تغییر می‌دهیم:
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";

import { MainContentComponent } from "./components/main-content/main-content.component";
import { ContactManagerAppComponent } from "./contact-manager-app/contact-manager-app.component";

const routes: Routes = [
  {
    path: "", component: ContactManagerAppComponent,
    children: [
      { path: "", component: MainContentComponent }
    ]
  },
  { path: "**", redirectTo: "" }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class ContactManagerRoutingModule { }
در اولین بار بارگذاری این ماژول، کامپوننت ContactManagerApp بارگذاری می‌شود. همچنین مسیر not found نیز به همان مسیر ریشه‌ی این کامپوننت تنظیم شده‌است.
کامپوننت ContactManagerApp که کار هاست سایر کامپوننت‌های این ماژول را بر عهده دارد، دارای router-outlet خاص خود خواهد بود. به همین جهت برای آن children تعریف شده‌است که مسیر پیش‌فرض آن، بارگذاری کامپوننت MainContent است.
بنابراین نیاز است به فایل contact-manager-app\contact-manager-app.component.html مراجعه و ابتدا منوی کنار صفحه را به آن افزود:
 <app-sidenav></app-sidenav>
app-sidenav همان selector کامپوننت sidenav است که در فایل sidenav\sidenav.component.ts قابل مشاهده‌است.
سپس در قالب sidenav\sidenav.component.html، کار تعریف toolbar و همچنین router-outlet را انجام می‌دهیم:
<app-toolbar></app-toolbar>
<router-outlet></router-outlet>
بر اساس مسیریابی که تعریف کردیم، router-outlet کار نمایش Main Content را انجام می‌دهد.


معرفی Angular Material به ماژول جدید مدیریت تماس‌ها

در قسمت اول، یک فایل material.module.ts را ایجاد کردیم که به همراه تعریف تمامی کامپوننت‌های Angular Material بود. سپس آن‌را به shared.module.ts افزودیم که حاوی تعریف ماژول فرم‌ها و همچنین Flex Layout نیز هست. به همین جهت برای معرفی این‌ها به این ماژول جدید تنها کافی است در فایل src\app\contact-manager\contact-manager.module.ts در قسمت imports، کار معرفی SharedModule صورت گیرد:
import { SharedModule } from "../shared/shared.module";

@NgModule({
  imports: [
    CommonModule,
    SharedModule,
    ContactManagerRoutingModule
  ]
})
export class ContactManagerModule { }
تا اینجا پیش از ادامه‌ی کار، فرمان ng serve -o را صادر کنید تا مطمئن شویم همه چیز به درستی قابل دریافت و کامپایل است.


پس از اجرای برنامه مشاهده می‌کنید که ابتدا ماژول مدیریت تماس‌ها بارگذاری شده‌است و سپس contact-manager-app عمل و sidenav را بارگذاری کرده و آن نیز سبب نمایش کامپوننت toolbar و سپس main-content شده‌است.


تنظیم طرحبندی برنامه توسط کامپوننت‌های Angular Material و همچنین Flex Layout


پس از این تنظیمات اکنون نوبت به تنظیم طرحبندی برنامه‌است و آن‌را با قراردادن کامپوننت Sidenav بسته‌ی Angular Material شروع می‌کنیم:
<mat-sidenav-container *ngIf="shouldRun">
   <mat-sidenav mode="side" opened>
      Sidenav content
   </mat-sidenav>
   Primary content
</mat-sidenav-container>
از کامپوننت Sidenav عموما برای طراحی منوی راهبری سایت استفاده می‌شود. این کامپوننت در سه حالت قابل تنظیم است که بر روی نحوه‌ی نمایش Sidenav content و Primary content تاثیرگذار است:
- Over: قسمت Sidenav content بر روی Primary content قرار می‌گیرد.
- Push: قسمت Sidenav content قسمت Primary content را از سر راه خود بر می‌دارد.
- Side:  قسمت Sidenav content در کنار Primary content قرار می‌گیرد.

در اینجا از حالت Side، در صفحات نمایشی بزرگ (اولین تصویر این قسمت) و از حالت Over، در صفحات نمایشی موبایل (مانند تصویر زیر) استفاده خواهیم کرد.



در ابتدا کدهای کامل هر سه کامپوننت و سپس توضیحات آن‌ها را مشاهده خواهید کرد:


تنظیم margin در CSS اصلی برنامه

زمانیکه sidenav و toolbar را بر روی صفحه قرار می‌دهیم، فاصله‌ای بین آن‌ها و لبه‌های صفحه مشاهده می‌شود. برای اینکه این فاصله را به صفر برسانیم، به فایل src\styles.css مراجعه کرده و margin بدنه‌ی صفحه را به صفر تنظیم می‌کنیم:
@import "~@angular/material/prebuilt-themes/indigo-pink.css";

body {
  margin: 0;
}


طراحی قالب main content

<mat-card>
  <h1>Main content</h1>
</mat-card>
برای نمایش main-content از کامپوننت ساده‌ی card استفاده شده‌است که به فایل main-content\main-content.component.html اضافه خواهد شد. این قسمت در نهایت توسط router-outlet نمایش داده می‌شود.


طرای منوی راهبری واکنشگرا

sidenav\sidenav.component.css 
 sidenav\sidenav.component.html 
.app-sidenav-container {
  position: fixed;
}

.app-sidenav {
  width: 240px;
}

.wrapper {
  margin: 50px;
}
<mat-sidenav-container fxLayout="row"
         fxFill>
  <mat-sidenav #sidenav 
    fxFlex="1 1 100%" [opened]="!isScreenSmall"
    [mode]="isScreenSmall ? 'over' : 'side'">
    <mat-toolbar color="primary">
      Contacts
    </mat-toolbar>
    <mat-list>
      <mat-list-item>Item 1</mat-list-item>
      <mat-list-item>Item 2</mat-list-item>
      <mat-list-item>Item 3</mat-list-item>
    </mat-list>
  </mat-sidenav>

  <mat-sidenav-content fxLayout="column" fxFlex="1 1 100%" fxFill>
    <app-toolbar (toggleSidenav)="sidenav.toggle()"></app-toolbar>
    <div>
      <router-outlet></router-outlet>
    </div>
  </mat-sidenav-content>
</mat-sidenav-container>

- نمای کلی صفحه در این قسمت طراحی شده‌است. sidenav-container که در برگیرنده‌ی اصلی است، به fxLayout از نوع row تنظیم شده‌است. یعنی mat-sidenav و mat-sidenav-content دو ستون آن‌را از چپ به راست تشکیل می‌دهند و درون یک ردیف، سیلان خواهند یافت. همچنین می‌خواهیم این container کل صفحه را پر کند، به همین جهت از fxFill استفاده شده‌است. این fxFill اعمال شده‌ی به container، زمانی عمل خواهد کرد که position آن در css، به fixed تنظیم شود که اینکار در css این قالب و در کلاس app-sidenav-container آن انجام شده‌است.
- سپس toolbar و همچنین router-outlet که main content را نمایش می‌دهند، داخل sidenav-content قرار گرفته‌اند و هر دو با هم، ستون دوم این طرحبندی را تشکیل می‌دهند. به همین جهت fxLayout آن به column تنظیم شده‌است (ستون اول آن، لیست تماس‌ها است و ستون دوم آن، از دو ردیف toolbar و main-content تشکیل می‌شود).
- اگر دقت کنید یک template reference variable به نام sidenav# به container اعمال شده‌است. از آن، جهت باز و بسته کردن sidenav استفاده می‌شود:
<app-toolbar (toggleSidenav)="sidenav.toggle()"></app-toolbar>
زمانیکه در toolbar بر روی دکمه‌ی منوی همبرگری کلیک شود، متد sidenav.toggle فراخوانی شده و این مورد سبب نمایان شدن مجدد sidenav خواهد شد. در این مورد در ادامه بیشتر بحث می‌کنیم.
- mat-sidenav از دو قسمت تشکیل شده‌است. بالای آن توسط mat-toolbar صرفا کلمه‌ی Contacts نمایش داده می‌شود و سپس ذیل آن، لیست فرضی تماس‌ها توسط کامپوننت mat-list قرار گرفته‌اند (تا فعلا خالی نباشد. در قسمت‌های بعدی آن‌را پویا خواهیم کرد). رنگ تولبار آن‌را ("color="primary) نیز به primary تنظیم کرده‌ایم تا خاکستری پیش‌فرض آن نباشد.
- کار کلاس mat-elevation-z10 این است که بین sidenav و main-content یک سایه‌ی سه بعدی را ایجاد کند که آن‌را در تصاویر مشاهده می‌کنید. عددی که پس از z قرار می‌گیرد، میزان عمق سایه را مشخص می‌کند.
- این قسمت از sidenav به همراه دو خاصیت opened و همچنین mode است که به مقدار isScreenSmall عکس العمل نشان می‌دهند:
<mat-sidenav  [opened]="!isScreenSmall" [mode]="isScreenSmall ? 'over' : 'side'">
در اینجا می‌خواهیم اگر اندازه‌ی صفحه xs شد، حالت over بجای حالت پیش‌فرض side تنظیم شود. یعنی در حالت موبایل و اندازه‌ی صفحه‌ی کوچک، sidenav در صورت فراخوانی متد sidenav.toggle در toolbar، بر روی قسمتی از صفحه ظاهر شود و نه در کنار آن که مخصوص حالت تمام صفحه است. همچنین می‌خواهیم اگر اندازه‌ی صفحه کوچک بود، sidenav بسته شود و نمایان نباشد. به همین جهت خاصیت opened آن به isScreenSmall تنظیم شده‌است. مدیریت خاصیت isScreenSmall در کدهای این کامپوننت به صورت زیر انجام می‌شود:

محتویات فایل sidenav\sidenav.component.ts:
import { Component, OnDestroy, OnInit } from "@angular/core";
import { MediaChange, ObservableMedia } from "@angular/flex-layout";
import { Subscription } from "rxjs";

@Component({
  selector: "app-sidenav",
  templateUrl: "./sidenav.component.html",
  styleUrls: ["./sidenav.component.css"]
})
export class SidenavComponent implements OnInit, OnDestroy {

  isScreenSmall = false;
  watcher: Subscription;

  constructor(private media: ObservableMedia) {
    this.watcher = media.subscribe((change: MediaChange) => {
      this.isScreenSmall = change.mqAlias === "xs";
    });
  }

  ngOnInit() {
  }

  ngOnDestroy() {
    this.watcher.unsubscribe();
  }
}
ObservableMedia را در انتهای قسمت دوم این سری بررسی کردیم. کار آن گوش فرادادن به تغییرات اندازه‌ی صفحه‌است. زمانیکه mqAlias آن برای مثال مساوی xs شد، یعنی در حالت موبایل قرار داریم. در این حالت مقدار خاصیت isScreenSmall به true تنظیم می‌شود و برعکس. با توجه به اینکه این media یک Observable است، نیاز است کار unsubscribe از آن نیز همواره در کدها وجود داشته باشد که نمونه‌ای از آن در متد ngOnDestroy صورت گرفته‌است.
تاثیر خاصیت isScreenSmall بر روی دو خاصیت opened و mode کامپوننت sidenav را در دو تصویر زیر مشاهده می‌کنید. اگر اندازه‌ی صفحه کوچک شود، ابتدا sidenav مخفی می‌شود. اگر کاربر بر روی دکمه‌ی منوی همبرگری کلیک کند، سبب نمایش مجدد sidenav، اینبار با حالت over و بر روی محتوای زیرین آن خواهد شد:




طراحی نوار ابزار واکنشگرا

کدهای قالب و css تولبار (ستون دوم طرحبندی کلی صفحه) را در ادامه مشاهده می‌کنید:
  toolbar\toolbar.component.css    toolbar\toolbar.component.html 
 
.sidenav-toggle {
  padding: 0;
  margin: 8px;
  min-width:56px;
}
 
<mat-toolbar color="primary">
  <button mat-button fxHide fxHide.xs="false" 
              class="sidenav-toggle" (click)="toggleSidenav.emit()">
    <mat-icon>menu</mat-icon>
  </button>

  <span>Contact Manager</span>
</mat-toolbar>

با توجه به استفاده‌ی از fxHide، یعنی دکمه‌ی نمایش منوی همبرگری در تمام حالات مخفی خواهد بود. برای لغو آن و نمایش آن در حالت موبایل، از حالت واکنشگرای آن یعنی fxHide.xs استفاده می‌کنیم (قسمت «کار با API واکنشگرای Angular Flex layout» در مطلب قبلی این سری). به این ترتیب زمانیکه کاربر اندازه‌ی صفحه را کوچک می‌کند و یا اندازه‌ی واقعی صفحه‌ی نمایش او کوچک است، این دکمه نمایان خواهد شد.
همچنین در sidenav یک چنین تعریفی را داریم:
 <app-toolbar (toggleSidenav)="sidenav.toggle()"></app-toolbar>
بروز رخ‌داد toggleSidenav سبب خواهد شد که متد sidenav.toggle فراخوانی شود و سبب نمایش sidenav در اندازه‌های کوچک صفحه‌ی نمایشی گردد. این رخ‌داد سفارشی را نیز به رخ‌داد click دکمه‌ی همبرگری تولبار متصل کرده‌ایم که با کلیک بر روی آن، کار emit آن صورت می‌گیرد. این emit نیز سبب خواهد شد تا sidenav.toggle متصل به سمتی دیگر، فعال شود. نحوه‌ی تعریف این رخ‌داد سفارشی را در کدهای کامپوننت تولبار، در ادامه مشاهده می‌کنید:

محتویات فایل toolbar\toolbar.component.ts:
import { Component, EventEmitter, OnInit, Output } from "@angular/core";

@Component({
  selector: "app-toolbar",
  templateUrl: "./toolbar.component.html",
  styleUrls: ["./toolbar.component.css"]
})
export class ToolbarComponent implements OnInit {

  @Output() toggleSidenav = new EventEmitter<void>();

  constructor() { }

  ngOnInit() {
  }
}


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: MaterialAngularClient-02.zip
برای اجرای آن نیز ابتدا فایل restore.bat و سپس فایل ng-serve.bat را اجرا کنید. پس از اجرای برنامه، یکبار آن‌را در حالت تمام صفحه و بار دیگر با کوچک‌تر کردن اندازه‌ی مرورگر آزمایش کنید. در حالتیکه به اندازه‌ی موبایل رسیدید، بر روی دکمه‌ی همبرگری نمایان شده کلیک کنید تا عکس العمل آن و نمایش مجدد sidenav را در حالت over، مشاهده نمائید.
مطالب
آپلود فایل‌ها در یک برنامه‌ی Angular به کمک کامپوننت ng2-file-upload
در مطلب «بررسی روش آپلود فایل‌ها از طریق یک برنامه‌ی Angular به یک برنامه‌ی ASP.NET Core» روش عمومی آپلود فایل‌ها را بررسی کردیم. آن مطلب وابستگی به کامپوننت خاصی ندارد و عمومی است. در مطلب جاری می‌خواهیم روش دیگری را مبتنی بر کامپوننت ng2-file-upload بررسی کنیم که به همراه نمایش درصد پیشرفت ارسال فایل‌ها، امکان انتخاب بهتر نوع فایل‌های آپلودی و همچنین امکان مشاهده‌ی لیست کامل فایل‌های انتخاب شده و امکان حذف مواردی از آن، پیش از ارسال نهایی است.



پیشنیازهای کار با کامپوننت 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
  ]
در غیراینصورت خطای شناخته نشدن خاصیت uploader را در حین اعمال این کامپوننت مشاهده خواهید کرد.


تکمیل 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();
در اینجا یک خاصیت عمومی از نوع FileUploader تعریف شده‌است که در اختیار قالب این کامپوننت قرار خواهد گرفت. همچنین شیء مدل فرم نیز همانند مطلب «بررسی روش آپلود فایل‌ها از طریق یک برنامه‌ی Angular به یک برنامه‌ی ASP.NET Core» تهیه شده‌است. هدف این است که بررسی کنیم علاوه بر ارسال فایل‌ها، چگونه می‌توان اطلاعات یک فرم را نیز به سمت سرور ارسال کرد.


وهله سازی از کامپوننت 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
      }
    );
- در اینجا url، مسیر اکشن متدی را در سمت سرور مشخص می‌کند که قرار است فایل‌های ارسالی را دریافت و ذخیره کند.
- اگر برنامه از نکات 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]);
        }
      }
    };
در اینجا شبیه به مطلب مشابه قبلی، مقادیر خواص شیء مدل، به صورت خودکار استخراج شده و به خاصیت form این کامپوننت که درحقیقت همان FormData ارسالی به سمت سرور است، اضافه می‌شوند.

ب) اطلاع یافتن از رخ‌داد خاتمه‌ی کار
رخ‌داد 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>
هدف از این فیلد این است که شیء Ticket را وهله سازی و مقدار دهی کند. از مقدار آن در رخ‌دادگردان onBuildItemForm که پیشتر توضیح داده شد، استفاده می‌شود.

تعریف ویژه‌ی فیلد ارسال فایل‌ها به سمت سرور

    <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>
در اینجا ابتدا دایرکتیو ng2FileSelect ذکر می‌شود تا کامپوننت مرتبط فعالسازی شود. سپس خاصیت uploader این دایرکتیو توسط خاصیت fileUploader که پیشتر در کامپوننت، وهله سازی و تنظیم شد، مقدار دهی می‌شود.
ذکر ویژگی استاندارد 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>
شیء fileUploader وهله سازی شده‌ی در کامپوننت این قالب، دارای خاصیت queue است. در این خاصیت، لیست فایل‌های انتخابی توسط کاربر درج می‌شوند. برای مثال مقدار fileUploader?.queue?.length مساوی تعداد فایل‌های انتخابی توسط کاربر است. بنابراین می‌توان حلقه‌ای را بر روی آن تشکیل داد و مشخصات این فایل‌ها را در صفحه نمایش داد. همچنین هر آیتم آن دارای متد remove نیز هست. کار این متد، حذف این آیتم از لیست queue است و یا اگر متد fileUploader.clearQueue فراخوانی شود، تمام آیتم‌های این لیست را حذف می‌کند.
در اینجا از progress-bar بوت استرپ برای نمایش درصد آپلود فایل‌ها استفاده شده‌است:
              <div class="progress" style="margin-bottom: 0;">
                <div class="progress-bar" role="progressbar" [ngStyle]="{ 'width': item.progress + '%' }"></div>
              </div>
این کامپوننت ارسال فایل، خاصیت item.progress هر فایل موجود در queue را مدام به روز رسانی می‌کند. به همین جهت می‌توان از آن جهت تغییر عرض پیشرفت progress-bar بوت استرپ استفاده کرد.

غیرفعال کردن دکمه‌ی ارسال، در صورت عدم انتخاب یک فایل

    <button class="btn btn-primary" [disabled]="form.invalid || !fileUploader.queue.length"
      type="submit">Submit</button>
  </form>
اگر بخواهیم انتخاب حداقل یک فایل را توسط کاربر اجباری کنیم، می‌توان خاصیت disabled دکمه‌ی ارسال را به طول صف یا fileUploader.queue.length نیز متصل کرد.


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید.
اشتراک‌ها
کتابخانه ngx-trend

Simple, elegant spark lines for Angular  Demo

Features

  • Simple. Integrate in seconds.
  • Scalable. Uses SVG for sharp, scalable graphs. Will fill the parent container, or you can provide a fixed size.
  • Beautiful. Built-in gradient support, and customizable smoothing.
  • Animatable. Support for on-mount animations where the trend graph draws from left to right.
  • Tiny. Zero-dependency, gzips to <3kb.
کتابخانه ngx-trend