In this video we perform a full step by step build of a .NET MAUI App that we test on both Windows and Android. The app interacts with a separate .NET 6 API that we also build step by step.
Level: Beginner
⏲️ Time Codes ⏲️
Theory
- 0:48 Welcome
- 03:13 App demo
- 06:07 Course overview
- 09:14 Ingedients
- 10:10 What is .NET MAUI?
- 12:48 How MAUI works
- 15:14 MAUI project anatomy
- 19:47 MAUI App start up sequence
- 22:29 UI Conepts
- 28:21 XAML vs C#
- 30:29 Solution Architecture
- 31:41 Application Architecture
API Build
- 35:31 API Project Set up
- 42:41 API Model definition
- 44:47 API Db Context
- 47:13 Connection String
- 52:19 Migrations
- 56:31 API Read Endpoint
- 1:01:58 API Create Endpoint
- 1:08:15 API Update Endpoint
- 1:12:57 API Delete Endpoint
MAUI App Build
- 1:17:21 MAUI App Project Set up
- 1:21:00 Android Device Manager
- 1:25:08 MAUI Model definition
- 1:31:16 Data Service Interface
- 1:35:40 Data Service Implementation
- 1:47:27 Data Service Read Method
- 1:53:34 Data Service Create Method
- 1:58:48 Data Service Delete Method
- 2:01:53 Data Service Update Method
- 2:05:41 Android environment config
- 2:11:00 Architecture check point
- 2:11:54 Register MainPage for DI
- 2:14:13 MainPage code-behind
- 2:21:03 MainPage XAML Layout
- 2:30:19 Re-work MainPage layout
- 2:35:12 Add another page (ManagePage)
- 2:38:01 Adding a Route
- 2:30:01 Regiter ManagePage for DI
- 2:40:29 Complete MainPage code-behind
- 2:45:12 ManagePage code-behind
- 2:51:16 QueryProperty
- 2:57:34 ManagePage XMAL
- 3:07:56 Run on Windows
- 3:09:30 Re-work ManagePage layout
- 3:16:26 Using HttpClientFactory
Outro
- 3:21:02 Wrap up and thanks
- 3:21:31 Supporter Credits
11- S1,S2
در مثال فوق Text 1 و Text 3 و همچنین محتوای تگ table به رنگ قرمز نمایش مییابند.
در مثال فوق، تگ h1 که ویژگی id آن برابر index میباشد، با توجه به Selector ی که بصورت h1#index تعریف شده است، به رنگ آبی نمایش مییابد. تمامی تگهای span که عضو کلاس tag میباشند و در داخل تگ div ی قرار دارند که عضو کلاس content است، با توجه به Selector ی که بصورت .content .tag تعریف شده است، به رنگ قرمز نمایش مییابند. تمامی تگهای li که ویژگی class آنها برابر even میباشد، و فرزند تگ ul ی هستند که عضو کلاس list است، با توجه به Selector ی که بصورت ul.list li.even تعریف شده است، به رنگ سبز نمایش مییابند.
12- [attribute]
در مثال فوق، رنگ پس زمینه تگ input اول که دارای ویژگی readonly میباشد، به رنگ سبز نمایش مییابد.
13- [attribute=value]
در مثال فوق، جهت نوشتاری محتوای تگ div که دارای ویژگی lang با مقدار fa میباشد، از راست به چپ میشود و در سمت راست صفحه نمایش مییابد.
14- [attribute=value i]
در مثال فوق، جهت نوشتاری محتوای تگ div که دارای ویژگی lang با مقدار FA میباشد، از راست به چپ میشود و در سمت راست صفحه نمایش مییابد. با اینکه در Selector مقدار fa با حروف کوچک ذکر شده است.
15- [attribute|=value]
در مثال فوق Text 1 و Text 4 به رنگ قرمز نمایش مییابند.
16- [attribute^=value]
در مثال فوق Text 1 و Text 2 و Text 3 و Text 4 به رنگ قرمز نمایش مییابند.
17- [attribute~=value]
در مثال فوق Text 1 و Text 3 و Text 6 و Text 9 به رنگ قرمز نمایش مییابند.
18- [attribute*=value]
در مثال فوق تمامی متون به رنگ قرمز نمایش مییابند.
19- [attribute$=value]
در مقال فوق Text 1 و Text 5 و Text 6 و Text 7 به رنگ قرمز نمایش مییابند.
اگر بخواهیم قالب بندی را برای چند Selector به صورت یکجا انجام دهیم، این Selectorها را با کاما (,) از هم جدا مینماییم.
<style> div,.content,table.main { color: red; } </style> <div>Text 1</div> <p>Text 2</p> <table class="main" border="1"> <tr> <td>Cell 1</td> <td>Cell 2</td> <td>Cell 3</td> </tr> <tr> <td>Cell 4</td> <td>Cell 5</td> <td>Cell 6</td> </tr> </table> <span class="content">Text 3</span> <h1>Text 4</h1>
پشتیبانی در مرورگرها:
|
|
|
|
| Selector | نسخه CSS |
Yes | Yes | Yes | Yes | Yes | S1,S2 | 1 |
توجه:
Selectorها میتوانند به صورت ترکیبی نیز استفاده شوند. به مثال زیر توجه کنید:
<style> .content .tag { color: red; } h1#index { color: blue; } ul.list li.even { color: green; } </style> <h1 id="index">Index</h1> <h1>Header 1</h1> <div class="content"> Lorem ipsum dolor sit amet, <span class="tag">consectetuer</span> adipiscing elit. Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, <span class="tag">sit</span> amet commodo magna eros quis urna. Nunc viverra <span class="tag">imperdiet</span> enim. Fusce est. </div> <h1>Header 2</h1> <div class="content"> <ul class="list"> <li>Item 1</li> <li class="even">Item 2</li> <li>Item 3</li> <li class="even">Item 4</li> <li>Item 5</li> <li class="even">Item 6</li> </ul> </div>
12- [attribute]
تگهایی را انتخاب مینماید که دارای یک attribute یا ویژگی خاص باشند.
<style> [readonly] { background: green; } </style> <input type="text" value="Value 1" readonly="readonly"/> <input type="text"/>
پشتیبانی در مرورگرها:
|
|
|
|
| Selector | نسخه CSS |
3.1 | 9.6 | 7.0 | 2.0 | 4.0 | [attribute] | 2 |
13- [attribute=value]
تگ هایی را انتخاب مینماید که دارای یک attribute یا ویژگی خاص هستند و مقدار آن ویژگی دقیقا برابر با value میباشد.
<style> [lang=fa] { direction:rtl; } </style> <div lang="fa">متن 1</div>
پشتیبانی در مرورگرها:
|
|
|
|
| Selector | نسخه CSS |
3.1 | 9.6 | 7.0 | 2.0 | 4.0 | [attribute=value] | 2 |
14- [attribute=value i]
تگهایی را انتخاب مینماید که دارای یک attribute یا ویژگی خاص هستند و مقدار آن ویژگی دقیقا برابر با value میباشد. همچنین value، نسبت به حروف کوچک و بزرگ حساس نمیباشد.
<style> [lang=fa] { direction:rtl; } </style> <div lang="FA">متن 1</div>
پشتیبانی در مرورگرها:
|
|
|
|
| Selector | نسخه CSS |
No | No | No | No | No | [attribute=value i] | 4 |
15- [attribute|=value]
تگ هایی را انتخاب مینماید که دارای یک attribute یا ویژگی خاص هستند و مقدار آن ویژگی با یک value خاص آغاز میشود. کلمه آغازین مقدار حتما باید با value برابر باشد یا با dash (-) از کلمهی بعدی جدا شده باشد.
<style> [class|=info] { color:red } </style> <div class="info">Text 1</div> <div class="infobar">Text 2</div> <div class="info bar">Text 3</div> <div class="info-bar">Text 4</div> <div class="btninfo">Text 5</div> <div class="btn info">Text 6</div> <div class="btn-info">Text 7</div> <div class="toolinfoicon">Text 8</div> <div class="tool info icon">Text 9</div> <div class="tool-info-icon">Text 10</div>
پشتیبانی در مرورگرها:
|
|
|
|
| Selector | نسخه CSS |
3.1 | 9.6 | 7.0 | 2.0 | 4.0 | [attribute|=value] | 2 |
16- [attribute^=value]
تگهایی را انتخاب مینماید که دارای یک attribute یا ویژگی خاص هستند و مقدار آن ویژگی با یک value خاص آغاز می شود.
<style> [class^=info] { color: red; } </style> <div class="info">Text 1</div> <div class="infobar">Text 2</div> <div class="info bar">Text 3</div> <div class="info-bar">Text 4</div> <div class="btninfo">Text 5</div> <div class="btn info">Text 6</div> <div class="btn-info">Text 7</div> <div class="toolinfoicon">Text 8</div> <div class="tool info icon">Text 9</div> <div class="tool-info-icon">Text 10</div>
پشتیبانی در مرورگرها:
|
|
|
|
| Selector | نسخه CSS |
3.2 | 9.6 | 7.0 | 3.5 | 4.0 | [attribute^=value] | 3 |
17- [attribute~=value]
تگهایی را انتخاب مینماید که دارای یک attribute یا ویژگی خاص هستند و مقدار آن ویژگی شامل یک value خاص میباشد که با Space یا فاصلهی خالی از سایر مقادیر جدا شده است.
<style> [class~=info] { color: red; } </style> <div class="info">Text 1</div> <div class="infobar">Text 2</div> <div class="info bar">Text 3</div> <div class="info-bar">Text 4</div> <div class="btninfo">Text 5</div> <div class="btn info">Text 6</div> <div class="btn-info">Text 7</div> <div class="toolinfoicon">Text 8</div> <div class="tool info icon">Text 9</div> <div class="tool-info-icon">Text 10</div>
پشتیبانی در مرورگرها:
|
|
|
|
| Selector | نسخه CSS |
3.1 | 9.6 | 7.0 | 2.0 | 4.0 | [attribute~=value] | 2 |
18- [attribute*=value]
تگهایی را انتخاب مینماید که دارای یک attribute یا ویژگی خاص هستند و مقدار آن ویژگی شامل یک value خاص میباشد.
<style> [class*=info] { color: red; } </style> <div class="info">Text 1</div> <div class="infobar">Text 2</div> <div class="info bar">Text 3</div> <div class="info-bar">Text 4</div> <div class="btninfo">Text 5</div> <div class="btn info">Text 6</div> <div class="btn-info">Text 7</div> <div class="toolinfoicon">Text 8</div> <div class="tool info icon">Text 9</div> <div class="tool-info-icon">Text 10</div>
پشتیبانی در مرورگرها:
|
|
|
|
| Selector | نسخه CSS |
3.2 | 9.6 | 7.0 | 3.5 | 4.0 | [attribute*=value] | 3 |
19- [attribute$=value]
تگهایی را انتخاب مینماید که دارای یک attribute یا ویژگی خاص هستند و مقدار آن ویژگی به یک value خاص ختم میشود.
<style> [class$=info] { color: red; } </style> <div class="info">Text 1</div> <div class="infobar">Text 2</div> <div class="info bar">Text 3</div> <div class="info-bar">Text 4</div> <div class="btninfo">Text 5</div> <div class="btn info">Text 6</div> <div class="btn-info">Text 7</div> <div class="toolinfoicon">Text 8</div> <div class="tool info icon">Text 9</div> <div class="tool-info-icon">Text 10</div>
پشتیبانی در مرورگرها:
|
|
|
|
| Selector | نسخه CSS |
3.2 | 9.6 | 7.0 | 3.5 | 4.0 | [attribute$=value] | 3 |
در قسمت قبل، مروری داشتیم بر تفاوتهای دو نوع مختلف فناوریهای ایجاد و مدیریت فرمها در Angular و هچنین ساختار ابتدایی برنامهی این سری را ایجاد کردیم. در ادامه، اولین فرم مبتنی بر قالبها را ایجاد خواهیم کرد.
ایجاد اولین فرم مبتنی بر قالبها
پس از ایجاد کامپوننت employee-register، فایل قالب آن یا src\app\employee\employee-register\employee-register.component.html را گشوده و به نحو ذیل تکمیل میکنیم:
زمانیکه المان فرم به صفحه اضافه میشود، Angular به صورت خودکار دایرکتیو مرتبطی را به فرم اضافه میکند. برای دسترسی به این دایرکتیو نیاز است یک template reference variable را تعریف کرد. برای مثال "form="ngForm# به معنای تعریف متغیر form است که به دایرکتیو توکار ngForm متصل شدهاست و اکنون حاوی وهلهای از این دایرکتیو میباشد. به همین جهت است که امکان دسترسی به اطلاعات این وهله توسط درج form.pristine در همان قالب وجود دارد.
خاصیت pristine مشخص میکند که آیا فرم توسط کاربر تغییر یافتهاست یا خیر؟
مقدار خاصیت pristine در ابتدای کار true است؛ به این معنا که هنوز تغییری در آن اعمال نشدهاست.
یک نکته: ممکن است در حین توسعهی برنامه، خطای ذیل را در کنسول developer tools مرورگرها مشاهده کنید:
این دایرکتیو تنها زمانی قابل دسترسی است که در قسمت imports ماژول جاری که با آن کار میکنید، تعریف FormsModule را به همان نحوی که در انتهای قسمت قبل بررسی کردیم (قسمت «افزودن ماژول فرمها به برنامه»)، افزوده باشید.
در ادامه، در همین فرمی که تعاریف آنرا در بالا مشاهده میکنید، اطلاعاتی را وارد نمائید. هنوز هم مقدار خاصیت pristine مساوی true است. علت اینجا است که هنوز به Angular اعلام نکردهایم که کدام فیلد یا فیلدهای فرم را باید تحت نظر قرار دهد. برای این منظور ابتدا به المان تعریف شده نامی را انتساب داده و سپس دایرکتیو ngModel را نیز به انتهای تعاریف آن اضافه میکنیم:
اکنون اگر مقدار فرم را تغییر دهیم، مشاهده خواهیم کرد که مقدار خاصیتpristine به false تغییر میکند:
یک نکته: زمانیکه دایرکتیو ngModel ذکر میشود، تعریف name المان متناظر با آن، الزامی است؛ در غیراینصورت خطای ذیل را در کنسول developer tools مرورگرها مشاهده خواهید کرد:
خاموش کردن اعتبارسنجی توکار مرورگرها
یکی از کارهایی را که نیاز است در حین کار با فرمها انجام داد، خاموش کردن اعتبارسنجی توکار مرورگرها است. فرض کنید ویژگی معتبر و استاندارد required را به یکی از المانهای ورودی اضافه کردهاید:
در این حالت اگر برنامه را اجرا کنید و بدون تکمیل این فیلد بر روی دکمهی ارسال فرم کلیک نمائید، به ازای مرورگرهای مختلف، پیام انگلیسی «لطفا این فیلد را تکمیل کنید» ظاهر خواهد شد و هر کدام شکل متفاوتی را دارند که جزئیات آنها را نمیتوان تغییر داد و یا سفارشی سازی کرد. به این مورد، browser validation میگویند. به همین جهت برای خاموش کردن این اعتبارسنجی توکار مرورگرها و ارائهی تجربهی کاربری یکنواخت و یکدستی در تمام مرورگرها، نیاز است ویژگی novalidate را به تگ فرم اضافه کرد:
هر دوی ویژگیهای novalidate و یاrequired ، جزو استاندارد HTML هستند و ارتباطی به Angular ندارند.
بهبود ظاهر فرم توسط اعمال شیوهنامههای بوت استرپ
در قسمت قبل، در ابتدای کار تدارک ساختار مثال این سری، بوت استرپ را نیز نصب و تنظیم کردیم. در ادامه میخواهیم اندکی ظاهر این فرم را بر اساس شیوهنامههای بوت استرپ بهبود ببخشیم:
- برای افزودن بوت استرپ نیازی نیست تا شیوهنامهی آنرا به صورت دستی به Index.html برنامه اضافه کرد. همینقدر که ارجاعی از آن در فایل angular-cli.json. در قسمت شیوهنامههای آن وجود داشته باشد، به صورت خودکار در bundle نهایی تولید شدهی توسط سیستم ساخت برنامهی Angular CLI ظاهر خواهد شد.
- در اینجا ابتدا فرم خود را در داخل یک container قرار دادهایم. این مورد سبب میشود تا محتوای آن به میانهی صفحه منتقل شود.
- سپس شیوهنامهی btn به دکمهی ارسال فرم اضافه شدهاست تا شکل دکمههای بوت استرپ را پیدا کند.
- سپس هر فیلد ورودی داخل یک div با کلاس form-group محصور میشود و هر کنترل، کلاس form-control را خواهد یافت.
افزودن سایر المانهای ورودی به فرم
تا اینجا دو text box را به فرم اضافه کردهایم. در ادامه میخواهیم المانهای دیگری را نیز تعریف کنیم:
افزودن Check boxes
چون با بوت استرپ کار میکنیم، نیاز است المان ورودی از نوع checkbox را داخل یک div با کلاس checkbox محصور کنیم. سپس یک label را تعریف کرده و Input را داخل آن قرار دهیم. در اینجا نیز همانند سایر المانها نیاز است نامی را به آن انتساب داده و سپس دایرکتیو ngModel را قید نمود تا Angular این کنترل را تحت نظر قرار دهد.
افزودن Radio buttons
Radio buttons نیز شبیه به Check boxها تعریف میشوند. در اینجا نیز یک div با کلاس radio و سپس label ایی که المان ورودی از نوع radio داخل آن قرار میگیرد، افزوده خواهد شد. فقط در اینجا باید دقت داشت که گروه بندی این المانها بر اساس نام آنها انجام میشود. به همین جهت است که نام این دو المان یکی وارد شدهاست. همچنین باید value آنرا نیز تنظیم کرد. این مقداری است که در نهایت به سرور ارسال خواهد شد.
افزودن Drop downs
در اینجا از المان select برای تشکیل یک drop down استفاده میکنیم و نحوهی تعریف آن بسیار شبیه است به تعریف text boxهایی که داخل form-group محصور شده و همچنین کلاس form-control را پیدا میکنند.
اما قسمت مهم آن، اطلاعاتی است که قرار است در این drop down نمایش داده شوند. این اطلاعات را میتوان از آرایهی languages گرفت و سپس توسط یک ngFor به المان select اضافه کرد. بنابراین باید به فایل employee-register.component.ts مراجعه کرده و آرایهی languages را به آن افزود:
کاری که در اینجا انجام میشود، تکرار المان option توسط ngFor است. برای مثال در اینجا 4 بار المان option توسط عناصر آرایهی زبانها در داخل المان select تکرار خواهد شد. به عبارتی select نهایی رندر شدهی در صفحه، چنین شکلی را پیدا میکند:
تا اینجا فرم تشکیل شدهی ما چنین نمایی را پیدا میکند:
در قسمت بعد این فرم را توسط مباحث data binding و بررسی نحوهی دسترسی به اطلاعات آن در کامپوننت مرتبط، تکمیل خواهیم کرد.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: angular-template-driven-forms-lab-02.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کردهاید. سپس از طریق خط فرمان به ریشهی پروژه وارد شده و دستور npm install را صادر کنید تا وابستگیهای آن دریافت و نصب شوند. در آخر با اجرای دستور ng serve -o برنامه ساخته شده و در مرورگر پیش فرض سیستم نمایش داده خواهد شد.
ایجاد اولین فرم مبتنی بر قالبها
پس از ایجاد کامپوننت employee-register، فایل قالب آن یا src\app\employee\employee-register\employee-register.component.html را گشوده و به نحو ذیل تکمیل میکنیم:
<h3>Angular Forms</h3> <form #form="ngForm"> <input type="text" placeholder="Name"> <button type="submit">Ok</button> </form> form.pristine: {{ form.pristine }}
خاصیت pristine مشخص میکند که آیا فرم توسط کاربر تغییر یافتهاست یا خیر؟
مقدار خاصیت pristine در ابتدای کار true است؛ به این معنا که هنوز تغییری در آن اعمال نشدهاست.
یک نکته: ممکن است در حین توسعهی برنامه، خطای ذیل را در کنسول developer tools مرورگرها مشاهده کنید:
There is no directive with "exportAs" set to "ngForm"
در ادامه، در همین فرمی که تعاریف آنرا در بالا مشاهده میکنید، اطلاعاتی را وارد نمائید. هنوز هم مقدار خاصیت pristine مساوی true است. علت اینجا است که هنوز به Angular اعلام نکردهایم که کدام فیلد یا فیلدهای فرم را باید تحت نظر قرار دهد. برای این منظور ابتدا به المان تعریف شده نامی را انتساب داده و سپس دایرکتیو ngModel را نیز به انتهای تعاریف آن اضافه میکنیم:
<h3>Angular Forms</h3> <form #form="ngForm"> <input type="text" placeholder="Name" name="name" ngModel> <button type="submit">Ok</button> </form> form.pristine: {{ form.pristine }}
اکنون اگر مقدار فرم را تغییر دهیم، مشاهده خواهیم کرد که مقدار خاصیتpristine به false تغییر میکند:
یک نکته: زمانیکه دایرکتیو ngModel ذکر میشود، تعریف name المان متناظر با آن، الزامی است؛ در غیراینصورت خطای ذیل را در کنسول developer tools مرورگرها مشاهده خواهید کرد:
Error: If ngModel is used within a form tag, either the name attribute must be set or the form control must be defined as 'standalone' in ngModelOptions.
خاموش کردن اعتبارسنجی توکار مرورگرها
یکی از کارهایی را که نیاز است در حین کار با فرمها انجام داد، خاموش کردن اعتبارسنجی توکار مرورگرها است. فرض کنید ویژگی معتبر و استاندارد required را به یکی از المانهای ورودی اضافه کردهاید:
<input type="text" required placeholder="Name" name="name" ngModel>
<form #form="ngForm" novalidate>
بهبود ظاهر فرم توسط اعمال شیوهنامههای بوت استرپ
در قسمت قبل، در ابتدای کار تدارک ساختار مثال این سری، بوت استرپ را نیز نصب و تنظیم کردیم. در ادامه میخواهیم اندکی ظاهر این فرم را بر اساس شیوهنامههای بوت استرپ بهبود ببخشیم:
<div class="container"> <h3>Angular Forms</h3> <form #form="ngForm" novalidate> <div class="form-group"> <label>First Name</label> <input type="text" class="form-control" required name="firstName" ngModel> </div> <div class="form-group"> <label>Last Name</label> <input type="text" class="form-control" required name="lastName" ngModel> </div> <button class="btn btn-primary" type="submit">Ok</button> </form> </div> form.pristine: {{ form.pristine }}
- برای افزودن بوت استرپ نیازی نیست تا شیوهنامهی آنرا به صورت دستی به Index.html برنامه اضافه کرد. همینقدر که ارجاعی از آن در فایل angular-cli.json. در قسمت شیوهنامههای آن وجود داشته باشد، به صورت خودکار در bundle نهایی تولید شدهی توسط سیستم ساخت برنامهی Angular CLI ظاهر خواهد شد.
- در اینجا ابتدا فرم خود را در داخل یک container قرار دادهایم. این مورد سبب میشود تا محتوای آن به میانهی صفحه منتقل شود.
- سپس شیوهنامهی btn به دکمهی ارسال فرم اضافه شدهاست تا شکل دکمههای بوت استرپ را پیدا کند.
- سپس هر فیلد ورودی داخل یک div با کلاس form-group محصور میشود و هر کنترل، کلاس form-control را خواهد یافت.
افزودن سایر المانهای ورودی به فرم
تا اینجا دو text box را به فرم اضافه کردهایم. در ادامه میخواهیم المانهای دیگری را نیز تعریف کنیم:
افزودن Check boxes
<div class="checkbox"> <label> <input type="checkbox" name="is-full-time" ngModel> Full Time Employee </label> </div>
افزودن Radio buttons
<label>Payment Type</label> <div class="radio"> <label> <input type="radio" name="pay-type" value="FullTime" checked> Full Time </label> </div> <div class="radio"> <label> <input type="radio" name="pay-type" value="PartTime"> Part Time </label> </div>
افزودن Drop downs
<div class="form-group"> <label>Primary Language</label> <select class="form-control"> <option *ngFor="let lang of languages"> {{ lang }} </option> </select> </div>
اما قسمت مهم آن، اطلاعاتی است که قرار است در این drop down نمایش داده شوند. این اطلاعات را میتوان از آرایهی languages گرفت و سپس توسط یک ngFor به المان select اضافه کرد. بنابراین باید به فایل employee-register.component.ts مراجعه کرده و آرایهی languages را به آن افزود:
export class EmployeeRegisterComponent implements OnInit { languages = ["Persian", "English", "Spanish", "Other"];
<select class="form-control"> <option>Persian</option> <option>English</option> <option>Spanish</option> <option>Other</option> </select>
تا اینجا فرم تشکیل شدهی ما چنین نمایی را پیدا میکند:
در قسمت بعد این فرم را توسط مباحث data binding و بررسی نحوهی دسترسی به اطلاعات آن در کامپوننت مرتبط، تکمیل خواهیم کرد.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: angular-template-driven-forms-lab-02.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کردهاید. سپس از طریق خط فرمان به ریشهی پروژه وارد شده و دستور npm install را صادر کنید تا وابستگیهای آن دریافت و نصب شوند. در آخر با اجرای دستور ng serve -o برنامه ساخته شده و در مرورگر پیش فرض سیستم نمایش داده خواهد شد.
عموما از ajax برای ارائه سایتهایی سریع، با حداقل ریفرش و حداقل مصرف پهنای باند سرور، استفاده میشود. اما این روش، مشکلات خاص خود را نیز دارا است. عموما محتوای پویای بارگذاری شده، سبب تغییر آدرس صفحهی جاری در مرورگر نمیشود. برای مثال اگر قرار است چندین برگه در صفحه به صورت ajax ایی بارگذاری شوند، تغییر سریع محتوا را مشاهده میکنید، اما خبری از تغییر آدرس جاری صفحه در مرورگر نیست. همچنین روشهای ajax ایی عموما SEO friendly نیستند. زیرا اکثر موتورهای جستجو فاقد پردازشگرهای جاوا اسکریپت میباشند و محتوای پویای ajax ایی را مشاهده نمیکنند. برای آدرس دهی این مشکلات مهم، افزونهای به نام pjax طراحی شدهاست که کار آن دریافت محتوای HTML ایی از سرور و قرار دادن آن در یک جایگاه خاص مانند یک div است. در پشت صحنهی آن از jQuery ajax استفاده شده، به همراه push state
سایتهای بسیاری خودشان را با این الگو وفق دادهاند. برای نمونه Twitter و Github از مفهوم pjax استفادهی وسیعی دارند. برای نمونه، layout یا master page یک سایت را درنظر بگیرید. به ازای مرور هر صفحه، یکبار باید تمام قسمتهای تکراری layout از سرور بارگذاری شوند. توسط pjax به سرور اعلام میکنیم، ما تنها نیاز به body صفحات را داریم و نه کل صفحه را. همچنین اگر مرورگر از جاوا اسکریپت استفاده نمیکند، لطفا کل صفحه را همانند گذشته بازگشت بده. به علاوه مسایل سمت کلاینت مانند تغییر آدرس مرورگر و تغییر عنوان صفحه نیز به صورت خودکار مدیریت شوند. این تکنیک را دقیقا در حین مرور مخزنهای کد Github میتوانید مشاهده کنید. فقط قسمتی که لیست فایلها را ارائه میدهد، از سرور دریافت میگردد و نه کل صفحه.
بکارگیری pjax در ASP.NET MVC
مطابق توضیحاتی که ارائه شد، برای پیاده سازی سازی pjax نیاز به دو فایل layout داریم. یکی برای حالت ajax ایی و دیگری برای حالت بارگذاری کامل صفحه. حالت ajax ایی آن تنها از رندرکردن body پشتیبانی میکند؛ و نه ارائه تمام قسمتهای صفحه مانند هدر، فوتر، منوها و غیره. بنابراین خواهیم داشت:
الف) تعریف فایلهای layout سازگار با pjax
ابتدا یک فایل جدید را به نام _PjaxLayout.cshtml به پوشهی Shared اضافه کنید؛ با این محتوا:
سپس layout اصلی سایت را به نحو ذیل تغییر دهید
در فایل PjaxLayout خبری از هدر و فوتر نیست و فقط یک عنوان و نمایش body را به همراه دارد.
فایل layout اصلی سایت همانند قبل است. فقط RenderBody آن داخل یک div با id مساوی pjaxContainer قرار گرفته و از آن در فراخوانی افزونهی pjax استفاده شدهاست. همانطور که ملاحظه میکنید، مطابق تنظیمات ابتدای هدر layout، فقط لینکهایی که دارای ویژگی withpjax باشند، توسط pjax پردازش خواهند شد.
ب) تغییر فایل ViewStart برنامه
در فایل ViewStart، کار مقدار دهی layout پیش فرض صورت گرفتهاست. اکنون نیاز است این فایل را جهت معرفی layout دوم تعریف شده مخصوص pjax، اندکی ویرایش کنیم:
افزونهی pjax، هدری را به نام X-PJAX به سرور ارسال میکند. بر این اساس میتوان تصمیم گرفت که آیا از layout اصلی (در صورتیکه مرورگر از جاوا اسکریپت پشتیبانی نمیکند و این هدر را ارسال نکردهاست) یا از layout سبکتر pjax استفاده شود.
ج) آزمایش برنامه
یک کنترلر ساده را به نحو فوق با دو اکشن متد و دو View متناظر با آن ایجاد کنید.
سپس View متد Index را به نحو ذیل تغییر دهید:
در این View یک لینک معمولی به اکشن متد About اضافه شدهاست. فقط در ویژگیهای html آن، یک ویژگی جدید به نام withpjax را نیز اضافه کردهایم تا در صورت امکان و پشتیبانی مرورگر، از pjax استفاده شود.
اکنون اگر برنامه را اجرا کنید، چنین خروجی را در برگهی network آن مشاهده خواهید کرد:
همانطور که ملاحظه میکنید، با کلیک بر روی لینک About، یک درخواست pjax ایی به سرور ارسال شدهاست؛ به همراه هدرهای ویژه آن. هنوز قسمتهای اصلی layout سایت مشخص هستند (و مجددا از سرور درخواست نشدهاند). آدرس صفحه عوض شدهاست. به علاوه قسمت body آن تنها تغییر کردهاست.
این مثال را از اینجا نیز میتوانید دریافت کنید
PajxMvcApp.zip
برای مطالعه بیشتر
A Faster Web With PJAX
Favour PJAX over dynamically loaded partial views
What is PJAX and why
Pjax.Mvc
Using pjax with ASP.Net MVC3
Getting started with PJAX with ASP.NET MVC
ASP.NET MVC with PAjax or PushState/ReplaceState and Ajax
pjax = pushState + AJAX
Push state API همان HTML5 History API است؛ به این معنا که هرچند محتوای صفحهی جاری به صورت پویا بارگذاری میشود، اما آدرس مرورگر نیز به صورت خودکار تنظیم خواهد شد؛ به همراه عنوان صفحه. به علاوه تاریخچهی مرور صفحات نیز در مرورگر به روز رسانی شده و امکان حرکت بین صفحات توسط دکمههای back و forward همانند قبل وجود خواهد داشت. همچنین اگر مرورگر جاری سایت، امکان استفاده از جاوا اسکریپت را نداشته باشد، به صورت خودکار به حالت بارگذاری کامل صفحه سوئیچ خواهد کرد.سایتهای بسیاری خودشان را با این الگو وفق دادهاند. برای نمونه Twitter و Github از مفهوم pjax استفادهی وسیعی دارند. برای نمونه، layout یا master page یک سایت را درنظر بگیرید. به ازای مرور هر صفحه، یکبار باید تمام قسمتهای تکراری layout از سرور بارگذاری شوند. توسط pjax به سرور اعلام میکنیم، ما تنها نیاز به body صفحات را داریم و نه کل صفحه را. همچنین اگر مرورگر از جاوا اسکریپت استفاده نمیکند، لطفا کل صفحه را همانند گذشته بازگشت بده. به علاوه مسایل سمت کلاینت مانند تغییر آدرس مرورگر و تغییر عنوان صفحه نیز به صورت خودکار مدیریت شوند. این تکنیک را دقیقا در حین مرور مخزنهای کد Github میتوانید مشاهده کنید. فقط قسمتی که لیست فایلها را ارائه میدهد، از سرور دریافت میگردد و نه کل صفحه.
بکارگیری pjax در ASP.NET MVC
مطابق توضیحاتی که ارائه شد، برای پیاده سازی سازی pjax نیاز به دو فایل layout داریم. یکی برای حالت ajax ایی و دیگری برای حالت بارگذاری کامل صفحه. حالت ajax ایی آن تنها از رندرکردن body پشتیبانی میکند؛ و نه ارائه تمام قسمتهای صفحه مانند هدر، فوتر، منوها و غیره. بنابراین خواهیم داشت:
الف) تعریف فایلهای layout سازگار با pjax
ابتدا یک فایل جدید را به نام _PjaxLayout.cshtml به پوشهی Shared اضافه کنید؛ با این محتوا:
<title>@ViewBag.Title</title> @RenderBody()
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> <link href="~/Content/Site.css" rel="stylesheet" /> <script src="~/Scripts/jquery-1.8.2.min.js"></script> <script src="~/Scripts/jquery.pjax.js"></script> <script type="text/javascript"> $(function () { $(document).pjax('a[withpjax]', '#pjaxContainer', { timeout: 5000 }); }); </script> </head> <body> <div>Main layout ...</div> <div id="pjaxContainer"> @RenderBody() </div> </body> </html>
فایل layout اصلی سایت همانند قبل است. فقط RenderBody آن داخل یک div با id مساوی pjaxContainer قرار گرفته و از آن در فراخوانی افزونهی pjax استفاده شدهاست. همانطور که ملاحظه میکنید، مطابق تنظیمات ابتدای هدر layout، فقط لینکهایی که دارای ویژگی withpjax باشند، توسط pjax پردازش خواهند شد.
ب) تغییر فایل ViewStart برنامه
در فایل ViewStart، کار مقدار دهی layout پیش فرض صورت گرفتهاست. اکنون نیاز است این فایل را جهت معرفی layout دوم تعریف شده مخصوص pjax، اندکی ویرایش کنیم:
@{ if (Request.Headers["X-PJAX"] != null) { Layout = "~/Views/Shared/_PjaxLayout.cshtml"; } else { Layout = "~/Views/Shared/_Layout.cshtml"; } }
ج) آزمایش برنامه
using System.Web.Mvc; namespace PajxMvcApp.Controllers { public class HomeController : Controller { public ActionResult Index() { return View(); } public ActionResult About() { return View(); } } }
سپس View متد Index را به نحو ذیل تغییر دهید:
@{ ViewBag.Title = "Index"; } <h2>Index</h2> @Html.ActionLink(linkText: "About", actionName:"About", routeValues: null, controllerName:"Home", htmlAttributes: new { withpjax = "with-pjax"})
اکنون اگر برنامه را اجرا کنید، چنین خروجی را در برگهی network آن مشاهده خواهید کرد:
همانطور که ملاحظه میکنید، با کلیک بر روی لینک About، یک درخواست pjax ایی به سرور ارسال شدهاست؛ به همراه هدرهای ویژه آن. هنوز قسمتهای اصلی layout سایت مشخص هستند (و مجددا از سرور درخواست نشدهاند). آدرس صفحه عوض شدهاست. به علاوه قسمت body آن تنها تغییر کردهاست.
این مثال را از اینجا نیز میتوانید دریافت کنید
PajxMvcApp.zip
برای مطالعه بیشتر
A Faster Web With PJAX
Favour PJAX over dynamically loaded partial views
What is PJAX and why
Pjax.Mvc
Using pjax with ASP.Net MVC3
Getting started with PJAX with ASP.NET MVC
ASP.NET MVC with PAjax or PushState/ReplaceState and Ajax
اشتراکها
پیاده سازی Dark Mode با سه سطر CSS
اشتراکها
راهنمای کامل طراحی Dark Mode
نظرات مطالب
استفاده از pjax بجای ajax در ASP.NET MVC
با سلام
از کمک شما ممنون
بالاخره خطا رو پیدا کردم
The following sections have been defined but have not been rendered for the layout page "~/Views/Shared/_PjaxLayout.cshtml": "Scripts".
ولی دلیلش چی میتونه باشه مگه فقط نمیاد قسمت مثلا main در کد زیر را جایگذاری کنه؟
و برای فراخوانی لینکهای pjax نوشته شده:
و لینک هم به اینصورت:
از کمک شما ممنون
بالاخره خطا رو پیدا کردم
The following sections have been defined but have not been rendered for the layout page "~/Views/Shared/_PjaxLayout.cshtml": "Scripts".
ولی دلیلش چی میتونه باشه مگه فقط نمیاد قسمت مثلا main در کد زیر را جایگذاری کنه؟
<div id="main"> @RenderBody() </div>
//********** @Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/bootstrap")
@RenderSection("Scripts", required: false)
<script type="text/javascript"> $(function () { $(document).pjax('a[withpjax]', '#main', { timeout: 5000 });
و لینک هم به اینصورت:
@Html.ActionLink("ارتباط با ما","Contact", "Home" , null,new { withpjax="with-pjax" })