Boostrap Ladda یک پلاگین برای نمایش دکمه هایی است که پس از کلیک باید به حالت loading تبدیل شوند. این پلاگین در فرمهای ایجاد و یا ویرایش و یا صفحات جستجو بسیار مفید هستند. Angular-Ladda نسخه همگام سازی شده این پلاگین با AngularJs است که به سادگی میتواند به برنامه تک صفحه ای شما اضافه شود.
آیا تاحالا این روش رو در فرمهای Modal هم امتحان کردید؟ فکر میکنم با یک مشکل برخورد خواهید کرد اونم اینکه پیغام در تمام صفحات زیرین هم نمایش داده خواهد شد، همچنین در Partial Viewها باید مکانیزمی به کار برده شود که پیغام در موقعیت تعیین شده نمایش داده شود و نه در تمام جاهایی از صفحه که
به کار رفته است.
@{ Html.RenderPartial("_Alerts"); }
- به دلایل امنیتی نباید جزئیات خطاها را به کاربران نمایش داد. صرفا به نمایش صفحات و پیامهای عمومی بسنده کنید.
+ در مورد MVC و مدیریت خطاها در آن بحث مجزایی در سایت وجود دارد (^)؛ قسمت «دسترسی به اطلاعات استثناء در صفحه نمایش خطاها»
+ در مورد MVC و مدیریت خطاها در آن بحث مجزایی در سایت وجود دارد (^)؛ قسمت «دسترسی به اطلاعات استثناء در صفحه نمایش خطاها»
در قسمت قبل، یک لیست ثابت item 1/item 2/… را در sidenav نمایش دادیم. در این قسمت میخواهیم این لیست را با اطلاعات دریافت شدهی از سرور، پویا کنیم و همچنین با کلیک بر روی هر کدام، جزئیات آنها را نیز در قسمت main-content نمایش دهیم.
تهیه سرویس اطلاعات پویای برنامه
سرویس Web API ارائه شدهی توسط ASP.NET Core در این برنامه، لیست کاربران را به همراه یادداشتهای آنها به سمت کلاینت باز میگرداند و ساختار موجودیتهای آنها به صورت زیر است:
موجودیت کاربر که یک رابطهی one-to-many را با UserNotes دارد:
و موجودیت یادداشتهای کاربر که سر دیگر رابطه را تشکیل میدهد:
در نهایت اطلاعات ذخیره شدهی در بانک اطلاعاتی توسط سرویس کاربران:
در اختیار کنترلر Web API برنامه، برای ارائهی به سمت کلاینت، قرار میگیرد:
کدهای کامل لایه سرویس، تنظیمات EF Core و تنظیمات ASP.NET Core این قسمت را از پروژهی پیوستی انتهای بحث میتوانید دریافت کنید.
در این حالت اگر برنامه را اجرا کنیم، در مسیر زیر
یک چنین خروجی قابل مشاهده خواهد بود:
و آدرس https://localhost:5001/api/users/1 صرفا مشخصات اولین کاربر را بازگشت میدهد.
تنظیم محل تولید خروجی Angular CLI
ساختار پوشه بندی پروژهی جاری به صورت زیر است:
همانطور که ملاحظه میکنید، کلاینت Angular در یک پوشهاست و برنامهی سمت سرور ASP.NET Core در پوشهای دیگر. برای اینکه خروجی نهایی Angular CLI را به پوشهی wwwroot پروژهی وب کپی کنیم، فایل angular.json کلاینت Angular را به صورت زیر ویرایش میکنیم:
تنظیم این outputPath به wwwroot پروژهی وب سبب خواهد شد تا با صدور فرمان زیر:
برنامهی Angular در حالت watch (گوش فرا دادان به تغییرات فایلها) کامپایل شده و سپس به صورت خودکار در پوشهی MaterialAspNetCoreBackend.WebApp/wwwroot کپی شود. به این ترتیب پس از اجرای برنامهی ASP.NET Core توسط دستور زیر:
این برنامهی سمت سرور، در همان لحظه هم API خود را ارائه خواهد داد و هم هاست برنامهی Angular میشود.
بنابراین دو صفحهی کنسول مجزا را باز کنید. در اولی ng build (را با پارامترهای یاد شده در پوشهی MaterialAngularClient) و در دومی dotnet watch run را در پوشهی MaterialAspNetCoreBackend.WebApp اجرا نمائید.
هر دو دستور در حالت watch اجرا میشوند. مزیت مهم آن این است که اگر تغییر کوچکی را در هر کدام از پروژهها ایجاد کردید، صرفا همان قسمت کامپایل میشود و در نهایت سرعت کامپایل نهایی برنامه به شدت افزایش خواهد یافت.
تعریف معادلهای کلاسهای موجودیتهای سمت سرور، در برنامهی Angular
در ادامه پیش از تکمیل سرویس دریافت اطلاعات از سرور، نیاز است معادلهای کلاسهای موجودیتهای سمت سرور خود را به صورت اینترفیسهایی تایپاسکریپتی تعریف کنیم:
این دستورات دو اینترفیس خالی کاربر و یادداشتهای او را در پوشهی جدید models ایجاد میکنند. سپس آنها را به صورت زیر و بر اساس تعاریف سمت سرور آنها، تکمیل میکنیم:
محتویات فایل contact-manager\models\user-note.ts :
محتویات فایل contact-manager\models\user.ts :
ایجاد سرویس Angular دریافت اطلاعات از سرور
ساختار ابتدایی سرویس دریافت اطلاعات از سرور را توسط دستور زیر ایجاد میکنیم:
که سبب ایجاد فایل user.service.ts در پوشهی جدید services خواهد شد:
قسمت providedIn آن مخصوص Angular 6x است و هدف از آن کم حجمتر کردن خروجی نهایی برنامهاست؛ اگر از سرویسی که تعریف شده، در برنامه جائی استفاده نشدهاست. به این ترتیب دیگر نیازی نیست تا آنرا به صورت دستی در قسمت providers ماژول جاری ثبت و معرفی کرد.
کدهای تکمیل شدهی UserService را در ذیل مشاهده میکنید:
در اینجا از pipe-able operators مخصوص RxJS 6x استفاده شده که در مطلب «ارتقاء به Angular 6: بررسی تغییرات RxJS» بیشتر در مورد آنها بحث شدهاست.
- متد getAllUsersIncludeNotes، لیست تمام کاربران را به همراه یادداشتهای آنها از سرور واکشی میکند.
- متد getUserIncludeNotes صرفا اطلاعات یک کاربر را به همراه یادداشتهای او از سرور دریافت میکند.
بارگذاری و معرفی فایل svg نمایش avatars کاربران به Angular Material
بستهی Angular Material و کامپوننت mat-icon آن به همراه یک MatIconRegistry نیز هست که قصد داریم از آن برای نمایش avatars کاربران استفاده کنیم.
در قسمت اول، نحوهی «افزودن آیکنهای متریال به برنامه» را بررسی کردیم که در آنجا آیکنهای مرتبط، از فایلهای قلم، دریافت و نمایش داده میشوند. این کامپوننت، علاوه بر قلم آیکنها، از فایلهای svg حاوی آیکنها نیز پشتیبانی میکند که یک نمونه از این فایلها در مسیر wwwroot\assets\avatars.svg فایل پیوستی انتهای مطلب کپی شدهاست (چون برنامهی وب ASP.NET Core، هاست برنامه است، این فایل را در آنجا کپی کردیم).
ساختار این فایل svg نیز به صورت زیر است:
هر svg تعریف شدهی در آن دارای یک id است. از این id به عنوان نام avatar کاربرها استفاده خواهیم کرد. نحوهی فعالسازی آن نیز به صورت زیر است:
ابتدا به فایل contact-manager-app.component.ts مراجعه و سپس این کامپوننت آغازین ماژول مدیریت تماسها را با صورت زیر تکمیل میکنیم:
MatIconRegistry جزئی از بستهی angular/material است که در ابتدای کار import شدهاست. متد addSvgIconSet آن، مسیر یک فایل svg حاوی آیکنهای مختلف را دریافت میکند. این مسیر نیز باید توسط سرویس DomSanitizer در اختیار آن قرار گیرد که در کدهای فوق روش انجام آنرا ملاحظه میکنید. در مورد سرویس DomSanitizer در مطلب «نمایش HTML در برنامههای Angular» بیشتر بحث شدهاست.
در اینجا در صورتیکه فایل svg شما دارای یک تک آیکن است، روش ثبت آن به صورت زیر است:
که در نهایت کامپوننت mat-icon، این آیکن را به صورت زیر میتواند نمایش دهد:
یک نکته: پوشهی node_modules\material-design-icons به همراه تعداد قابل ملاحظهای فایل svg نیز هست.
نمایش لیست کاربران در sidenav
در ادامه به فایل sidenav\sidenav.component.ts مراجعه کرده و سرویس فوق را به آن جهت دریافت لیست کاربران، تزریق میکنیم:
به این ترتیب با اجرای برنامه و بارگذاری sidenav، در رخداد OnInit آن، کار دریافت اطلاعات کاربران و انتساب آن به خاصیت عمومی users صورت میگیرد.
اکنون میخواهیم از این اطلاعات جهت نمایش پویای آنها در sidenav استفاده کنیم. در قسمت قبل، جای آنها را در منوی سمت چپ صفحه به صورت زیر با اطلاعات ایستا مشخص کردیم:
اگر به مستندات mat-list مراجعه کنیم، در میانهی صفحه، navigation lists نیز ذکر شدهاست که میتواند لیستی پویا را به همراه لینک به آیتمهای آن نمایش دهد و این مورد دقیقا کامپوننتی است که در اینجا به آن نیاز داریم. بنابراین فایل sidenav\sidenav.component.html را گشوده و mat-list فوق را با mat-nav-list تعویض میکنیم:
اکنون اگر برنامه را اجرا کنیم، یک چنین شکلی قابل مشاهده است:
که در اینجا علاوه بر لیست کاربران که از سرویس Users دریافت شده، آیکن avatar آنها که از فایل assets/avatars.svg بارگذاری شده نیز قابل مشاهده است.
اتصال کاربران به صفحهی نمایش جزئیات آنها
در mat-nav-list فوق، فعلا هر کاربر به آدرس # لینک شدهاست. در ادامه میخواهیم با کمک سیستم مسیریابی، با کلیک بر روی نام هر کاربر، در سمت راست صفحه جزئیات او نیز نمایش داده شود:
در اینجا با استفاده از routerLink، هر کاربر را بر اساس Id او، به صفحهی جزئیات آن شخص، متصل کردهایم. البته این مسیریابی برای اینکه کار کند باید به صورت زیر به فایل contact-manager-routing.module.ts اضافه شود:
البته اگر تا اینجا برنامه را اجرا کنید، با نزدیک کردن اشارهگر ماوس به نام هر کاربر، آدرسی مانند https://localhost:5001/contactmanager/1 در status bar مرورگر ظاهر خواهد شد، اما با کلیک بر روی آن، اتفاقی رخ نمیدهد.
این مشکل دو علت دارد:
الف) چون ContactManagerModule را به صورت lazy load تعریف کردهایم، دیگر نباید در لیست imports فایل AppModule ظاهر شود. بنابراین فایل app.module.ts را گشوده و سپس تعریف ContactManagerModule را هم از قسمت imports بالای صفحه و هم از قسمت imports ماژول حذف کنید؛ چون نیازی به آن نیست.
ب) برای مدیریت خواندن id کاربر، فایل main-content\main-content.component.ts را گشوده و به صورت زیر تکمیل میکنیم:
در اینجا به کمک سرویس ActivatedRoute و گوش فرادادن به تغییرات params آن در ngOnInit، مقدار id مسیر دریافت میشود. سپس بر اساس این id، با کمک سرویس کاربران، اطلاعات این تک کاربر از سرور دریافت و به خاصیت عمومی user نسبت داده خواهد شد.
اکنون میتوان از اطلاعات این user دریافتی، در قالب این کامپوننت و یا همان فایل main-content.component.html استفاده کرد:
در اینجا از کامپوننت mat-spinner برای نمایش حالت منتظر بمانید استفاده کردهایم. اگر user نال باشد، این spinner نمایش داده میشود و برعکس.
همچنین mat-card را هم بر اساس مثال مستندات آن، ابتدا کپی و سپس سفارشی سازی کردهایم (اگر دقت کنید، هر کامپوننت آن سه برگهی overview، سپس API و در آخر Example را به همراه دارد). این روشی است که همواره میتوان با کامپوننتهای این مجموعه انجام داد. ابتدا مثالی را در مستندات آن پیدا میکنیم که مناسب کار ما باشد. سپس سورس آنرا از همانجا کپی و در برنامه قرار میدهیم و در آخر آنرا بر اساس اطلاعات خود سفارشی سازی میکنیم.
نمایش جزئیات اولین کاربر در حین بارگذاری اولیهی برنامه
تا اینجای کار اگر برنامه را از ابتدا بارگذاری کنیم، mat-spinner قسمت نمایش جزئیات تماسها ظاهر میشود و همانطور باقی میماند، با اینکه هنوز موردی انتخاب نشدهاست. برای رفع آن به کامپوننت sidnav مراجعه کرده و در لحظهی بارگذاری اطلاعات، اولین مورد را به صورت دستی نمایش میدهیم:
در اینجا ابتدا سرویس Router به سازندهی کلاس تزریق شدهاست و سپس زمانیکه کار دریافت اطلاعات تماسها پایان یافت و this.router.navigated نبود (یعنی پیشتر هدایت به آدرسی صورت نگرفته بود؛ برای مثال کاربر آدرس id داری را ریفرش نکرده بود)، اولین مورد را توسط متد this.router.navigate فعال میکنیم که سبب تغییر آدرس صفحه از https://localhost:5001/contactmanager به https://localhost:5001/contactmanager/1 و باعث نمایش جزئیات آن میشود.
البته روش دیگر مدیریت این حالت، حذف کدهای فوق و تبدیل کدهای کامپوننت main-content به صورت زیر است:
در اینجا اگر id انتخاب نشده باشد، یعنی اولین بار نمایش برنامه است و خودمان id مساوی 1 را برای آن در نظر میگیریم.
بستن خودکار sidenav در حالت نمایش موبایل
اگر اندازهی صفحهی نمایشی را کوچکتر کنیم، قسمت sidenav در حالت over نمایان خواهد شد. در این حالت اگر آیتمهای آنرا انتخاب کنیم، هرچند آنها نمایش داده میشوند، اما زیر این sidenav مخفی باقی خواهند ماند:
بنابراین در جهت بهبود کاربری این قسمت بهتر است با کلیک کاربر بر روی sidenav و گزینههای آن، این قسمت بسته شده و ناحیهی زیر آن نمایش داده شود.
در کدهای قالب sidenav، یک template reference variable برای آن به نام sidenav درنظر گرفته شدهاست:
برای دسترسی به آن در کدهای کامپوننت خواهیم داشت:
اکنون که به این ViewChild دسترسی داریم، میتوانیم در حالت نمایشی موبایل، متد close آنرا فراخوانی کنیم:
در اینجا با مشترک this.router.events شدن، متوجهی کلیک کاربر و نمایش صفحهی جزئیات آن میشویم. در قسمت سوم این مجموعه نیز خاصیت isScreenSmall را بر اساس ObservableMedia مقدار دهی کردیم. بنابراین اگر کاربر بر روی گزینهای کلیک کرده بود و همچنین اندازهی صفحه در حالت موبایل قرار داشت، sidenav را خواهیم بست تا بتوان محتوای زیر آنرا مشاهده کرد:
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: MaterialAngularClient-03.zip
برای اجرای آن:
الف) ابتدا به پوشهی src\MaterialAngularClient وارد شده و فایلهای restore.bat و ng-build-dev.bat را اجرا کنید.
ب) سپس به پوشهی src\MaterialAspNetCoreBackend\MaterialAspNetCoreBackend.WebApp وارد شده و فایلهای restore.bat و dotnet_run.bat را اجرا کنید.
اکنون برنامه در آدرس https://localhost:5001 قابل دسترسی است.
تهیه سرویس اطلاعات پویای برنامه
سرویس Web API ارائه شدهی توسط ASP.NET Core در این برنامه، لیست کاربران را به همراه یادداشتهای آنها به سمت کلاینت باز میگرداند و ساختار موجودیتهای آنها به صورت زیر است:
موجودیت کاربر که یک رابطهی one-to-many را با UserNotes دارد:
using System; using System.Collections.Generic; namespace MaterialAspNetCoreBackend.DomainClasses { public class User { public User() { UserNotes = new HashSet<UserNote>(); } public int Id { set; get; } public DateTimeOffset BirthDate { set; get; } public string Name { set; get; } public string Avatar { set; get; } public string Bio { set; get; } public ICollection<UserNote> UserNotes { set; get; } } }
using System; namespace MaterialAspNetCoreBackend.DomainClasses { public class UserNote { public int Id { set; get; } public DateTimeOffset Date { set; get; } public string Title { set; get; } public User User { set; get; } public int UserId { set; get; } } }
public interface IUsersService { Task<List<User>> GetAllUsersIncludeNotesAsync(); Task<User> GetUserIncludeNotesAsync(int id); }
namespace MaterialAspNetCoreBackend.WebApp.Controllers { [Route("api/[controller]")] public class UsersController : Controller { private readonly IUsersService _usersService; public UsersController(IUsersService usersService) { _usersService = usersService ?? throw new ArgumentNullException(nameof(usersService)); } [HttpGet] public async Task<IActionResult> Get() { return Ok(await _usersService.GetAllUsersIncludeNotesAsync()); } [HttpGet("{id:int}")] public async Task<IActionResult> Get(int id) { return Ok(await _usersService.GetUserIncludeNotesAsync(id)); } } }
در این حالت اگر برنامه را اجرا کنیم، در مسیر زیر
https://localhost:5001/api/users
و آدرس https://localhost:5001/api/users/1 صرفا مشخصات اولین کاربر را بازگشت میدهد.
تنظیم محل تولید خروجی Angular CLI
ساختار پوشه بندی پروژهی جاری به صورت زیر است:
همانطور که ملاحظه میکنید، کلاینت Angular در یک پوشهاست و برنامهی سمت سرور ASP.NET Core در پوشهای دیگر. برای اینکه خروجی نهایی Angular CLI را به پوشهی wwwroot پروژهی وب کپی کنیم، فایل angular.json کلاینت Angular را به صورت زیر ویرایش میکنیم:
"build": { "builder": "@angular-devkit/build-angular:browser", "options": { "outputPath": "../MaterialAspNetCoreBackend/MaterialAspNetCoreBackend.WebApp/wwwroot",
ng build --no-delete-output-path --watch
dotnet watch run
بنابراین دو صفحهی کنسول مجزا را باز کنید. در اولی ng build (را با پارامترهای یاد شده در پوشهی MaterialAngularClient) و در دومی dotnet watch run را در پوشهی MaterialAspNetCoreBackend.WebApp اجرا نمائید.
هر دو دستور در حالت watch اجرا میشوند. مزیت مهم آن این است که اگر تغییر کوچکی را در هر کدام از پروژهها ایجاد کردید، صرفا همان قسمت کامپایل میشود و در نهایت سرعت کامپایل نهایی برنامه به شدت افزایش خواهد یافت.
تعریف معادلهای کلاسهای موجودیتهای سمت سرور، در برنامهی Angular
در ادامه پیش از تکمیل سرویس دریافت اطلاعات از سرور، نیاز است معادلهای کلاسهای موجودیتهای سمت سرور خود را به صورت اینترفیسهایی تایپاسکریپتی تعریف کنیم:
ng g i contact-manager/models/user ng g i contact-manager/models/user-note
محتویات فایل contact-manager\models\user-note.ts :
export interface UserNote { id: number; title: string; date: Date; userId: number; }
import { UserNote } from "./user-note"; export interface User { id: number; birthDate: Date; name: string; avatar: string; bio: string; userNotes: UserNote[]; }
ایجاد سرویس Angular دریافت اطلاعات از سرور
ساختار ابتدایی سرویس دریافت اطلاعات از سرور را توسط دستور زیر ایجاد میکنیم:
ng g s contact-manager/services/user --no-spec
import { Injectable } from "@angular/core"; @Injectable({ providedIn: "root" }) export class UserService { constructor() { } }
کدهای تکمیل شدهی UserService را در ذیل مشاهده میکنید:
import { HttpClient, HttpErrorResponse } from "@angular/common/http"; import { Injectable } from "@angular/core"; import { Observable, throwError } from "rxjs"; import { catchError, map } from "rxjs/operators"; import { User } from "../models/user"; @Injectable({ providedIn: "root" }) export class UserService { constructor(private http: HttpClient) { } getAllUsersIncludeNotes(): Observable<User[]> { return this.http .get<User[]>("/api/users").pipe( map(response => response || []), catchError((error: HttpErrorResponse) => throwError(error)) ); } getUserIncludeNotes(id: number): Observable<User> { return this.http .get<User>(`/api/users/${id}`).pipe( map(response => response || {} as User), catchError((error: HttpErrorResponse) => throwError(error)) ); } }
- متد getAllUsersIncludeNotes، لیست تمام کاربران را به همراه یادداشتهای آنها از سرور واکشی میکند.
- متد getUserIncludeNotes صرفا اطلاعات یک کاربر را به همراه یادداشتهای او از سرور دریافت میکند.
بارگذاری و معرفی فایل svg نمایش avatars کاربران به Angular Material
بستهی Angular Material و کامپوننت mat-icon آن به همراه یک MatIconRegistry نیز هست که قصد داریم از آن برای نمایش avatars کاربران استفاده کنیم.
در قسمت اول، نحوهی «افزودن آیکنهای متریال به برنامه» را بررسی کردیم که در آنجا آیکنهای مرتبط، از فایلهای قلم، دریافت و نمایش داده میشوند. این کامپوننت، علاوه بر قلم آیکنها، از فایلهای svg حاوی آیکنها نیز پشتیبانی میکند که یک نمونه از این فایلها در مسیر wwwroot\assets\avatars.svg فایل پیوستی انتهای مطلب کپی شدهاست (چون برنامهی وب ASP.NET Core، هاست برنامه است، این فایل را در آنجا کپی کردیم).
ساختار این فایل svg نیز به صورت زیر است:
<?xml version="1.0" encoding="utf-8"?> <svg width="512" height="512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <svg viewBox="0 0 128 128" height="100%" width="100%" pointer-events="none" display="block" id="user1" >
ابتدا به فایل contact-manager-app.component.ts مراجعه و سپس این کامپوننت آغازین ماژول مدیریت تماسها را با صورت زیر تکمیل میکنیم:
import { Component } from "@angular/core"; import { MatIconRegistry } from "@angular/material"; import { DomSanitizer } from "@angular/platform-browser"; @Component() export class ContactManagerAppComponent { constructor(iconRegistry: MatIconRegistry, sanitizer: DomSanitizer) { iconRegistry.addSvgIconSet(sanitizer.bypassSecurityTrustResourceUrl("assets/avatars.svg")); } }
در اینجا در صورتیکه فایل svg شما دارای یک تک آیکن است، روش ثبت آن به صورت زیر است:
iconRegistry.addSvgIcon( "unicorn", this.domSanitizer.bypassSecurityTrustResourceUrl("assets/unicorn_icon.svg") );
<mat-icon svgIcon="unicorn"></mat-icon>
یک نکته: پوشهی node_modules\material-design-icons به همراه تعداد قابل ملاحظهای فایل svg نیز هست.
نمایش لیست کاربران در sidenav
در ادامه به فایل sidenav\sidenav.component.ts مراجعه کرده و سرویس فوق را به آن جهت دریافت لیست کاربران، تزریق میکنیم:
import { User } from "../../models/user"; import { UserService } from "../../services/user.service"; @Component() export class SidenavComponent implements OnInit { users: User[] = []; constructor(private userService: UserService) { } ngOnInit() { this.userService.getAllUsersIncludeNotes() .subscribe(data => this.users = data); } }
اکنون میخواهیم از این اطلاعات جهت نمایش پویای آنها در sidenav استفاده کنیم. در قسمت قبل، جای آنها را در منوی سمت چپ صفحه به صورت زیر با اطلاعات ایستا مشخص کردیم:
<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-nav-list> <mat-list-item *ngFor="let user of users"> <a matLine href="#"> <mat-icon svgIcon="{{user.avatar}}"></mat-icon> {{ user.name }} </a> </mat-list-item> </mat-nav-list>
که در اینجا علاوه بر لیست کاربران که از سرویس Users دریافت شده، آیکن avatar آنها که از فایل assets/avatars.svg بارگذاری شده نیز قابل مشاهده است.
اتصال کاربران به صفحهی نمایش جزئیات آنها
در mat-nav-list فوق، فعلا هر کاربر به آدرس # لینک شدهاست. در ادامه میخواهیم با کمک سیستم مسیریابی، با کلیک بر روی نام هر کاربر، در سمت راست صفحه جزئیات او نیز نمایش داده شود:
<mat-nav-list> <mat-list-item *ngFor="let user of users"> <a matLine [routerLink]="['/contactmanager', user.id]"> <mat-icon svgIcon="{{user.avatar}}"></mat-icon> {{ user.name }} </a> </mat-list-item> </mat-nav-list>
const routes: Routes = [ { path: "", component: ContactManagerAppComponent, children: [ { path: ":id", component: MainContentComponent }, { path: "", component: MainContentComponent } ] }, { path: "**", redirectTo: "" } ];
این مشکل دو علت دارد:
الف) چون ContactManagerModule را به صورت lazy load تعریف کردهایم، دیگر نباید در لیست imports فایل AppModule ظاهر شود. بنابراین فایل app.module.ts را گشوده و سپس تعریف ContactManagerModule را هم از قسمت imports بالای صفحه و هم از قسمت imports ماژول حذف کنید؛ چون نیازی به آن نیست.
ب) برای مدیریت خواندن id کاربر، فایل main-content\main-content.component.ts را گشوده و به صورت زیر تکمیل میکنیم:
import { Component, OnInit } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; import { User } from "../../models/user"; import { UserService } from "../../services/user.service"; @Component({ selector: "app-main-content", templateUrl: "./main-content.component.html", styleUrls: ["./main-content.component.css"] }) export class MainContentComponent implements OnInit { user: User; constructor(private route: ActivatedRoute, private userService: UserService) { } ngOnInit() { this.route.params.subscribe(params => { this.user = null; const id = params["id"]; if (!id) { return; } this.userService.getUserIncludeNotes(id) .subscribe(data => this.user = data); }); } }
اکنون میتوان از اطلاعات این user دریافتی، در قالب این کامپوننت و یا همان فایل main-content.component.html استفاده کرد:
<div *ngIf="!user"> <mat-spinner></mat-spinner> </div> <div *ngIf="user"> <mat-card> <mat-card-header> <mat-icon mat-card-avatar svgIcon="{{user.avatar}}"></mat-icon> <mat-card-title> <h2>{{ user.name }}</h2> </mat-card-title> <mat-card-subtitle> Birthday {{ user.birthDate | date:'d LLLL' }} </mat-card-subtitle> </mat-card-header> <mat-card-content> <mat-tab-group> <mat-tab label="Bio"> <p> {{user.bio}} </p> </mat-tab> <!-- <mat-tab label="Notes"></mat-tab> --> </mat-tab-group> </mat-card-content> </mat-card> </div>
همچنین mat-card را هم بر اساس مثال مستندات آن، ابتدا کپی و سپس سفارشی سازی کردهایم (اگر دقت کنید، هر کامپوننت آن سه برگهی overview، سپس API و در آخر Example را به همراه دارد). این روشی است که همواره میتوان با کامپوننتهای این مجموعه انجام داد. ابتدا مثالی را در مستندات آن پیدا میکنیم که مناسب کار ما باشد. سپس سورس آنرا از همانجا کپی و در برنامه قرار میدهیم و در آخر آنرا بر اساس اطلاعات خود سفارشی سازی میکنیم.
نمایش جزئیات اولین کاربر در حین بارگذاری اولیهی برنامه
تا اینجای کار اگر برنامه را از ابتدا بارگذاری کنیم، mat-spinner قسمت نمایش جزئیات تماسها ظاهر میشود و همانطور باقی میماند، با اینکه هنوز موردی انتخاب نشدهاست. برای رفع آن به کامپوننت sidnav مراجعه کرده و در لحظهی بارگذاری اطلاعات، اولین مورد را به صورت دستی نمایش میدهیم:
import { Router } from "@angular/router"; @Component() export class SidenavComponent implements OnInit, OnDestroy { users: User[] = []; constructor(private userService: UserService, private router: Router) { } ngOnInit() { this.userService.getAllUsersIncludeNotes() .subscribe(data => { this.users = data; if (data && data.length > 0 && !this.router.navigated) { this.router.navigate(["/contactmanager", data[0].id]); } }); } }
البته روش دیگر مدیریت این حالت، حذف کدهای فوق و تبدیل کدهای کامپوننت main-content به صورت زیر است:
let id = params['id']; if (!id) id = 1;
بستن خودکار sidenav در حالت نمایش موبایل
اگر اندازهی صفحهی نمایشی را کوچکتر کنیم، قسمت sidenav در حالت over نمایان خواهد شد. در این حالت اگر آیتمهای آنرا انتخاب کنیم، هرچند آنها نمایش داده میشوند، اما زیر این sidenav مخفی باقی خواهند ماند:
بنابراین در جهت بهبود کاربری این قسمت بهتر است با کلیک کاربر بر روی sidenav و گزینههای آن، این قسمت بسته شده و ناحیهی زیر آن نمایش داده شود.
در کدهای قالب sidenav، یک template reference variable برای آن به نام sidenav درنظر گرفته شدهاست:
<mat-sidenav #sidenav
import { MatSidenav } from "@angular/material"; @Component() export class SidenavComponent implements OnInit, OnDestroy { @ViewChild(MatSidenav) sidenav: MatSidenav;
ngOnInit() { this.router.events.subscribe(() => { if (this.isScreenSmall) { this.sidenav.close(); } }); }
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: MaterialAngularClient-03.zip
برای اجرای آن:
الف) ابتدا به پوشهی src\MaterialAngularClient وارد شده و فایلهای restore.bat و ng-build-dev.bat را اجرا کنید.
ب) سپس به پوشهی src\MaterialAspNetCoreBackend\MaterialAspNetCoreBackend.WebApp وارد شده و فایلهای restore.bat و dotnet_run.bat را اجرا کنید.
اکنون برنامه در آدرس https://localhost:5001 قابل دسترسی است.
در گذشته نه چندان دور، کوکیها نقش اصلی را در مدیریت کاربران ، و ذخیره اطلاعات کاربران ایفا میکردند. ولی بعد از کشف شدن باگ امنیتی ( که ناشی از اشتباه برنامه نویس بود ) در کوکی ها، برای مدتی کنار گذاشته شدند و اکثر اطلاعات کاربران در session های سمت سرور ذخیره میشد.
با کلیک بر روی لینک Write کوکی data با مقدار مشخص پر میشود.
ذخیره اطلاعات زیاد و نه چندان مهم کاربران در session های سمت سرور ، بار زیادی را به سخت افزار تحمیل میکرد. بعد از این، برنامه نویسان به سمتی استفاده متعادل از هرکدام اینها ( کوکی و سشن) رفتند.
اکثر دوستان با مدیریت سمت سرور کوکیها آشنایی دارند ، بنده قصد دارم در اینجا با استفاده از یک پلاگین جی کوئری مدیریت کوکیها را نمایش دهم.
در این برنامه ما از پلاگی jQuery.cookie استفاده میکنیم که شما میتوانید با مراجعه به صفحه این پلاگین اطلاعات کاملی از این پلاگین به دست بیاورید.
کار با این پلاگین بسیار ساده است.
ابتدا فایل پلاگین را به صفحه خودتون اضافه میکنید.
<script src="/path/to/jquery.cookie.js"></script>
حالا خیلی راحت میتوانید با این دستور یک مقدار را در کوکی قرار دهید.
$.cookie('the_cookie', 'the_value');
و برای گرفتن کوکی نوشته شده هم به این صورت عمل میکنید.
$.cookie('the_cookie'); // => "the_value"
همان طور که دیدید کار بسیار ساده ای است. ولی قدرت این پلاگین در option هایی است که در اختیار ما قرار میدهد.
مثلا شما میتوانید انتخاب کنید این کوکی برای چند روز معتبر باشد ، و یا اطلاعات را به صورت json ذخیره و بازیابی کنید، و حتی option های دیگری برای بحث امنیت کوکی شما.
برای درک بهتر از قطعه کدی که کمی پیچیدهتر از مثال منبع است، استفاده میکنیم.
به کد زیر توجه کنید :
JavaScript :
<script type="text/javascript"> $(function () { $('#write').click(function () { $.cookie('data', '{"iri":"Iran","usa":"United States"}', { expires: 365, json: true }); alert('Writed'); }); $('#show').click(function () { var obj = jQuery.parseJSON($.cookie('data')); alert(obj.iri); }); $('#remove').click(function () { $.removeCookie('data'); }); }) </script>
HTML :
<body> <a href="#" id="write">Write</a> <br /> <a href="#" id="show">Show</a> <br /> <a href="#" id="remove">Remove</a> </body>
در اینجا ما سه لینک داریم که هر کدام برای ما عملی را نمایش میدهند.
توضیحات کد :
$('#write').click(function () { $.cookie('data', '{"iri":"Iran","usa":"United States"}', { expires: 365, json: true }); alert('Writed'); });
با کلیک بر روی لینک Write کوکی data با مقدار مشخص پر میشود.
دقت داشته باشید که این مقدار از نوع json انتخاب شده است و در انتها نیز این را مشخص کرده ایم ، همچنین اعلام کرده ایم که این کوکی برای 365 روز معتبر است.
حالا مرورگر خودتان را ببندید و دوباره باز کنید.
این بار بر روی Show کلیک میکنیم :
$('#show').click(function () { var obj = jQuery.parseJSON($.cookie('data')); alert(obj.iri);
با کلیک بر روی لینک Show مقدار از کوکی خوانده میشود و نمایش داده میشود. دقت کنید ، به دلیل اینکه مقدار ذخیره شده ما از نوع json است باید دوباره این مقدار را pars کنیم تا به مقادیر property آن دسترسی داشته باشیم.
همچنین شما میتوانید خیلی راحت کوکی ساخته شده را از بین ببرید :
$('#remove').click(function () { $.removeCookie('data'); });
و یا این که کوکی را برابر null قرار دهید.
نکته ای که باید رعایت کنید و در این مثال هم نیامده است ، این است که ، هنگامی که شما میخواهید object ی که با کد تولید کرده اید در کوکی قرار بدهید ، باید از متد JSON.stringify استفاده کنید و مقدار را به این صورت در کوکی قرار دهید.
$.cookie('data', JSON.stringify(jsonobject), { expires: 365, json: true });
که در اینجا jsonobject ، ابجکتی است که شما تولید کرده اید و قصد ذخیره آن را دارید.
من از این امکان در نسخه بعدی این پروژه استفاده کرده ام ، و به کمک این پلاگین ساده اما مفید ، وب سایت هایی که کاربر نتایج آن را مشاهده کرده است در کوکی کاربر ذخیره میکنم تا در مراجعه بعدی میزان تغییرات رنکینگهای وب سایت ای در خواست شده را ، به کاربر نمایش دهم. نسخه بعد all-ranks.com تا آخر هفته آینده در سرور اختصاصی ( و نه این هاست رایگان (!)) قرار میگیرد و به مرور قسمت هایی که در این پروژه پیاده سازی شده (پلاگینهای جی کوئری و کدهای سرور ) در اینجا شرح میدهم.
امیدوارم تونسته باشم مطلب مفید و مناسبی به شما دوستان عزیزم انتقال بدم.
سلام
ایا میشه از این کتابخانه به صورت ثانیه ای استفاده کرد ؟ در هر ثانیه چک کنه و در صورت داشتن وظایف در این ساعت و دقیقه و ثانیه (که لیست اونها در دیتابیس هست) بهشون عمل کنه !
مثلا اینطوری اگر کار کنه مشکلی پیش نمیاد ؟
return now.Second % 1 == 0 && now.Second == 1;
در قسمت قبل، نحوهی افزودن قالب ابتدایی ASP.NET Core Identity را به یک برنامهی Blazor Server بررسی کردیم. در این مطلب، قسمتهای ورود و خروج آنرا به همراه نمایش قسمتی از صفحه، تنها به کاربران اعتبارسنجی شده، بررسی میکنیم تا روش دسترسی به اطلاعات ASP.NET Core Identity را در یک برنامهی Blazor Server یکپارچه شدهی با آن، مطالعه کنیم.
نمایش قسمتی از صفحه بر اساس وضعیت اعتبارسنجی کاربر
فرض کنید میخواهیم در کامپوننت Shared\LoginDisplay.razor که در قسمت قبل آنرا اضافه کردیم، لینکهای ثبت نام و لاگین را به کاربران غیر اعتبارسنجی شده (هنوز لاگین نکرده) نمایش دهیم و اگر کاربر، اعتبارسنجی شده بود (لاگین کرده بود)، لینک خروج را به او نمایش دهیم. برای این منظور کامپوننت Shared\LoginDisplay.razor را به صورت زیر تغییر میدهیم:
AuthorizeView، یکی از کامپوننتهای استاندارد Blazor Server است. زمانیکه کاربری به سیستم لاگین کرده باشد، فرگمنت Authorized و در غیر اینصورت قسمت NotAuthorized آنرا مشاهده خواهد کرد.
البته اگر برنامه را در همین حالت اجرا کنیم، به استثنای زیر خواهیم رسید:
برای رفع این مشکل و ارائهی AuthenticationState به تمام کامپوننتهای یک برنامهی Blazor Server، نیاز است از کامپوننت CascadingAuthenticationState استفاده کرد. در مورد پارامترهای آبشاری، در قسمت نهم این سری بیشتر بحث شد و هدف از آن، ارائهی یکسری اطلاعات، به تمام زیر کامپوننتهای یک کامپوننت والد است؛ بدون اینکه نیاز باشد مدام این پارامترها را در هر زیر کامپوننتی، تعریف و تنظیم کنیم. همینقدر که آنها را در بالاترین سطح سلسله مراتب کامپوننتهای تعریف شده تعریف کردیم، در تمام زیر کامپوننتهای آن نیز در دسترس خواهند بود.
بنابراین به فایل BlazorServer.App\App.razor که محل تعریف ریشهی مسیریابی برنامهاست، مراجعه کرده و کامپوننت آنرا با کامپوننت توکار CascadingAuthenticationState محصور میکنیم:
اینکار سبب میشود تا اطلاعات AuthenticationState، بین تمام کامپوننتهای یک برنامهی Blazor به اشتراک گذاشته شود.
اکنون اگر برنامه را اجرا کنیم، مشاهده خواهیم کرد که در اولین بار مراجعهی به آن (پیش از لاگین)، لینک به صفحهی خروج، نمایش داده نشدهاست؛ چون آنرا در فرگمنت مخصوص Authorized قرار دادیم:
آزمایش نمایش منوی خروج برنامه
برای آزمایش برنامه، نیاز است ابتدا یک کاربر جدید را ثبت کنیم؛ چون هنوز هیچ کاربری در آن ثبت نشدهاست و همچنین کاربر پیشفرضی را هم به همراه ندارد. در مورد روش ثبت کاربران پیشفرض ASP.NET Core Identity، میتوانید به مطلب «بازنویسی متد مقدار دهی اولیهی کاربر ادمین در ASP.NET Core Identity توسط متد HasData در EF Core» مراجعه کنید و تمام نکات آن، در اینجا هم صادق است (چون پایهی سیستم Identity مورد استفاده، یکی است و هدف ما در اینجا بیشتر بررسی نکات یکپارچه سازی آن با Blazor Server است و نه مرور تمام نکات ریز Identity).
بنابراین ابتدا از منوی بالای صفحه، گزینهی Register را انتخاب کرده و کاربری را ثبت میکنیم. پس از ثبت نام، بلافاصله به منوی جدید زیر میرسیم که در آن گزینههای ورود و ثبت نام، مخفی شدهاند و اکنون گزینهی خروج از سیستم را نمایش میدهد:
بهبود تجربهی کاربری خروج از سیستم
در همین حال که گزینهی خروج نمایش داده شدهاست، اگر بر روی لینک آن کلیک کنیم، ابتدا ما را به صفحهی مجزای logout هدایت میکند. سپس باید در این صفحه، مجددا بر روی لینک logout بالای آن کلیک کنیم. زمانیکه اینکار را انجام دادیم، اکنون صفحهی دیگری را نمایش میدهد که به همراه پیام «خروج موفقیت آمیز از سیستم» است! در این پروسه، کاربر احساس میکند که کاملا از برنامهی اصلی خارج شدهاست و همچنین مراحل طولانی را نیز باید طی کند.
مدیریت این مراحل توسط دو فایل زیر انجام میشوند:
Areas\Identity\Pages\Account\Logout.cshtml
Areas\Identity\Pages\Account\Logout.cshtml.cs
میخواهیم کدهای این دو فایل را به نحوی تغییر دهیم که اگر کاربری بر روی لینک logout برنامهی اصلی کلیک کرد، به صورت خودکار logout شده و سپس مجددا به صفحهی اصلی برنامهی Blazor Server هدایت شود و مجبور نباشد تا مراحل طولانی یاد شده را تکرار کند.
به همین جهت ابتدا فایل Logout.cshtml.cs را حذف میکنیم؛ چون نیازی به آن نداریم. سپس محتوای فایل Logout.cshtml را به صورت زیر تغییر میدهیم:
با استفاده از سرویس SignInManager در ASP.NET Core Identity میتوان یک کاربر را logout کرد که نمونهای از آنرا در اینجا مشاهده میکنید. در این حالت بررسی میشود که آیا کاربر جاری، به سیستم وارد شدهاست؟ اگر بله، کوکیهای او حذف شده و سپس به صفحهی اصلی برنامه، Redirect میشود. به این ترتیب به تجربهی کاربری خروج بهتری خواهیم رسید.
نمایش User Claims، در یک برنامهی Blazor Server
سیستم ASP.NET Core Identity، بر اساس User Claims کار میکند؛ اطلاعات بیشتر. پس از استفاده از CascadingAuthenticationState در بالاترین سطح برنامه، اطلاعات آن در سراسر برنامهی Blazor Server هم قابل دسترسی است. برای مثال در کامپوننت Shared\LoginDisplay.razor، به نحو زیر میتوان نام کاربر ثبت نام شده را که یکی از User Claims او است، نمایش داد:
محدود کردن دسترسی به صفحات برنامه تنها برای کاربران اعتبارسنجی شده
پس از لاگین موفق به سیستم، اکنون میخواهیم دسترسی به صفحات تعریف اتاقها و یا امکانات رفاهی هتل را تنها به کاربران لاگین شده، محدود کنیم. برای اینکار تنها کافی است از ویژگی Authorize استفاده کنیم. برای مثال به کامپوننت Pages\HotelRoom\HotelRoomList.razor مراجعه کرده و یک سطر زیر را به آن اضافه میکنیم:
دسترسی به کامپوننتی که دارای دایرکتیو فوق باشد، تنها مختص به کاربران اعتبارسنجی شدهی سیستم است.
مشکل! با اینکه تمام کامپوننتهای مثال جاری را به ویژگی Authorize مزین کردهایم، اما ... کار نمیکند! و هنوز هم میتوان بدون لاگین به سیستم، به محتوای آنها دسترسی داشت.
برای رفع این مشکل، مجددا نیاز است کامپوننت BlazorServer.App\App.razor را ویرایش کرد:
در اینجا RouteView پیشین را کامنت کرده و با AuthorizeRouteView، جایگزین کردهایم. کار آن فعالسازی پردازش ویژگی Authorize افزوده شدهی به کامپوننتهای برنامهاست. همچنین در اینجا محتوای سفارشی را که در صورت درخواست یک چنین کامپوننتهایی نمایش داده میشود، در فرگمنت NotAuthorized مشاهده میکنید؛ که حتی میتواند یک کامپوننت مجزا هم باشد:
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید: Blazor-5x-Part-22.zip
نمایش قسمتی از صفحه بر اساس وضعیت اعتبارسنجی کاربر
فرض کنید میخواهیم در کامپوننت Shared\LoginDisplay.razor که در قسمت قبل آنرا اضافه کردیم، لینکهای ثبت نام و لاگین را به کاربران غیر اعتبارسنجی شده (هنوز لاگین نکرده) نمایش دهیم و اگر کاربر، اعتبارسنجی شده بود (لاگین کرده بود)، لینک خروج را به او نمایش دهیم. برای این منظور کامپوننت Shared\LoginDisplay.razor را به صورت زیر تغییر میدهیم:
<AuthorizeView> <Authorized> <a href="Identity/Account/Logout">Logout</a> </Authorized> <NotAuthorized> <a href="Identity/Account/Register">Register</a> <a href="Identity/Account/Login">Login</a> </NotAuthorized> </AuthorizeView>
البته اگر برنامه را در همین حالت اجرا کنیم، به استثنای زیر خواهیم رسید:
InvalidOperationException: Authorization requires a cascading parameter of type Task<AuthenticationState>. Consider using CascadingAuthenticationState to supply this. Microsoft.AspNetCore.Components.Authorization.AuthorizeViewCore.OnParametersSetAsync()
بنابراین به فایل BlazorServer.App\App.razor که محل تعریف ریشهی مسیریابی برنامهاست، مراجعه کرده و کامپوننت آنرا با کامپوننت توکار CascadingAuthenticationState محصور میکنیم:
<CascadingAuthenticationState> <Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true"> <Found Context="routeData"> <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> </Found> <NotFound> <LayoutView Layout="@typeof(MainLayout)"> <p>Sorry, there's nothing at this address.</p> </LayoutView> </NotFound> </Router> </CascadingAuthenticationState>
اکنون اگر برنامه را اجرا کنیم، مشاهده خواهیم کرد که در اولین بار مراجعهی به آن (پیش از لاگین)، لینک به صفحهی خروج، نمایش داده نشدهاست؛ چون آنرا در فرگمنت مخصوص Authorized قرار دادیم:
آزمایش نمایش منوی خروج برنامه
برای آزمایش برنامه، نیاز است ابتدا یک کاربر جدید را ثبت کنیم؛ چون هنوز هیچ کاربری در آن ثبت نشدهاست و همچنین کاربر پیشفرضی را هم به همراه ندارد. در مورد روش ثبت کاربران پیشفرض ASP.NET Core Identity، میتوانید به مطلب «بازنویسی متد مقدار دهی اولیهی کاربر ادمین در ASP.NET Core Identity توسط متد HasData در EF Core» مراجعه کنید و تمام نکات آن، در اینجا هم صادق است (چون پایهی سیستم Identity مورد استفاده، یکی است و هدف ما در اینجا بیشتر بررسی نکات یکپارچه سازی آن با Blazor Server است و نه مرور تمام نکات ریز Identity).
بنابراین ابتدا از منوی بالای صفحه، گزینهی Register را انتخاب کرده و کاربری را ثبت میکنیم. پس از ثبت نام، بلافاصله به منوی جدید زیر میرسیم که در آن گزینههای ورود و ثبت نام، مخفی شدهاند و اکنون گزینهی خروج از سیستم را نمایش میدهد:
بهبود تجربهی کاربری خروج از سیستم
در همین حال که گزینهی خروج نمایش داده شدهاست، اگر بر روی لینک آن کلیک کنیم، ابتدا ما را به صفحهی مجزای logout هدایت میکند. سپس باید در این صفحه، مجددا بر روی لینک logout بالای آن کلیک کنیم. زمانیکه اینکار را انجام دادیم، اکنون صفحهی دیگری را نمایش میدهد که به همراه پیام «خروج موفقیت آمیز از سیستم» است! در این پروسه، کاربر احساس میکند که کاملا از برنامهی اصلی خارج شدهاست و همچنین مراحل طولانی را نیز باید طی کند.
مدیریت این مراحل توسط دو فایل زیر انجام میشوند:
Areas\Identity\Pages\Account\Logout.cshtml
Areas\Identity\Pages\Account\Logout.cshtml.cs
میخواهیم کدهای این دو فایل را به نحوی تغییر دهیم که اگر کاربری بر روی لینک logout برنامهی اصلی کلیک کرد، به صورت خودکار logout شده و سپس مجددا به صفحهی اصلی برنامهی Blazor Server هدایت شود و مجبور نباشد تا مراحل طولانی یاد شده را تکرار کند.
به همین جهت ابتدا فایل Logout.cshtml.cs را حذف میکنیم؛ چون نیازی به آن نداریم. سپس محتوای فایل Logout.cshtml را به صورت زیر تغییر میدهیم:
@page @using Microsoft.AspNetCore.Identity @inject SignInManager<IdentityUser> SignInManager @functions { public async Task<IActionResult> OnGet() { if (SignInManager.IsSignedIn(User)) { <p>You have successfully logged out of the application.</p> await SignInManager.SignOutAsync(); } return Redirect("~/"); } }
نمایش User Claims، در یک برنامهی Blazor Server
سیستم ASP.NET Core Identity، بر اساس User Claims کار میکند؛ اطلاعات بیشتر. پس از استفاده از CascadingAuthenticationState در بالاترین سطح برنامه، اطلاعات آن در سراسر برنامهی Blazor Server هم قابل دسترسی است. برای مثال در کامپوننت Shared\LoginDisplay.razor، به نحو زیر میتوان نام کاربر ثبت نام شده را که یکی از User Claims او است، نمایش داد:
<AuthorizeView> <Authorized> Hello, @context.User.Identity.Name <a href="Identity/Account/Logout">Logout</a> </Authorized>
محدود کردن دسترسی به صفحات برنامه تنها برای کاربران اعتبارسنجی شده
پس از لاگین موفق به سیستم، اکنون میخواهیم دسترسی به صفحات تعریف اتاقها و یا امکانات رفاهی هتل را تنها به کاربران لاگین شده، محدود کنیم. برای اینکار تنها کافی است از ویژگی Authorize استفاده کنیم. برای مثال به کامپوننت Pages\HotelRoom\HotelRoomList.razor مراجعه کرده و یک سطر زیر را به آن اضافه میکنیم:
@attribute [Authorize]
مشکل! با اینکه تمام کامپوننتهای مثال جاری را به ویژگی Authorize مزین کردهایم، اما ... کار نمیکند! و هنوز هم میتوان بدون لاگین به سیستم، به محتوای آنها دسترسی داشت.
برای رفع این مشکل، مجددا نیاز است کامپوننت BlazorServer.App\App.razor را ویرایش کرد:
<CascadingAuthenticationState> <Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true"> <Found Context="routeData"> @*<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />*@ <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"> <NotAuthorized> <p>Sorry, you do not have access to this page</p> </NotAuthorized> </AuthorizeRouteView> </Found> <NotFound> <LayoutView Layout="@typeof(MainLayout)"> <p>Sorry, there's nothing at this address.</p> </LayoutView> </NotFound> </Router> </CascadingAuthenticationState>
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید: Blazor-5x-Part-22.zip
نظرات مطالب
فقط به خاطر یک نیم فاصله!
بله. لینک به همان صفحه است (http://www.isiri.org/std/2901.htm). ممنون.
نظرات مطالب
هزینه استفاده از دات نت فریم ورک چقدر است؟
- من نمیدونم محدود بودن تعداد breakpoints چه اهمیتی داره، یا اینکه چندتا مثلا شما رو راضی میکنه. یا اینکه مثلا کامپایلر CPP چه ربطی به بحث دات نت داره. خوشبختانه پروژه سورس باز CodeBlocks موجود است و کامپایلر GCC لینوکس رو در ویندوز به صورت یکپارچه به شما ارائه میده (http://www.codeblocks.org/). بحث ما اینجا در مورد دات نت است در مورد کدهای مدیریت شده آن.
- من در مورد مایکروسافت عرض کردم نه در مورد ناول. در مورد قراردادهای مایکروسافت با ناول و پشتیبانی مایکروسافت از ناول. در مورد استفاده کنندگان از مونو مراجعه کنید به کامنتهای لینکی که در مورد شرکت زاماریان داده شد. لینکش در متن فوق هست.
- به این اصطلاحا میگن توهم توطئه ... (+)
و صد البته اگر در رابطه با کاهش هزینههای توسعه با دات نت باشد بسیار نیکو و پسندیده است و در راستای بحث جاری. اون شرکت معروف سان هم اگر کارش درست بود الان سرپا بود. هنوز نتونسته جاوای 7 رو جمع کنه. سیستم JIT اون باگ داره. کسی جرات نمیکنه ازش در محیط توسعه استفاده کنه : (+)
من فقط خواستم ثابت کنم با حداقل هزینه و با یک مجوز اولیه ویندوز راحت میشود با دات نت برنامه توسعه داد. هزینه زیادی هم ندارد. لطفا بحث رو به حاشیه نکشید.
- من در مورد مایکروسافت عرض کردم نه در مورد ناول. در مورد قراردادهای مایکروسافت با ناول و پشتیبانی مایکروسافت از ناول. در مورد استفاده کنندگان از مونو مراجعه کنید به کامنتهای لینکی که در مورد شرکت زاماریان داده شد. لینکش در متن فوق هست.
- به این اصطلاحا میگن توهم توطئه ... (+)
و صد البته اگر در رابطه با کاهش هزینههای توسعه با دات نت باشد بسیار نیکو و پسندیده است و در راستای بحث جاری. اون شرکت معروف سان هم اگر کارش درست بود الان سرپا بود. هنوز نتونسته جاوای 7 رو جمع کنه. سیستم JIT اون باگ داره. کسی جرات نمیکنه ازش در محیط توسعه استفاده کنه : (+)
من فقط خواستم ثابت کنم با حداقل هزینه و با یک مجوز اولیه ویندوز راحت میشود با دات نت برنامه توسعه داد. هزینه زیادی هم ندارد. لطفا بحث رو به حاشیه نکشید.
نظرات مطالب
هزینه استفاده از دات نت فریم ورک چقدر است؟
- من نمیدونم محدود بودن تعداد breakpoints چه اهمیتی داره، یا اینکه چندتا مثلا شما رو راضی میکنه. یا اینکه مثلا کامپایلر CPP چه ربطی به بحث دات نت داره. خوشبختانه پروژه سورس باز CodeBlocks موجود است و کامپایلر GCC لینوکس رو در ویندوز به صورت یکپارچه به شما ارائه میده (http://www.codeblocks.org/). بحث ما اینجا در مورد دات نت است در مورد کدهای مدیریت شده آن.
- من در مورد مایکروسافت عرض کردم نه در مورد ناول. در مورد قراردادهای مایکروسافت با ناول و پشتیبانی مایکروسافت از ناول. در مورد استفاده کنندگان از مونو مراجعه کنید به کامنتهای لینکی که در مورد شرکت زاماریان داده شد. لینکش در متن فوق هست.
- به این اصطلاحا میگن توهم توطئه ... (+)
و صد البته اگر در رابطه با کاهش هزینههای توسعه با دات نت باشد بسیار نیکو و پسندیده است و در راستای بحث جاری. اون شرکت معروف سان هم اگر کارش درست بود الان سرپا بود. هنوز نتونسته جاوای 7 رو جمع کنه. سیستم JIT اون باگ داره. کسی جرات نمیکنه ازش در محیط توسعه استفاده کنه : (+)
من فقط خواستم ثابت کنم با حداقل هزینه و با یک مجوز اولیه ویندوز راحت میشود با دات نت برنامه توسعه داد. هزینه زیادی هم ندارد. لطفا بحث رو به حاشیه نکشید.
- من در مورد مایکروسافت عرض کردم نه در مورد ناول. در مورد قراردادهای مایکروسافت با ناول و پشتیبانی مایکروسافت از ناول. در مورد استفاده کنندگان از مونو مراجعه کنید به کامنتهای لینکی که در مورد شرکت زاماریان داده شد. لینکش در متن فوق هست.
- به این اصطلاحا میگن توهم توطئه ... (+)
و صد البته اگر در رابطه با کاهش هزینههای توسعه با دات نت باشد بسیار نیکو و پسندیده است و در راستای بحث جاری. اون شرکت معروف سان هم اگر کارش درست بود الان سرپا بود. هنوز نتونسته جاوای 7 رو جمع کنه. سیستم JIT اون باگ داره. کسی جرات نمیکنه ازش در محیط توسعه استفاده کنه : (+)
من فقط خواستم ثابت کنم با حداقل هزینه و با یک مجوز اولیه ویندوز راحت میشود با دات نت برنامه توسعه داد. هزینه زیادی هم ندارد. لطفا بحث رو به حاشیه نکشید.