مسیریابی در +Angular 2
عموما از مسیریابی جهت حرکت بین Viewهای مختلف برنامه استفاده میشود، اما کارهای بیشتری را نیز میتوان با آن انجام داد؛ مانند ارسال اطلاعات، به مسیریابیها، پیش بارگذاری اطلاعات، جهت نمایش در Viewها، گروه بندی و محافظت از مسیریابیها، پویانمایی و انیمیشن و همچنین بهبود کارآیی، با بارگذاری async مسیرهای مختلف.
کار سیستم مسیریاب +Angular 2 زمانی شروع میشود که تغییری را در آدرس درخواستی از برنامه مشاهده میکند؛ یا از طریق درخواست آدرسی توسط مرورگر و یا هدایت به قسمتی خاص، از طریق کدنویسی. سپس مسیریاب به آرایهی تنظیم شدهی مسیرهای سیستم مراجعه میکند تا بتواند تطابقی را بین آدرس درخواستی و یکی از کلیدهای تنظیم شدهی در آن پیدا کند. در این حالت اگر تطابقی یافت نشود، کارمسیریابی خاتمه خواهد یافت. در غیراینصورت کار ادامه یافته و سپس مسیریاب، محافظهای مسیر درخواستی را بررسی میکند تا مشخص شود که آیا کاربر مجاز به هدایت به این قسمت خاص از برنامه هست یا خیر؟ در صورت مثبت بودن پاسخ، مرحلهی بعد، پیش بارگذاری اطلاعات درخواستی جهت نمایش View مرتبط است. در ادامه کامپوننت متناظر با مسیریابی فعالسازی میشود. سپس قالب این کامپوننت را در قسمتی که توسط router-outlet مشخص میگردد، جایگذاری کرده و نمایش میدهد.
تعریف مسیر پایه یا Base path
اولین مرحلهی کار با سیستم مسیریابی +Angular 2، تعریف یک base path است. مسیرپایه، به زیرپوشهای اشاره میکند که برنامهی ما در آن قرار گرفتهاست:
www.mysite.com/myapp
مسیریاب از این مسیرپایه جهت ساخت آدرسهای مسیریابی استفاده میکند. مقدار آن نیز به صورت ذیل در فایل index.html برنامه، درست پس از تگ head تعیین میگردد:
<!DOCTYPE html> <html> <head> <base href="/">
<base href="/myapp/">
تعیین مسیرپایه جهت ارائهی نهایی
استفاده از مسیر پایه / برای حالت توسعه و همچنین زمانیکه برنامهی نهایی شما در ریشهی سایت توزیع میشود، بسیار مناسب است. اما اگر برای حالت توسعه از مقدار / و برای حالت توزیع از مقدار /myapp/ بخواهید استفاده کنید، مدام نیاز خواهید داشت تا فایل index.html نهایی سایت را ویرایش کنید. برای این منظور Angular CLI دارای پرچمی است به نام base-href:
> ng build --base-href /myapp/
حالت پیش فرض تولید برنامههای Angular توسط Angular CLI، تنظیم مسیرپایه در فایل src\index.html به صورت خودکار به / میباشد.
تعریف مسیریاب Angular
مسیریاب Angular در ماژولی به نام RouterModule قرار گرفتهاست و باید در ابتدای کار import شود. این ماژول شامل سرویسی است جهت هدایت کاربران به صفحات دیگر و مدیریت URLها، تنظیماتی برای تعریف جزئیات مسیریابیها و تعدادی دایرکتیو که برای فعالسازی و نمایش مسیرها از آنها استفاده میشود. برای مثال دایرکتیو RouterLink آن یک المان قابل کلیک HTML را به مسیر و کامپوننتی خاص در برنامه متصل میکند. RouterLinkActive، شیوهنامهها را به لینک فعال انتساب میدهد و RouterOutlet محل نمایش قالب کامپوننت فعال شده را مشخص میکند.
یک مثال: در ادامه، یک پروژهی جدید مبتنی بر Angular CLI را به نام angular-routing-lab به همراه تنظیمات ابتدایی مسیریابی آن ایجاد میکنیم:
> ng new angular-routing-lab --routing
> npm install bootstrap --save
"apps": [ { "styles": [ "../node_modules/bootstrap/dist/css/bootstrap.min.css", "styles.css" ],
در ادامه اگر به فایل src\app\app-routing.module.ts مراجعه کنید، یک چنین محتوایی را خواهید یافت:
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; const routes: Routes = [ { path: '', children: [] } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
میتوان این قسمت را خلاصه کرد و فایل app-routing.module.ts را نیز حذف کرد و سپس import لازم و تعریف ماژول آنرا به ماژول آغازین برنامه یا همان src\app\app.module.ts نیز منتقل کرد. اما پس از مدتی تنظیمات مسیریابی آن، فایل ماژول اصلی برنامه را بیش از اندازه شلوغ خواهند کرد. بنابراین Angular-CLI تصمیم به ایجاد یک ماژول مستقل را برای تعریف تنظیمات مسیریابی برنامه گرفتهاست. سپس تعریف آن را به فایل src\app\app.module.ts به صورت خودکار اضافه میکند:
import { AppRoutingModule } from './app-routing.module'; @NgModule({ imports: [ AppRoutingModule ],
اگر به قسمت import مربوط به NgModule فایل src\app\app-routing.module.ts دقت کنید، این ماژول به همراه متد forRoot معرفی شدهاست.
@NgModule({ imports: [RouterModule.forRoot(routes)],
الف) forRoot
- کار آن تعریف دایرکتیوهای مسیریابی، مدیریت تنظیمات مسیریابی و ثبت سرویس مسیریابی است.
- نکتهی مهم اینجا است که متد forRoot تنها یکبار باید در طول عمر یک برنامه تعریف شود.
- این متد آرایهای از تنظیمات مسیریابیهای تعریف شده را دریافت میکند.
ب) forChild
- کار آن تعریف دایرکتیوهای مسیریابی و مدیریت تنظیمات مسیریابی است؛ اما سرویس مسیریابی را مجددا ثبت نمیکند.
- از این متد در جهت تعریف مسیریابیهای ماژولهای ویژگیهای مختلف برنامه و نظم بخشیدن به آنها استفاده میشود.
بنابراین زمانیکه از forRoot استفاده میشود، سرویس مسیریابی تنها یکبار ثبت خواهد شد و تنها یک وهله از آن موجود خواهد بود. در ادامه هر کدام از ماژولهای دیگر برنامه میتوانند forChild خاص خودشان را داشته باشند.
اکنون تمام کامپوننتهای قید شدهی در قسمت declaration، امکان دسترسی به دایرکتیوهای مسیریابی را پیدا میکنند. همچنین از آنجائیکه AppRoutingModule به همراه متد forRoot است، سرویس مسیریابی نیز در کل برنامه قابل دسترسی است.
تنظیمات اولیه مسیریابی برنامه
آرایهی const routes: Routes فایل src\app\app-routing.module.ts در ابتدای کار خالی است. در اینجا کار تعریف URL segments و سپس اتصال آنها به کامپوننتهای متناظری جهت فعالسازی و نمایش قالب آنها صورت میگیرد. این نمایش نیز در محل router-outlet تعریف شدهی در فایل src\app\app.component.html انجام میشود:
<h1> {{title}} </h1> <router-outlet></router-outlet>
در ادامه برای تکمیل مثال جاری، دو کامپوننت جدید خوشآمد گویی و همچنین یافتن نشدن مسیرها را به برنامه اضافه میکنیم:
>ng g c welcome >ng g c PageNotFound
@NgModule({ declarations: [ AppComponent, WelcomeComponent, PageNotFoundComponent ],
سپس فایل src\app\app-routing.module.ts را به نحو ذیل تکمیل نمائید:
import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; import { WelcomeComponent } from './welcome/welcome.component'; import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; const routes: Routes = [ { path: 'welcome', component: WelcomeComponent }, { path: '', redirectTo: 'welcome', pathMatch: 'full' }, { path: '**', component: PageNotFoundComponent }, ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
یک نکته: افزونهی auto import، کار تعریف کامپوننتها را در VSCode بسیار ساده میکند و امکان تشکیل خودکار قسمت import را با ارائهی یک intellisense به همراه دارد.
سپس کار تکمیل آرایهی Routes انجام شدهاست. همانطور که مشاهده میکنید، این آرایه متشکل است از اشیایی که به همراه خاصیت path و سایر پارامترهای مورد نیاز هستند.
کار خاصیت path، تعیین URL segment متناظری است که این مسیریابی را فعال میکند. برای مثال اولین شیء تعریف شده با آدرسهایی مانند www.mysite.com/welcome متناظر است.
{ path: 'welcome', component: WelcomeComponent },
چند نکته:
- در حین تعریف مقدار خاصیت path، هیچ / آغاز کنندهای تعریف نشدهاست.
- مقدار خاصیت path، حساس به کوچکی و بزرگی حروف است.
- WelcomeComponent تعریف شده، یک رشته نیست و ارجاعی را به کامپوننت مرتبط دارد. به همین جهت نیاز به import statement ابتدایی را دارد و وجود آن توسط کامپایلر بررسی میشود.
تعیین مسیریابی پیش فرض سایت
اما زمانیکه برنامه برای بار اول بارگذاری میشود، چطور؟ در این حالت هیچ URL segment ایی وجود ندارد. بنابراین برای تنظیم مسیرپیش فرض سایت، خاصیت path، به یک رشتهی خالی همانند دومین شیء تنظیمات مسیریابی، تنظیم میشود:
{ path: '', redirectTo: 'welcome', pathMatch: 'full' },
مدیریت مسیریابی آدرسهای ناموجود در سایت
تنظیم سومی را نیز در اینجا مشاهده میکنید:
{ path: '**', component: PageNotFoundComponent },
یک نکته: ترتیب مسیریابیها در آرایهی تعریف آنها اهمیت دارد. در اینجا از استراتژی «اولین تطابق یافته، برنده خواهد بود» استفاده میشود. بنابراین تنظیم ** باید در انتهای لیست ذکر شود؛ در غیراینصورت هیچکدام از مسیریابیهای تعریف شدهی پس از آن پردازش نخواهند شد.
مدیریت تغییرات آدرسهای برنامه
در طول عمر برنامه ممکن است نیاز به تغییر آدرسهای برنامه باشد. برای مثال بجای مسیر welcome مسیر home نمایش داده شود:
const routes: Routes = [ { path: 'home', component: WelcomeComponent }, { path: 'welcome', redirectTo: 'home', pathMatch: 'full' }, { path: '', redirectTo: 'welcome', pathMatch: 'full' }, { path: '**', component: PageNotFoundComponent } ];
نکته: redirectToها قابلیت تعریف زنجیرهای را ندارند. به این معنا که اگر ریشهی سایت درخواست شود، ابتدا به مسیر welcome هدایت خواهیم شد. مسیر welcome هم یک redirectTo دیگر به مسیر home را دارد. اما در اینجا کار به این redirectTo دوم نخواهد رسید و این پردازش، زنجیرهای نیست. بنابراین مسیریابی پیشفرض را نیز باید ویرایش کرد و به home تغییر داد:
const routes: Routes = [ { path: 'home', component: WelcomeComponent }, { path: 'welcome', redirectTo: 'home', pathMatch: 'full' }, { path: '', redirectTo: 'home', pathMatch: 'full' }, { path: '**', component: PageNotFoundComponent } ];
نکته: redirectToها میتوانند local و یا absolute باشند. تعریف محلی آنها مانند ذکر home و welcome در اینجا است و تنها سبب تغییر یک URL segment میشود. اما اگر در ابتدای مقادیر redirectToها یک / قرار دهیم، به معنای تعریف یک مسیر مطلق است و کل URL را جایگزین میکند.
تعیین محل نمایش قالبهای کامپوننتها
زمانیکه یک کامپوننت فعالسازی میشود، قالب آن در router-outlet نمایش داده خواهد شد. برای این منظور فایل src\app\app.component.html را گشوده و به نحو ذیل تغییر دهید:
<nav class="navbar navbar-default"> <div class="container-fluid"> <a class="navbar-brand">{{title}}</a> <ul class="nav navbar-nav"> <li> <a [routerLink]="['/home']">Home</a> </li> </ul> </div> </nav> <div class="container"> <router-outlet></router-outlet> </div>
یک نکته: چون کامپوننت welcome از طریق مسیریابی نمایش داده میشود و دیگر به صورت مستقیم با درج تگ selector آن در صفحه فعالسازی نخواهد شد، میتوان به تعریف کامپوننت آن مراجعه کرده و selector آنرا حذف کرد.
@Component({ //selector: 'app-welcome', templateUrl: './welcome.component.html', styleUrls: ['./welcome.component.css'] })
تا اینجا اگر دستور ng serve -o را صادر کنیم (کار build درون حافظهای جهت محیط توسعه و نمایش خودکار برنامه در مرورگر)، چنین خروجی در مرورگر نمایان خواهد شد:
اگر به آدرس تنظیم شدهی در مرورگر دقت کنید، http://localhost:4200/home آدرسی است که در ابتدای نمایش سایت نمایان خواهد شد. علت آن نیز به تنظیم مسیریابی پیش فرض سایت برمیگردد.
و اگر یک مسیر غیرموجود را درخواست دهیم، قالب کامپوننت PageNotFound ظاهر میشود:
هدایت کاربران به قسمتهای مختلف برنامه
کاربران را میتوان به روشهای مختلفی به قسمتهای گوناگون برنامه هدایت کرد؛ برای مثال با کلیک بر روی المانهای قابل کلیک HTML و سپس اتصال آنها به کامپوننتهای برنامه. استفادهی کاربر از bookmark مرورگر و یا ورود مستقیم و دستی آدرس قسمتی از برنامه و یا کلیک بر روی دکمههای forward و back مرورگر. تنها مورد اول است که نیاز به تنظیم دارد و سایر قسمتها به صورت خودکار مدیریت خواهند شد. نمونهی آنرا نیز با تعریف لینک Home پیشتر مشاهده کردید:
<a [routerLink]="['/home']">Home</a>
- زمانیکه کاربر بر روی این لینک کلیک میکند، اولین path متناظر با routerLink یافت شده و فعالسازی خواهد شد.
- علت تعریف مقدار routerLink به صورت [] این است که آرایهی پارامترهای لینک را مشخص میکند. بنابراین چون آرایهاست، نیاز به [] دارد. اولین پارامتر این آرایه مفهوم root URL segment را دارد. در اینجا حتما نیاز است URL segment را با یک / شروع کرد. به علاوه باید دقت داشت که خاصیت path تنظیمات مسیریابی، حساس به حروف کوچک و بزرگ است. بنابراین این مورد را باید در اینجا نیز مدنظر داشت.
- پارامترهای دیگر routerLink میتوانند مفهوم پارامترهای این segment و یا حتی segments دیگری باشند.
یک نکته: چون در مثال فوق، آرایهی تعریف شده تنها دارای یک عضو است، آنرا میتوان به صورت ذیل نیز خلاصه نویسی کرد (one-time binding):
<a routerLink="/home">Home</a>
تفاوت بین آدرسهای HTML 5 و Hash-based
زمانیکه مسیریاب Angular کار پردازش آدرسهای رسیده را انجام میدهد، اینکار در سمت کلاینت صورت میگیرد و تنها URL segment مدنظر را تغییر داده و این درخواست را به سمت سرور ارسال نمیکند. به همین جهت سبب reload صفحه نمیشود. دو روش در اینجا جهت مدیریت سمت کلاینت آدرسها قابل استفاده است:
الف) HTML 5 Style
- آدرسی مانند http://localhost:4200/home، یک آدرس به شیوهی HTML 5 است. در اینجا مسیریاب Angular با استفاده از HTML 5 history pushState سبب به روز رسانی History مرورگر شده و آدرسها را بدون ارسال درخواستی به سمت سرور، در همان سمت کلاینت تغییر میدهد.
- این روش حالت پیش فرض Angular است و نحوهی نمایش آن بسیار طبیعی به نظر میرسد.
- در اینجا URL rewriting سمت سرور نیز جهت هدایت آدرسها، به برنامهی Angular ضروری است. برای مثال زمانیکه کاربری آدرس http://localhost:4200/home را مستقیما در مرورگر وارد میکند، این درخواست ابتدا به سمت سرور ارسال خواهد شد و چون چنین صفحهای در سمت سرور وجود ندارد، پیغام خطای 404 را دریافت میکند. اینجا است که URL rewriting سمت سرور به فایل index.html برنامه، جهت مدیریت یک چنین حالتهایی ضروری است.
برای نمونه اگر از وب سرور IIS استفاده میکنید، تنظیم ذیل را به فایل web.config در قسمت system.webServer اضافه کنید (کار کرد آن هم وابستهاست به نصب و فعالسازی ماژول URL Rewrite بر روی IIS):
<rewrite> <rules> <rule name="Angular 2+ pushState routing" stopProcessing="true"> <match url=".*" /> <conditions logicalGrouping="MatchAll"> <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /> <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" /> <add input="{REQUEST_FILENAME}" pattern=".*\.[\d\w]+$" negate="true" /> <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" /> </conditions> <action type="Rewrite" url="/index.html" /> </rule> </rules> </rewrite>
ب) Hash-based
- آدرسی مانند http://localhost:4200/#/home یک آدرس به شیوهی Hash-based بوده و مخصوص مرورگرهایی است بسیار قدیمی که از HTML 5 پشتیبانی نمیکنند. اینبار قطعات قرار گرفتهی پس از علامت # دارای نام URL fragments بوده و قابلیت پردازش در سمت کلاینت را دارا میباشند.
- اگر علاقمند به استفادهی از این روش هستید، نیاز است خاصیت useHash را به true تنظیم کنید:
@NgModule({ imports: [RouterModule.forRoot(routes, { useHash: true })],
Docker Client برای Windows
Today, at Microsoft and Docker, Inc. we are making the Docker command line interface available on Windows starting with the Docker 1.6 release.
پیشتر مطلبی را در مورد ساخت کتابخانههای مخصوص Angular را به کمک Angular CLI، در این سایت مطالعه کرده بودید. در این مطلب فرض بر این است که شما توسعه دهندهی Angular «نیستید»، اما قصد دارید با استفاده از ابزار Angular CLI، کتابخانهی جاوا اسکریپتی عمومی بسیار مدرنی را با پشتیبانی از تمام موارد یاد شده، تولید کنید.
ساخت قالب آغازین کتابخانه به کمک Angular CLI
برای تبدیل کتابخانههای جاوا اسکریپتی خود به قالب مدرنی که در مقدمه عنوان شد، نیاز به ابزاری جهت خودکارسازی فرآیندهای آن داریم و این ویژگیها مدتی است که به ابزار Angular CLI اضافه شدهاند و همانطور که عنوان شد، مخاطب این مطلب، توسعه دهندگان عمومی JavaScript است و نه صرفا توسعه دهندگان Angular. به همین جهت نیاز است ابتدا این ابزار را نصب کرد:
npm install -g @angular/cli
پس از نصب Angular CLI، از آن جهت ساخت قالب تولید کتابخانههای TypeScript ای استفاده میکنیم:
ng new my-math-app
بنابراین پس از اجرای دستور فوق، از طریق خط فرمان به پوشهی my-math-app وارد شده و سپس دستور زیر را اجرا کنید:
ng generate library ts-math-example
تکمیل کتابخانهی جاوا اسکریپتی
همچنین میتوان به فایل my-math-app\projects\ts-math-example\package.json نیز مراجعه کرد (فایل package.json پروژهی کتابخانه) و قسمت peerDependencies آن را که به Angular اشاره میکند نیز حذف نمود.
سپس یک فایل خالی math.ts را به پوشهی یاد شده اضافه میکنیم:
با این محتوا:
export function add(num1: number, num2: number) { return num1 + num2; }
در ادامه نیاز است این ماژول را به فایل my-math-app\projects\ts-math-example\src\public-api.ts معرفی کرد تا به عنوان API قابل دسترسی کتابخانه، در دسترس قرار گیرد:
/* * Public API Surface of ts-math-example */ export * from './lib/math';
در حین توسعهی کتابخانه خود،جهت اطمینان از صحت کامپایل برنامه، دستور ng build ts-math-example --watch را در پوشهی my-math-app صادر کنید. کار آن کامپایل مداوم پروژهی کتابخانه بر اساس تغییرات داده شدهاست. حاصل این کامپایل نیز در پوشهی my-math-app\dist\ts-math-example قرار میگیرد:
این همان خروجی مدرنی است که در ابتدای بحث از آن صحبت کردیم و شامل کتابخانههای ES5 و ES2015 به بعد و همچنین ارائهی متادیتای مخصوص TypeScript نیز هست.
کامپایل و انتشار نهایی کتابخانه
پس از تکمیل کتابخانهی خود، اکنون میتوانیم آنرا به سایت npm، برای استفادهی سایرین ارسال کنیم. برای این منظور باید مراحل زیر طی شوند:
ابتدا فایل package.json واقع در ریشهی پوشهی ts-math-example را جهت تعریف اطلاعات این کتابخانه، تکمیل کنید. سپس دستورات زیر را در ریشهی پروژهی اصلی صادر کنید:
ng build ts-math-example --prod cd dist/ts-math-example npm publish
با دستور دوم به پوشهی خروجی کتابخانه وارد شده و دستور سوم، آنرا به سایت npm ارسال میکند.
استفاده کنندهی از کتابخانهی ما (این استفاده کننده میتواند هر نوع پروژهی جاوا اسکریپتی اعم از Angular ،React ،Vue ،ES6 ،TypeScript و غیره باشد) ابتدا با دستور npm install ts-math-example --save آنرا نصب و به پروژهی خود اضافه کرده و سپس به نحو زیر میتواند از آن استفاده کند:
import { add } from '@myuser/ts-math-example';
پیشنیازها
دو پروژه خالی ASP.NET Web forms و ASP.NET MVC را در VS.NET تحت ویندوز ایجاد نمائید. آنها را یکبار کامپایل کرده و اجرا کنید. سپس فایلهای آنها را به ubuntu منتقل کنید (پوشههای bin پروژهها فراموش نشوند؛ خصوصا نگارش MVC که به همراه یک سری کتابخانه جانبی است).
برای انتقال فایلها به لینوکس، اگر از VMWare workstation برای اجرا و آزمایش Ubuntu استفاده میکنید، کپی و paste مستقیم فایلها از ویندوز به درون ماشین مجازی لینوکس پشتیبانی میشود.
نصب وب سرور آزمایشی مونو یا XSP
اگر نیاز به یک وب سرور آزمایشی، چیزی شبیه به وب سرور توکار VS.NET داشتید، پروژه XSP جهت این نوع آزمایشات ایجاد شده است.
پس از نصب آن (که به همراه همان بسته PPA قسمت قبل، هم اکنون بر روی سیستم شما نصب است)، در ترمینال لینوکس، با استفاده از دستور cd به ریشه وب سایت خود وارد شوید، سپس دستور xsp4 را اجرا کنید تا وب سرور xsp4 مشغول هاست سایت شما شود (برای اجرا در مسیر /opt/bin/xsp4 نصب شده است).
اجرای برنامه ASP.NET Web forms 4 توسط XSP
بدون هیچ مشکل خاصی در همان ابتدای کار اجرا شد (البته باید دقت داشت که لینوکس به کوچکی و بزرگی حروف حساس است. یعنی حتما باید Default.aspx وارد شود و نه default.aspx):
اجرای برنامه ASP.NET MVC 4 توسط XSP
اجرا نشد! پیام میدهد که
"Missing method System.Web.Security.FormsAuthentication::get_IsEnabled() in assembly System.Web.dll
Compiler Error Message: CS1703: An assembly with the same identity `mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' has already been imported. Consider removing one of the references
git clone git://github.com/mono/xsp.git cd xsp ./autogen.sh --prefix=/opt make sudo make install
System.IO.FileNotFoundException: Could not load file or assembly XSP, Version=3.0.0.0
البته من سورس دریافت شده را در خود monodevelop کامپایل کردم (فایل sln آنرا در monodevelop باز کرده و پروژه را build کنید). در این حالت دو فایل Mono.WebServer.dll و Mono.WebServer.XSP.exe در پوشه xsp/src/Mono.WebServer.XSP/bin/Debug ظاهر میشوند.
یکی دیگر از دلایل ظاهر شدن خطای فوق، نیاز به نصب این دو فایل در GAC است که به نحو زیر قابل انجام میباشد:
cd xsp/src/Mono.WebServer.XSP/bin/Debug sudo gacutil -i Mono.WebServer.XSP.exe sudo gacutil -i Mono.WebServer.dll
cd myMvcAppPath sudo mono Mono.WebServer.XSP.exe
CS0234: The type or namespace name `Helpers' does not exist in the namespace `System.Web'. Are you missing an assembly reference?
<system.web> <compilation debug="true" targetFramework="4.0"> <assemblies> <add assembly="System.Web.Helpers, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <add assembly="System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> </assemblies> </compilation>
Microsoft.Web.Infrastructure.dll
System.Net.Http.dll
System.Net.Http.Formatting.dll
System.Web.Http.dll
System.Web.Http.WebHost.dll
این فایلها توسط تیم Mono به صورت مستقل پیاده سازی شدهاند و نیازی نیست تا از ویندوز به لینوکس کپی شوند.
بعد از حذف این فایلهای اضافی، برنامه ASP.NET MVC نیز اجرا شد:
چند نکته تکمیلی
- نحوه تشخیص موجود بودن یک DLL خاص، در نگارش جاری Mono نصب شده:
$ gacutil -l Microsoft.Web.Infrastructure The following assemblies are installed into the GAC: Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 Number of items = 1
cd /opt/lib/mono/gac # assuming this is your main gac sudo find . */*/*.dll -exec gacutil -i '{}' \;
CoffeeScript #11
کامپایل خودکار CoffeeScript
همانطور که گفته شده CoffeeScript یک لایه میان شما و جاوااسکریپت است و هر زمان که فایل CoffeeScript تغییر کرد، باید به صورت دستی آن را کامپایل کرد. خوشبختانه CoffeeScript روشهای دیگری را برای کامپایل کردن دارد که به وسیله آن میتوان چرخهی توسعه را بسیار سادهتر نمود.
در قسمت اول گفته شد، برای کامپایل فایل CoffeeScript با استفاده از coffee به صورت زیر عمل میکردیم:
coffee --compile --output lib src
حال به کامپایل خودکار CoffeeScript توجه کنید.
Cake
Cake یک سیستم فوق العاده ساده برای کامپایل خودکار است که مانند Make و Rake عمل میکند. این کتابخانه همراه پکیج coffee-script npm نصب میشود و برای استفاده با فراخوانی cake اجرا میشود.
برای ایجاد فایل tasks در cake که Cakefile نامیده میشود، میتوان از خود CoffeeScript استفاده کرد. برای اجرای cake با استفاده از دستور [cake [task] [options میتوان عمل کرد. برای اطلاع از لیست امکانات cake کافی است دستور cake را به تنهایی اجرا کنید.
وظایف را میتوان با استفاده از تابع task، با ارسال نام و توضیحات (اختیاری) و تابع callback، تعریف کرد. به مثال زیر توجه کنید:
fs = require 'fs' {print} = require 'sys' {spawn} = require 'child_process' build = (callback) -> coffee = spawn 'coffee', ['-c', '-o', 'lib', 'src'] coffee.stderr.on 'data', (data) -> process.stderr.write data.toString() coffee.stdout.on 'data', (data) -> print data.toString() coffee.on 'exit', (code) -> callback?() if code is 0 task 'build', 'Build lib/ from src/', -> build()
همان طور که مشاهده میکنید پس از تغییر در فایل CoffeeScript باید به صورت دستی cake build را فراخوانی کنیم که این دور از حالت ایده آل است.
خوشبختانه دستور coffee پارامتر دیگری به نام watch-- دارد که به وسیله آن میتوان تمامی تغییرات یک پوشه را زیر نظر گرفت و در صورت نیاز دوباره کامپایل انجام شود. به مثال زیر توجه کنید:
task 'watch', 'Watch src/ for changes', -> coffee = spawn 'coffee', ['-w', '-c', '-o', 'lib', 'src'] coffee.stderr.on 'data', (data) -> process.stderr.write data.toString() coffee.stdout.on 'data', (data) -> print data.toString()
task 'open', 'Open index.html', -> # First open, then watch spawn 'open', 'index.html' invoke 'watch'
option '-o', '--output [DIR]', 'output dir' task 'build', 'Build lib/ from src/', -> # Now we have access to a `options` object coffee = spawn 'coffee', ['-c', '-o', options.output or 'lib', 'src'] coffee.stderr.on 'data', (data) -> process.stderr.write data.toString() coffee.stdout.on 'data', (data) -> print data.toString()
Cake یک روش عالی برای انجام وظایف معمول به صورت خودکار است، مانند کامپایل فایلهای CoffeeScript است. همچنین برای آشنایی بیشتر میتوانید به سورس cake نگاهی کنید.
Employee ID: {Orders.EmployeeID} --- GLine: {GroupLine}
در فرم ظاهر شده مقادیر را به صورت زیر وارد نمایید:
Name: TotalPrice Alias: TotalPrice Type: decimal Value: Order_Details.UnitPrice * Order_Details.Quantity
ایجاد گزارش به صورت Master-Detail:
برای ایجاد چنین گزارشی نیاز به ارتباط بین جدولها میباشد. با توجه به نحوه ایجاد Connection برای این مثال، روابط بین جدولها انتقال داده نشده است ولی شما میتوانید رابطه بین جدولها را اضافه نمایید حتی اگر این رابطه در منبع اطلاعات وجود نداشته باشد. برای این مثال نیاز به دو رابطه بین جدول Orders Detail و جدولهای Orders, Products میباشد. برای انجام این کار کافیست در پنل Dictionary بر روی جدول Orders Detail رایت کلیک کنید و گزینه New Relation را انتخاب نماید. همانند تصاویر زیر مقادیر را ست نمایید.
حال بر روی صفحه طراحی بعد از بند DataBand1 به ترتیب بندهای Header و Data و Footer را اضافه نمایید. در بند HeaderBand2 چهار کامپوننت Text قرار دهید و به ترتیب از سمت چپ مقادیر زیر را در خصوصیت Text کامپوننتها قرار دهید.
ProductName UnitPrice Quantity TotalPrice
{Order_Details.Products.ProductName} {Order_Details.UnitPrice} {Order_Details.Quantity} {Order_Details.TotalPrice}
{Sum(DataBand2,Order_Details.TotalPrice)}
حال میتوانید خروجی گزارش Master-Detail را از سربرگ Preview مشاهده نمایید. در صورتی که همانند تصویر بالا گزارش را طراحی کرده باشید در خروجی گزارش فاصلهای بین سطرها ایجاد شده است که علت آن ارتفاع کمتر کامپوننتهای Text نسبت به بندهای خود میباشد. برای رفع این مشکل، شما سه راه حل دارید.
الف: یکسان سازی ارتفاع کاپوننتها با بند دربرگیرنده آنها
ب: ست کردن خصوصیت Can Shrink بند دربرگیرنده کامپوننتها به مقدار true
ج: ست کردن خصوصیت Grow to Height کامپوننتهای Text به مقدار true
در این مثال ما از روش دوم استفاده میکنیم و خصوصیت Can Shrink بندهای HeaderBand2 و DataBand2 را به مقدار true ست میکنیم.
کامپوننتهای Panel و Clone :
Panel: به شما امکان میدهد تا کامپوننتها و بندها را دربر بگیرد و در واقع گروهی از کامپوننتها را ایجاد خواهد کرد. پنل میتواند مستقل بر روی صفحه طراحی قرار گیرد و یا در یک بند.
Clone: به شما امکان کپی کردن یک بخش از گزارش را میدهد که البته آن بخش فقط میتواند از نوع Panel باشد.
این دو کامپوننت یکی از عوامل قدرت این گزارشساز میباشد. برای شروع یک Page (صفحه طراحی) دیگر به گزارش اضافه کنید. میتوانید با رایت کلیک بر روی نوار سربرگهای محیط طراحی گزینه New Page را انتخاب نمایید. بر روی Page2 یک کامپوننت Panel قرار دهید، سپس از پنل Dictionary جدول Countries را دراگ کرده و در Panel1 رها کنید. در فرم Data تمامی فیلدها و بند Header را انتخاب نمایید، سپس یک کامپوننت Clone به صفحه طراحی اضافه کنید بلافاصله فرم Select Container ظاهر میشود، Panel1 را انتخاب کنید. حال شما میتوانید خروجی گزارش را مشاهده کنید، خروجی Page2 بعد از خروجی Page1 ظاهر خواهد شد.
MongoDB #14
- برای ایمن نگه داری اطلاعات
- دسترسی پذیری بالای اطلاعات (شبانه روزی)
- بازیابی اطلاعات
- نیازی به از کار افتادن هنگام انجام عملیات نگهداری ندارد
- مقایس پذیری خواندن دادهها (کپی برداریهای اضافه برای عمل خواندن)
- کپی اطلاعات برای نرم افزارها شفاف و قابل دستیابی است.
تکثیر در MongoDB چگونه کار میکند
- یک مجموعهی کپی، یک گروه از دویا چند گره است. (عموما حداقل 3 گره نیاز است.)
- در یک مجموعهی کپی، یک گره، گره اصلی است و بقیه گرهها گرههای ثانویه هستند.
- همهی دادهها از گرهی اصلی به گرههای ثانویه تکثیر میشوند.
- هنگام انجام عملیات نگه داری یا ازدسترس خارج شدن سرور، گزینش برای گره اصلی و انتخاب گره اصلی جدید آغاز میشود.
- گره از کار افتاده، بعد از بازیابی دوباره، به مجموعه کپی ملحق میشود و بعنوان یک گره ثانویه کار میکند.
ویژگیهای مجموعهی کپی
- یک کلاستر از N عدد گره
- هر گرهایی میتواند گره اصلی باشد
- همهی عملیات نوشتن بر روی گره اصلی انجام میشود
- عمل ازدسترس خارج شدن سرور و جایگزین شدن یک گره بصورت اتوماتیک
- بازیابی بصورت اتوماتیک
- همراهی و توافق در گزینش گره اصلی
ساختن یک مجموعه کپی
- همهی نمونههای در حال اجرای mongod را در سمت سرور، متوقف کنید.
- اکنون mongod سمت سرور را با سوئیچ –replSet راه اندازی کنید.
mongod --port "PORT" --dbpath "YOUR_DB_DATA_PATH" --replSet "REPLICA_SET_INSTANCE_NAME"
mongod --port 27017 --dbpath "D:\set up\mongodb\data" --replSet rs0
گرامر
>rs.add(HOST_NAME:PORT)
>rs.add("mongod1.net:27017") >
آشنایی با AOP Interceptors
public class HomeViewModel { public string Id { get; set; } public string Message { get; set; } public DateTime DateTime { get; set; } }
اکنون به پوشهی Views بروید و فایل Index.cshtml را به این صورت تغییر دهید:
@model AspNetCoreDependencyInjection.Models.HomeViewModel @{ ViewData["Title"] = "Home"; } <div> <div> <div> <p> <b>Id : </b><span>@Model.Id</span> <br /> <b>Date And Time : </b><span> @Model.DateTime </span> <br/> <b>Message : </b><span>@Model.Message</span> </p> </div> </div> </div>
using AspNetCoreDependencyInjection.Services; namespace AspNetCoreDependencyInjection.ServicesImplentaions { public class MessageServiceAA { public string Message() { return "A message from MessageServiceAA"; } } }
namespace AspNetCoreDependencyInjection.Helpers { public class GuidProvider { private readonly Guid _serviceGuid; public GuidProvider() { _serviceGuid = Guid.NewGuid(); } public Guid GetNewGuid() => Guid.NewGuid(); public string GetGuidAsFormatedString(string prefix = "") => getFormatedGuid(_serviceGuid, prefix); private string getFormatedGuid(Guid guid, string prefix = "") { var guidString = guid.GetHashCode().ToString("x"); if (string.IsNullOrEmpty(prefix) == false) guidString = new StringBuilder($"{prefix}-").Append(guidString).ToString(); return guidString; } } }
حالا درون کنترل HomeController، این تغییرات را انجام میدهیم:
private readonly ILogger<HomeController> _logger; private readonly MessageServiceAA _messageService; private readonly GuidProvider _ guidProvider; public HomeController(ILogger<HomeController> logger) { _logger = logger; _messageService = new MessageServiceAA(); _guidProvider = new GuidProvider(); } public IActionResult Index() { var model = new HomeViewModel() { Id = _ guidProvider.GetGuidAsFormatedString(), Message = _messageService.Message(), DateTime = DateTime.Now, }; return View(model); }
همانطور که میبینید، در کد بالا، کنترلر HomeController، به دو شیء از کلاسها و یا سرویسهای GuidProvider و MessageServiceAA به صورت مستقیم وابسته شدهاست و با هر تغییری در هر کدام از این سرویسها، باید دوباره کامپایل شود. علاوه بر این اگر بخواهیم پیاده سازیهای مختلفی را برای هر کدام از این موارد، ارائه دهیم، به مشکل بر میخوریم. خب بیاید تغییراتی را در کد بالا بدهیم تا مشکلات ذکر شده را حل کنیم.
برای این منظور پوشهای را به نام Services میسازیم و اینترفیسی را
به نام IMessageBrokerA ایجاد میکنیم و سپس کاری میکنیم که MessageServiceAA از این
اینترفیس ارث بری کند:
namespace AspNetCoreDependencyInjection.Services { public interface IMessageServiceA { string Message(); } }
و حالا میخواهیم با
استفاده از تزریق وابستگی، وابستگی کنترلر HomeController را از کلاس MessageBrokerAA لغو کرده و آن را به اینترفیس IMessageBrokerA (انتزاع) وابسته کنیم. در
اینجا ما از تکنیک تزریق درون سازنده یا Constructor Injection استفاده میکنیم.
تزریق درون سازنده
در این تکنیک، ما لیستی از وابستگیهای مورد نیاز را به عنوان پارامترهای ورودی سازندهی کلاس، تعریف میکنیم:private readonly ILogger<HomeController> _logger; private readonly IMessageServiceA _messageService; private readonly GuidProvider _guidHelper; public HomeController(ILogger<HomeController> logger , IMessageServiceA messageService) { _logger = logger; _messageService = messageService; _messageService = new MessageServiceAA(); _guidHelper = new GuidProvider(); }
- IServiceCollection : برای ثبت سرویسها
- IServiceProvider : برای واکشی سرویسها
در ASP.NET Core معمولترین مکان برای ثبت کردن سرویسها درون Container، به صورت پیش فرض درون کلاس Startup و درون متد ConfigureServices انجام میگیرد.
به صورت پیش فرض کلاس Startup دو متد دارد:
- ConfigureServices : برای پیکربندی و ثبت سرویسهای درونی DI Container استفاده میشود.
- Configure : برای تنظیمات pipeline میان افزارها ( Middlewares ) بکار میرود.
در اینجا پیاده سازی پیش فرض کلاس Startup را میبینیم که البته کدهای درون متد Configure را برای درگیر نکردن ذهن شما، مخفی کردهایم:
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // کدها جهت خوانایی بیشتر مخفی شده اند } }
همانطورکه میبینید، متد ConfigureService پارامتر IServiceCollection را میگیرد که به وسیلهی WebHost در زمان اجرای برنامه، مقدار دهی میشود.
تعداد زیادی Extension method برای IServiceCollection وجود دارند که برای پشتیبانی از ثبت کردن سرویسهای مختلف در سناریوهای گوناگون به کار میروند. در اینجا ما از نسخهی 3.1 چارچوب ASP.NET Core استفاده میکنیم. برای همین هم برای ثبت سرویسهای پیش فرض فریمورک MVC از متد توسعهی services.AddControllersWithViews() استفاده میکنیم. متد توسعهی AddControllersWithViews() سرویسهایی را که معمولا در فریم ورک MVC استفاده میشوند، درون IServiceCollection ثبت میکند. در نسخههای قبلی چارچوب ASP.NET Core، مانند نسخههای 2.1 و 2.2 برای این کار از متد توسعهی AddMvc() استفاده میشد.
در Microsoft Dependency Injection Container ، معمولا ترتیب ثبت سرویسها مهم نیست.
خب، اولین سرویس اختصاصی برنامهی خودمان را با چرخهی حیات Transient و زیر سرویس پیشین، به شکل زیر ثبت میکنیم :
public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); services.AddTransient<IMessageServiceA, MessageServiceAA>(); }
public static IServiceCollection AddTransient<TService, TImplementation>(this IServiceCollection services)
در اینجا وقتی ما برای IMessageServiceA ، پیاده سازی MessageServiceA را ثبت میکنیم، از این به بعد DI Container، هر زمانیکه در لیست پارامترهای سازندهی یک کلاس، IMessageServiceA را مشاهده کند، بررسی میکند که چه کلاسی به عنوانی پیاده سازی این اینترفیس ثبت شدهاست، سپس از آن نمونه سازی میکند و درون سازندهی مورد نظر تزریق میکند. خب، حالا برنامه را دوباره اجرا کنید؛ میبینید که برنامه اجرا میشود.