اشتراک‌ها
Angular 2 برای توسعه دهنده‌های Angular 1
// Angular 1
const module = angular.module('myModule', []);
module.service('UserService', ['$http', function ($http) {  
  this.getUsers = () => {
    return $http.get('http://api.mywebsite.com/users')
                .then(res => res.data)
                .catch(res => new Error(res.data.error));
  }
}]);

/***************************************************************/

// Angular 2
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';

@Injectable()
class UserService { 
  constructor(private http: Http) {}
  
  getUsers(): Observable<User[]> {
    return this.http.get('http://api.mywebsite.com/users')
                    .map((res: Response) => res.json())
                    .catch((res: Response) => Observable.throw(res.json().error);
  }  
}
Angular 2 برای توسعه دهنده‌های Angular 1
اشتراک‌ها
معرفی سایت construx.vueocity.com
نویسنده کتاب Code Complete ، آقای استیو مک کانل (Steve McConnell )، با همکاری مدرسان سطح بالای دیگر، در سایت https://construx.vueocity.com آموزش‌های بسیار مفید برای مهندسی نرم افزار کاربردی ارائه می‌کنند.
آموزشها عبارتند از:
10x Software Development
Agile Planning and Estimation
Agile Practices for Developers
Agile Release Planning
Agile Requirements
Agile Requirements Modeling
Agile Team Metrics
Code Complete Essentials
Design Patterns
Developer Testing
Kanban Overview
Product Envisioning Overview
Scrum in Depth
Scrum in Depth Live
Scrum Overview
Software Configuration Management Overview
Software Design
Software Economics
Software Effectiveness Conference
Software Estimation
Software Project Management
Software Requirements
Software Risk Management
The Scrum Product Owner
Total Project Quality
Understanding Software Projects 
عضویت در این سایت برای یک هفته رایگان است. این آموزش‌ها به هیچ زبان برنامه نویسی وابسته نیستند و در تمام پروژه‌های متوسط و بزرگ نرم افزاری قابل استفاده هستند. در ابتدای هر آموزش کتابچه بسیار مفیدی که چکیده آموزش را در خود دارد نیز ارائه می‌شود.
معرفی سایت construx.vueocity.com
نظرات مطالب
احراز هویت و اعتبارسنجی کاربران در برنامه‌های Angular - قسمت ششم - کار با منابع محافظت شده‌ی سمت سرور
Http Module منسوخ شده‌است (و زمانیکه ماژولی منسوخ شده اعلام می‌شود، یعنی در چند نگارش بعد، از کل مجموعه حذف خواهد شد). این سری از Http Client استفاده می‌کند که مطلب روش «ارتقاء به HTTP Client در Angular 4.3» را در این مورد می‌توانید مطالعه کنید؛ به همراه مطلب «ارتقاء به Angular 6: بررسی تغییرات RxJS». مباحث Interceptors توکار مورد استفاده‌ی در اینجا، برای Http Client آن وجود خارجی دارد. برای Http Module منسوخ شده‌، روش «راه‌اندازی Http Interceptor در Angular» وجود داشت.
مطالب
Blazor 5x - قسمت 14 - کار با فرم‌ها - بخش 2 - تعریف فرم‌ها و اعتبارسنجی آن‌ها
در ادامه قصد داریم از سرویس زیر که در قسمت قبل تکمیل شد، در یک برنامه‌ی Blazor Server استفاده کنیم:
namespace BlazorServer.Services
{
    public interface IHotelRoomService
    {
        Task<HotelRoomDTO> CreateHotelRoomAsync(HotelRoomDTO hotelRoomDTO);

        Task<int> DeleteHotelRoomAsync(int roomId);

        IAsyncEnumerable<HotelRoomDTO> GetAllHotelRoomsAsync();

        Task<HotelRoomDTO> GetHotelRoomAsync(int roomId);

        Task<HotelRoomDTO> IsRoomUniqueAsync(string name);

        Task<HotelRoomDTO> UpdateHotelRoomAsync(int roomId, HotelRoomDTO hotelRoomDTO);
    }
}


تعریف کامپوننت‌های ابتدایی نمایش لیست اتاق‌ها و ثبت و ویرایش آن‌ها


در ابتدا کامپوننت‌های خالی نمایش لیست اتاق‌ها و همچنین فرم خالی ثبت و ویرایش آن‌ها را به همراه مسیریابی‌های مرتبط، ایجاد می‌کنیم. به همین جهت ابتدا داخل پوشه‌ی Pages، پوشه‌ی جدید HotelRoom را ایجاد کرده و فایل جدید HotelRoomList.razor را با محتوای ابتدایی زیر، به آن اضافه می‌کنیم.
@page "/hotel-room"

<div class="row mt-4">
    <div class="col-8">
        <h4 class="card-title text-info">Hotel Rooms</h4>
    </div>
    <div class="col-3 offset-1">
        <NavLink href="hotel-room/create" class="btn btn-info">Add New Room</NavLink>
    </div>
</div>

@code {

}
این کامپوننت در مسیر hotel-room/ قابل دسترسی خواهد بود. بر این اساس، به کامپوننت Shared\NavMenu.razor مراجعه کرده و مدخل منوی آن‌را تعریف می‌کنیم:
<li class="nav-item px-3">
    <NavLink class="nav-link" href="hotel-room">
        <span class="oi oi-list-rich" aria-hidden="true"></span> Hotel Rooms
    </NavLink>
</li>

تا اینجا صفحه‌ی ابتدایی نمایش لیست اتاق‌ها، به همراه یک دکمه‌ی افزودن اتاق جدید نیز هست. به همین جهت فایل جدید Pages\HotelRoom\HotelRoomUpsert.razor را به همراه مسیریابی hotel-room/create/ برای تعریف کامپوننت ابتدایی ثبت و ویرایش اطلاعات اتاق‌ها، اضافه می‌کنیم:
@page "/hotel-room/create"

<h3>HotelRoomUpsert</h3>

@code {

}
- واژه‌ی Upsert در مورد فرمی بکاربرده می‌شود که هم برای ثبت اطلاعات و هم برای ویرایش اطلاعات از آن استفاده می‌شود.
- NavLink تعریف شده‌ی در کامپوننت نمایش لیست اتاق‌ها، به مسیریابی کامپوننت فوق اشاره می‌کند.


ایجاد فرم ثبت یک اتاق جدید

برای ثبت یک اتاق جدید نیاز است به مدل UI آن که همان HotelRoomDTO تعریف شده‌ی در قسمت قبل است، دسترسی داشت. به همین جهت در پروژه‌ی BlazorServer.App، ارجاعی را به پروژه‌ی BlazorServer.Models.csproj اضافه می‌کنیم:
<Project Sdk="Microsoft.NET.Sdk.Web">
  <ItemGroup>
    <ProjectReference Include="..\BlazorServer.Models\BlazorServer.Models.csproj" />
  </ItemGroup>
</Project>
سپس جهت سراسری اعلام کردن فضای نام آن، یک سطر زیر را به انتهای فایل BlazorServer.App\_Imports.razor اضافه می‌کنیم:
@using BlazorServer.Models
اکنون می‌توانیم کامپوننت Pages\HotelRoom\HotelRoomUpsert.razor را به صورت زیر تکمیل کنیم:
@page "/hotel-room/create"

<div class="row mt-2 mb-5">
    <h3 class="card-title text-info mb-3 ml-3">@Title Hotel Room</h3>
    <div class="col-md-12">
        <div class="card">
            <div class="card-body">
                <EditForm Model="HotelRoomModel">
                    <div class="form-group">
                        <label>Name</label>
                        <InputText @bind-Value="HotelRoomModel.Name" class="form-control"></InputText>
                    </div>
                </EditForm>
            </div>
        </div>
    </div>
</div>

@code
{
    private HotelRoomDTO HotelRoomModel = new HotelRoomDTO();
    private string Title = "Create";
}
توضیحات:
- در برنامه‌های Blazor، کامپوننت ویژه‌ی EditForm را بجای تگ استاندارد form، مورد استفاده قرار می‌دهیم.
- این کامپوننت، مدل فرم را از فیلد HotelRoomModel که در قسمت کدها تعریف کردیم، دریافت می‌کند. کار آن تامین اطلاعات فیلدهای فرم است.
- سپس در EditForm تعریف شده، بجای المان استاندارد input، از کامپوننت InputText برای دریافت اطلاعات متنی استفاده می‌شود. با bind-value@ در قسمت چهارم این سری بیشتر آشنا شدیم و کار آن two-way data binding است. در اینجا هر اطلاعاتی که وارد می‌شود، سبب به روز رسانی خودکار مقدار خاصیت HotelRoomModel.Name می‌شود و برعکس.

یک نکته: در قسمت قبل، مدل UI را از نوع رکورد C# 9.0 و init only تعریف کردیم. رکوردها، با EditForm و two-way databinding آن سازگاری ندارند (bind-value@ در اینجا) و بیشتر برای کنترلرهای برنامه‌های Web API که یکبار قرار است کار وهله سازی آن‌ها در زمان دریافت اطلاعات از کاربر صورت گیرد، مناسب هستند و نه با فرم‌های پویای Blazor. به همین جهت به پروژه‌ی BlazorServer.Models مراجعه کرده و نوع آن‌ها را به کلاس و init‌ها را به set معمولی تغییر می‌دهیم تا در فرم‌های Blazor هم قابل استفاده شوند.

تا اینجا کامپوننت ثبت اطلاعات یک اتاق جدید، چنین شکلی را پیدا کرده‌است:



تکمیل سایر فیلدهای فرم ورود اطلاعات اتاق

پس از تعریف فیلد ورود اطلاعات نام اتاق، سایر فیلدهای متناظر با HotelRoomDTO را نیز به صورت زیر به EditForm تعریف شده اضافه می‌کنیم که در اینجا از InputNumber برای دریافت اطلاعات عددی و از InputTextArea، برای دریافت اطلاعات متنی چندسطری استفاده شده‌است:
<EditForm Model="HotelRoomModel">
    <div class="form-group">
        <label>Name</label>
        <InputText @bind-Value="HotelRoomModel.Name" class="form-control"></InputText>
    </div>
    <div class="form-group">
        <label>Occupancy</label>
        <InputNumber @bind-Value="HotelRoomModel.Occupancy" class="form-control"></InputNumber>
    </div>
    <div class="form-group">
        <label>Rate</label>
        <InputNumber @bind-Value="HotelRoomModel.RegularRate" class="form-control"></InputNumber>
    </div>
    <div class="form-group">
        <label>Sq ft.</label>
        <InputText @bind-Value="HotelRoomModel.SqFt" class="form-control"></InputText>
    </div>
    <div class="form-group">
        <label>Details</label>
        <InputTextArea @bind-Value="HotelRoomModel.Details" class="form-control"></InputTextArea>
    </div>
    <div class="form-group">
        <button class="btn btn-primary">@Title Room</button>
        <NavLink href="hotel-room" class="btn btn-secondary">Back to Index</NavLink>
    </div>
</EditForm>
با این خروجی:



تعریف اعتبارسنجی‌های فیلدهای یک فرم Blazor

در حین تعریف یک فرم، برای واکنش نشان دادن به دکمه‌ی submit، می‌توان رویداد OnSubmit را به کامپوننت EditForm اضافه کرد که سبب فراخوانی متدی در قسمت کدهای کامپوننت جاری خواهد شد؛ مانند فراخوانی متد HandleHotelRoomUpsert در مثال زیر:
<EditForm Model="HotelRoomModel" OnSubmit="HandleHotelRoomUpsert">
</EditForm>

@code
{
    private HotelRoomDTO HotelRoomModel = new HotelRoomDTO();

    private async Task HandleHotelRoomUpsert()
    {

    }
}
هرچند HotelRoomDTO تعریف شده به همراه تعریف اعتبارسنجی‌هایی مانند Required است، اما اگر بر روی دکمه‌ی submit کلیک کنیم، متد HandleHotelRoomUpsert فراخوانی می‌شود. یعنی روال رویدادگردان OnSubmit، صرفنظر از وضعیت اعتبارسنجی مدل فرم، همواره با submit فرم، اجرا می‌شود.
اگر این مورد، مدنظر نیست، می‌توان بجای OnSubmit، از رویداد OnValidSubmit استفاده کرد. در این حالت اگر اعتبارسنجی مدل فرم با شکست مواجه شود، دیگر متد HandleHotelRoomUpsert فراخوانی نخواهد شد. همچنین در این حالت می‌توان خطاهای اعتبارسنجی را نیز در فرم نمایش داد:
<EditForm Model="HotelRoomModel" OnValidSubmit="HandleHotelRoomUpsert">
    <DataAnnotationsValidator />
    @*<ValidationSummary />*@
    <div class="form-group">
        <label>Name</label>
        <InputText @bind-Value="HotelRoomModel.Name" class="form-control"></InputText>
        <ValidationMessage For="()=>HotelRoomModel.Name"></ValidationMessage>
    </div>
    <div class="form-group">
        <label>Occupancy</label>
        <InputNumber @bind-Value="HotelRoomModel.Occupancy" class="form-control"></InputNumber>
        <ValidationMessage For="()=>HotelRoomModel.Occupancy"></ValidationMessage>
    </div>
    <div class="form-group">
        <label>Rate</label>
        <InputNumber @bind-Value="HotelRoomModel.RegularRate" class="form-control"></InputNumber>
        <ValidationMessage For="()=>HotelRoomModel.RegularRate"></ValidationMessage>
    </div>
- در اینجا قسمت‌های تغییر کرده را مشاهده می‌کنید که به همراه درج DataAnnotationsValidator و ValidationMessage‌ها است.
- کامپوننت DataAnnotationsValidator، اعتبارسنجی مبتنی بر data annotations را مانند [Required]، در دامنه‌ی دید یک EditForm فعال می‌کند.
- اگر خواستیم تمام خطاهای اعتبارسنجی را به صورت خلاصه‌ای در بالای فرم نمایش دهیم، می‌توان از کامپوننت ValidationSummary استفاده کرد.
- و یا اگر خواستیم خطاها را به صورت اختصاصی‌تری ذیل هر تکست‌باکس نمایش دهیم، می‌توان از کامپوننت ValidationMessage کمک گرفت. خاصیت For آن از نوع <Expression<System.Func تعریف شده‌است که اجازه‌ی تعریف strongly typed نام خاصیت در حال اعتبارسنجی را به صورتی که مشاهده می‌کنید، میسر می‌کند.



ثبت اولین اتاق هتل

در ادامه می‌خواهیم روال رویدادگردان HandleHotelRoomUpsert را مدیریت کنیم. به همین جهت نیاز به کار با سرویس IHotelRoomService ابتدای بحث خواهد بود. بنابراین در ابتدا به فایل BlazorServer.App\_Imports.razor مراجعه کرده و فضای نام سرویس‌های برنامه را اضافه می‌کنیم:
@using BlazorServer.Services
اکنون امکان تزریق IHotelRoomService را که در قسمت قبل پیاده سازی و به سیستم تزریق وابستگی‌های برنامه معرفی کردیم، پیدا می‌کنیم:
@page "/hotel-room/create"

@inject IHotelRoomService HotelRoomService
@inject NavigationManager NavigationManager


@code
{
    private HotelRoomDTO HotelRoomModel = new HotelRoomDTO();
    private string Title = "Create";

    private async Task HandleHotelRoomUpsert()
    {
        var roomDetailsByName = await HotelRoomService.IsRoomUniqueAsync(HotelRoomModel.Name);
        if (roomDetailsByName != null)
        {
            //there is a duplicate room. show an error msg.
            return;
        }

        var createdResult = await HotelRoomService.CreateHotelRoomAsync(HotelRoomModel);
        NavigationManager.NavigateTo("hotel-room");
    }
}
در اینجا در ابتدا، سرویس IHotelRoomService به کامپوننت جاری تزریق شده و سپس از متدهای IsRoomUniqueAsync و CreateHotelRoomAsync آن، جهت بررسی منحصربفرد بودن نام اتاق و ثبت نهایی اطلاعات مدل برنامه که به فرم جاری به صورت دو طرفه‌ای متصل است، استفاده کرده‌ایم. در نهایت پس از ثبت اطلاعات، کاربر به صفحه‌ی نمایش لیست اتاق‌ها، توسط سرویس توکار NavigationManager، هدایت می‌شود.

اگر پیشتر با ASP.NET Web Forms کار کرده باشید (اولین روش توسعه‌ی برنامه‌های وب در دنیای دات نت)، مدل برنامه نویسی Blazor Server، بسیار شبیه به کار با وب فرم‌ها است؛ البته بر اساس آخرین تغییرات دنیای دانت نت مانند برنامه نویسی async، کار با سرویس‌ها، تزریق وابستگی‌های توکار و غیره.


نمایش لیست اتاق‌های ثبت شده


تا اینجا موفق شدیم اطلاعات یک مدل اعتبارسنجی شده را در بانک اطلاعاتی ثبت کنیم. مرحله‌ی بعد، نمایش لیست اطلاعات ثبت شده‌ی در بانک اطلاعاتی است. بنابراین به کامپوننت HotelRoomList.razor مراجعه کرده و آن‌را به صورت زیر تکمیل می‌کنیم:
@page "/hotel-room"

@inject IHotelRoomService HotelRoomService

<div class="row mt-4">
    <div class="col-8">
        <h4 class="card-title text-info">Hotel Rooms</h4>
    </div>
    <div class="col-3 offset-1">
        <NavLink href="hotel-room/create" class="btn btn-info">Add New Room</NavLink>
    </div>
</div>

<div class="row mt-4">
    <div class="col-12">
        <table class="table table-bordered table-hover">
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Occupancy</th>
                    <th>Rate</th>
                    <th>
                        Sqft
                    </th>
                    <th>

                    </th>
                </tr>
            </thead>
            <tbody>
                @if (HotelRooms.Any())
                {
                    foreach (var room in HotelRooms)
                    {
                        <tr>
                            <td>@room.Name</td>
                            <td>@room.Occupancy</td>
                            <td>@room.RegularRate.ToString("c")</td>
                            <td>@room.SqFt</td>
                            <td></td>
                        </tr>
                    }
                }
                else
                {
                    <tr>
                        <td colspan="5">No records found</td>
                    </tr>
                }
            </tbody>
        </table>
    </div>
</div>

@code
{
    private List<HotelRoomDTO> HotelRooms = new List<HotelRoomDTO>();

    protected override async Task OnInitializedAsync()
    {
        await foreach(var room in HotelRoomService.GetAllHotelRoomsAsync())
        {
            HotelRooms.Add(room);
        }
    }
}
توضیحات:
- متد GetAllHotelRoomsAsync، لیست اتاق‌های ثبت شده را بازگشت می‌دهد. البته خروجی آن از نوع <IAsyncEnumerable<HotelRoomDTO است که از زمان C# 8.0 ارائه شد و روش کار با آن اندکی متفاوت است. IAsyncEnumerable‌ها را باید توسط await foreach پردازش کرد.
- همانطور که در مطلب بررسی چرخه‌ی حیات کامپوننت‌ها نیز عنوان شد، متدهای رویدادگران OnInitialized و نمونه‌ی async آن برای دریافت اطلاعات از سرویس‌ها طراحی شده‌اند که در اینجا نمونه‌ای از آن‌را مشاهده می‌کنید.
- پس از تشکیل لیست اتاق‌ها، حلقه‌ی foreach (var room in HotelRooms) تعریف شده، ردیف‌های آن‌را در UI نمایش می‌دهد.


کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: Blazor-5x-Part-14.zip
اشتراک‌ها
پیاده سازی ویژگی های جدید در Angular 6

Angular 6 is out! The most outstanding changes are in its CLI and how services get injected. If you are looking to write your very first Angular 6 app—or Angular/Firebase app—in this tutorial, we’ll go over the basic steps of initial setup and create a small diary app. 

پیاده سازی ویژگی های جدید در Angular 6
اشتراک‌ها
تولید محتوای پویا در Angular

محتوای پویا Angular  : 

Multiple ways to create Angular components dynamically at runtime 

In this article, I am going to show you several ways of creating dynamic content in Angular. You will get examples of custom list templates, dynamic component creation, runtime component and module compilation. Full source code will be available at the end of the article.

 
تولید محتوای پویا در Angular
بازخوردهای دوره
تزریق خودکار وابستگی‌ها در برنامه‌های ASP.NET Web forms
با سلام؛ من در ویندوز اپلیکیشن ازاین ساختار استفاده میکنم و از فرم‌های Devexpress استفاده میکنم .
در کلاس BasePage  روی دستور  ObjectFactory this خطای زیر رو میده
 An unhandled exception of type 'StructureMap.StructureMapException' occurred in StructureMap.dll
Additional information: Error in the application.
نظرات مطالب
نمایش خطاهای اعتبارسنجی سمت سرور ASP.NET Core در برنامه‌های Angular
نکته تکمیلی
برای خلوت کردن قالب‌های مرتبط با فرم‌ها برای نمایش خطاهای اعتبارسنجی و همچنین برای جلوگیری از تکرار، می‌توان کامپوننت ValidationMessage را به شکل زیر نیز توسعه داد:
import { Component, Input, OnInit } from '@angular/core';
import { AbstractControl } from '@angular/forms';

@Component({
  selector: 'validation-message',
  template: `
    <ng-container *ngIf="control.invalid && control.touched">
      {{ message }}
    </ng-container>
  `
})
export class ValidationMessageComponent implements OnInit {
  @Input() control: AbstractControl;
  @Input() fieldDisplayName: string;
  @Input() rules: { [key: string]: string };

  get message(): string {
    return this.control.hasError('required')
      ? `${this.fieldDisplayName} را وارد نمائید.`
      : this.control.hasError('pattern')
      ? `${this.fieldDisplayName} را به شکل صحیح وارد نمائید.`
      : this.control.hasError('email')
      ? `${this.fieldDisplayName} را به شکل صحیح وارد نمائید.`
      : this.control.hasError('minlength')
      ? `${this.fieldDisplayName} باید بیشتر از  ${
          this.control.errors.minlength.requiredLength
        } کاراکتر باشد.`
      : this.control.hasError('maxlength')
      ? `${this.fieldDisplayName} باید کمتر از  ${
          this.control.errors.maxlength.requiredLength
        } کاراکتر باشد.`
      : this.control.hasError('min')
      ? `${this.fieldDisplayName} باید بیشتر از  ${
          this.control.errors.min.requiredLength
        } باشد.`
      : this.control.hasError('max')
      ? `${this.fieldDisplayName} باید کمتر از  ${
          this.control.errors.max.requiredLength
        } باشد.`
      : this.hasRule()
      ? this.findRule()
      : this.control.hasError('model')
      ? `${this.control.errors.model.messages[0]}`
      : '';
  }
  constructor() {}

  private hasRule() {
    return (
      this.rules &&
      Object.keys(this.control.errors).some(ruleKey =>
        this.rules[ruleKey] ? true : false
      )
    );
  }

  private findRule(): string {
    let message = '';
    Object.keys(this.control.errors).forEach(ruleKey => {
      if (this.rules[ruleKey]) {
        message += `${this.rules[ruleKey]} `;
      }
    });

    return message;
  }

  ngOnInit(): void {}
}

این کامپوننت، کنترل مورد نظر، یک نام نمایشی برای فیلد متناظر و یک شیء تحت عنوان rules را دریافت می‌کند. در بدنه پراپرتی message، به ترتیب اولویت validator، بررسی انجام شده و پیغام مناسب بازگشت داده خواهد شد. همچنین خطاهای سمت سروری هم که با کد "model" به لیست خطاهای کنترل اضافه شده اند نیز مورد بررسی قرار گرفته شده اند. 
 در اینجا اگر لازم باشد با یکسری قواعد سفارشی هم به صورت یکپارچه با اعتبارسنج‌های پیش فرض رفتار کنیم و از یک مسیر این پیغام‌ها نمایش داده شوند، می‌توان از خصوصیت rules بهره برد. به عنوان مثال:
 <mat-error *ngIf="form.controls['userName'].invalid && form.controls['userName'].touched" 
 class="mat-text-warn">
    <validation-message
        [control]="form.controls['userName']"
        fieldDisplayName="نام کاربری"
        [rules]="{rule1:'پیغام متناظر با rule1'}">
    </validation-message>
</mat-error>

به همراه یک Validator سفارشی
this.form = this.formBuilder.group({
      userName: [
        '',
        [Validators.required, UserNameValidators.rule1)]
      ],
      password: ['', Validators.required],
      rememberMe: [false]
    });

export class UserNameValidators{
   static rule1(control: AbstractControl) {
        if (control.value.indexOf(' ') >= 0) {
            return { rule1: true };
        }
        return null;
    }
}