مسیرراهها
عموما در برنامههای جاوا اسکریپتی با استفاده از متدهای setTimeout و setInterval میتوان یک تایمر را ایجاد کرد. اما در برنامههای Angular با توجه به استفادهی از کتابخانهی RxJS، امکان ایجاد تایمرهای reactive نیز وجود دارد که در این مطلب آنها را مرور خواهیم کرد.
ایجاد تایمرهای متوالی و بیوقفه
با استفاده از عملگر Observable.interval میتوان یک تایمر بینهایت را ایجاد کرد. پارامتر ورودی آن بر حسب میلی ثانیه است و مشترکین به آن در بازههای زمانی مشخص شدهی توسط این پارامتر، عدد جاری این بازه را دریافت میکنند.
یک مثال:
در این مثال میخواهیم تایمری را ایجاد کنیم که هر ثانیه یکبار، کدی را اجرا کند:
با این قالب:
عملگر interval باید از مسیر rxjs/add/observable/interval دریافت شود که در ابتدای تعاریف کامپوننت مشخص شدهاست.
پس از آن فراخوانی Observable.interval(1000) یک Observable را ایجاد میکند که توانایی صدور رخدادهایی را در بازههای زمانی متوالی 1000 میلی ثانیهای، دارا است.
اکنون مشترکین به آن، اعداد متوالی شروع شدهی از صفر را در هر ثانیه یکبار، دریافت میکنند:
این تایمر، به نحوی که تعریف شدهاست، تا ابد ادامه پیدا خواهد کرد. برای توقف آن نیاز است همانند روال معمول کار با Observableها، اشتراک به آن را لغو کرد:
مطلع شدن از پایان کار یک تایمر
با استفاده از اپراتور finally که از مسیر rxjs/add/operator/finally قابل import است، میتوان رخداد لغو اشتراک به این Observable و یا همان خاتمهی تایمر را در اینجا دریافت کرد:
ایجاد تایمرهای خود متوقف شونده
با استفاده از عملگر Observable.timer که در مسیر rxjs/add/observable/timer قرار دارد، میتوان تایمری را ایجاد کرد که پس از یک تاخیر مشخص شده، اجرا شود و بلافاصله خاتمه یابد:
در اینجا تایمری ایجاد شدهاست که پس از یک ثانیه اجرا شده و کد نمایش ding را در کنسول مرورگر اجرا میکند. سپس به صورت خودکار خاتمه خواهد یافت. در اینجا data نیز مساوی صفر است (اولین بار اجرای تایمر).
این تایمر امکان اجرای در بازههای زمانی مشخصی را نیز دارا است:
اولین پارامتر آن مشخص میکند که این تایمر باید پس از 2 ثانیه تاخیر، شروع به کار کند و دومین آرگومان آن مشخص میکند که این تایمر تا ابد، با فواصل زمانی هر 500 میلیثانیه یکبار، اجرا خواهد شد.
محدود کردن تعداد بار اجرای تایمر
اگر Observable.timer با پارامتر دوم آن بکار رود، بینهایت بار اجرا خواهد شد. اما میتوان این تعداد بار اجرا را توسط اپراتور take که از مسیر rxjs/add/operator/take قابل import است، محدود کرد:
در اینجا تایمر تعریف شده، پس از یک وقفهی آغازین 2 ثانیهای شروع به کار میکند. سپس تنها دو بار دیگر در بازههای متوالی زمانی 500 میلی ثانیه یکبار، اجرا خواهد شد. یعنی جمعا سه بار با توجه به take(3) اجرا خواهد شد.
اجرای با تاخیر بازههای زمانی
با استفاده از اپراتور delay که از مسیر rxjs/add/operator/delay قابل import است، میتوان هر بار اجرای callback تایمر را با یک تاخیر دریافت کرد:
در اینجا تایمر از نوع interval تعریف شده، با توجه به استفادهی از عملگر take، تنها سه بار اجرا میشود. اما این اجراها با تاخیری 300 میلیثانیهای به مشترکین آنها اطلاع رسانی میگردند. به این ترتیب خروجی لاگ شدهی این عملیات به صورت ذیل خواهد بود:
ایجاد یک تایمر شمارش معکوس
فرض کنید میخواهید تایمری را ایجاد کنید که در طی یک شمارش معکوس، از عدد 10000 شروع شود و هر ثانیه یکبار 1000 واحد از آن کاهش یابد و زمانیکه به صفر رسید، متوقف شود.
این تایمر پس از import وابستگیهای آن:
یک چنین تعریفی را پیدا میکند:
در اینجا تایمر تعریف شده با توجه به آرگومان صفر تاخیر آن، بلافاصله شروع به کار میکند. همچنین با توجه به عدد interval آن، هر یک ثانیه یکبار اعداد صفر، یک و ... را به مشترکین خود ارسال خواهد کرد. اکنون میخواهیم این تایمر دقیقا پس از 11 ثانیه متوقف شود. یکی از روشهای پیاده سازی آن استفاده از takeUntil است که در اینجا یک تایمر خود متوقف شوند را دریافت کردهاست. این تایمر دقیقا پس از 11 ثانیه از شروع عملیات، یکبار اجرا شده و بلافاصله خاتمه پیدا میکند. همین صدور رخداد، کار takeUntil را به پایان میرساند که این مورد نیز سبب خاتمهی تایمر اصلی میشود.
در اینجا چون اعداد صادر شدهی از طرف تایمر، افزایشی هستند، نیاز است به روشی آنها را تغییر داد. در یک چنین حالتی از اپراتور map استفاده میشود. در اینجا value، هربار مقدار افزایشی شروع شدهی از صفر را ارائه میدهد. توسط عملگر map، این خروجی افزایشی را به یک خروجی کاهشی تبدیل کردهایم تا بتوان به یک تایمر شمارش معکوس رسید.
دریافت مدت زمان بین اجرای بازههای زمانی
Observable.timer با هر بار اجرا، اعداد شروع شدهی از صفر را به مشترکین ارسال میکند. اگر در این بین از اپراتور timeInterval قرار گرفتهی در مسیر rxjs/add/operator/timeInterval استفاده شود، این مقدار ارسالی از نوع مخصوص <TimeInterval<number خواهد بود که دارای خواص value و interval است:
در اینجا value همان صفر، یک و ... است و interval بیانگر زمان سپری شدهی بین دو صدور رخداد میباشد.
در این مثال با استفاده از متد map، یک خروجی سفارشی تهیه شدهاست. اگر صرفا علاقمند به دریافت مقدار خاصیت interval باشید، میتوان به صورت ذیل نیز عمل کرد:
عملگر pluck که در مسیر rxjs/add/operator/pluck قرار دارد، خاصیت و یا خاصیتهایی از منبع را جهت بازگشت، انتخاب میکند. برای مثال در اینجا خاصیت interval یک شیء TimeInterval انتخاب شدهاست.
تعلیق و از سرگیری مجدد تایمرها
با قطع اشتراک از یک منبع تایمر، سبب توقف کامل آن خواهیم شد. اما اگر برای مدتی بخواهیم آنرا در حالت تعلیق قرار دهیم، میتوان به صورت ذیل عمل کرد:
نکتهی اصلی این طراحی در switchMap و Observable.never آن نهفتهاست. در اینجا وجود Subject سبب صدور رخدادی به مشترکین آن میشود. اگر توسط متد next آن false ارسال شود، سبب از سرگیری مجدد منبع اصلی یا همان تایمر برنامه میشود و اگر true ارسال شود، عملیات فراخوانی tickerFunc را با فراخوانی Observable.never به حالت تعلیق میبرد.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.
ایجاد تایمرهای متوالی و بیوقفه
با استفاده از عملگر Observable.interval میتوان یک تایمر بینهایت را ایجاد کرد. پارامتر ورودی آن بر حسب میلی ثانیه است و مشترکین به آن در بازههای زمانی مشخص شدهی توسط این پارامتر، عدد جاری این بازه را دریافت میکنند.
یک مثال:
در این مثال میخواهیم تایمری را ایجاد کنیم که هر ثانیه یکبار، کدی را اجرا کند:
import { Observable } from "rxjs/Observable"; import "rxjs/add/observable/interval"; import { Subscription } from "rxjs/Subscription"; @Component() export class UsingTimersComponent { private intervalSubscription: Subscription; interval = 0; startInterval() { const interval = Observable.interval(1000); this.intervalSubscription = interval.subscribe(i => this.interval += i); } stopInterval() { this.intervalSubscription.unsubscribe(); } }
<div class="panel panel-default"> <div class="panel-heading"> <h2 class="panel-title">Observable.interval(1000)</h2> </div> <div class="panel-body"> <div> <label>interval: </label> {{interval}} </div> <div> <button (click)="startInterval()" class="btn btn-success">Start</button> <button (click)="stopInterval()" class="btn btn-danger">Stop</button> </div> </div> </div>
پس از آن فراخوانی Observable.interval(1000) یک Observable را ایجاد میکند که توانایی صدور رخدادهایی را در بازههای زمانی متوالی 1000 میلی ثانیهای، دارا است.
اکنون مشترکین به آن، اعداد متوالی شروع شدهی از صفر را در هر ثانیه یکبار، دریافت میکنند:
this.intervalSubscription = interval.subscribe(i => this.interval += i);
this.intervalSubscription.unsubscribe();
مطلع شدن از پایان کار یک تایمر
با استفاده از اپراتور finally که از مسیر rxjs/add/operator/finally قابل import است، میتوان رخداد لغو اشتراک به این Observable و یا همان خاتمهی تایمر را در اینجا دریافت کرد:
this.intervalSubscription = interval .finally(() => console.log("All done!")) .subscribe(i => this.interval += i);
ایجاد تایمرهای خود متوقف شونده
با استفاده از عملگر Observable.timer که در مسیر rxjs/add/observable/timer قرار دارد، میتوان تایمری را ایجاد کرد که پس از یک تاخیر مشخص شده، اجرا شود و بلافاصله خاتمه یابد:
const timer = Observable.timer(1000); timer.subscribe(data => console.log('ding!'));
این تایمر امکان اجرای در بازههای زمانی مشخصی را نیز دارا است:
const moreThanOne$ = Observable.timer(2000, 500); moreThanOne$.subscribe(data => console.log('timer with args', data));
محدود کردن تعداد بار اجرای تایمر
اگر Observable.timer با پارامتر دوم آن بکار رود، بینهایت بار اجرا خواهد شد. اما میتوان این تعداد بار اجرا را توسط اپراتور take که از مسیر rxjs/add/operator/take قابل import است، محدود کرد:
let moreThanOne$ = Observable.timer(2000, 500).take(3); moreThanOne$.subscribe(data => console.log('timer with args', data));
اجرای با تاخیر بازههای زمانی
با استفاده از اپراتور delay که از مسیر rxjs/add/operator/delay قابل import است، میتوان هر بار اجرای callback تایمر را با یک تاخیر دریافت کرد:
const start = new Date(); const stream$ = Observable.interval(500).take(3); stream$.delay(300).subscribe(x => { console.log('val',x); console.log( new Date() - start ); })
val:0 800ms val:1 1300ms val:2 1800ms
ایجاد یک تایمر شمارش معکوس
فرض کنید میخواهید تایمری را ایجاد کنید که در طی یک شمارش معکوس، از عدد 10000 شروع شود و هر ثانیه یکبار 1000 واحد از آن کاهش یابد و زمانیکه به صفر رسید، متوقف شود.
این تایمر پس از import وابستگیهای آن:
import { Observable } from "rxjs/Observable"; import "rxjs/add/observable/timer"; import "rxjs/add/operator/finally"; import "rxjs/add/operator/takeUntil"; import "rxjs/add/operator/map";
const interval = 1000; const duration = 10 * 1000; const stream$ = Observable.timer(0, interval) .finally(() => console.log("All done!")) .takeUntil(Observable.timer(duration + interval)) .map(value => duration - value * interval); stream$.subscribe(value => console.log(value));
در اینجا چون اعداد صادر شدهی از طرف تایمر، افزایشی هستند، نیاز است به روشی آنها را تغییر داد. در یک چنین حالتی از اپراتور map استفاده میشود. در اینجا value، هربار مقدار افزایشی شروع شدهی از صفر را ارائه میدهد. توسط عملگر map، این خروجی افزایشی را به یک خروجی کاهشی تبدیل کردهایم تا بتوان به یک تایمر شمارش معکوس رسید.
دریافت مدت زمان بین اجرای بازههای زمانی
Observable.timer با هر بار اجرا، اعداد شروع شدهی از صفر را به مشترکین ارسال میکند. اگر در این بین از اپراتور timeInterval قرار گرفتهی در مسیر rxjs/add/operator/timeInterval استفاده شود، این مقدار ارسالی از نوع مخصوص <TimeInterval<number خواهد بود که دارای خواص value و interval است:
const source = Observable.timer(0, 1000) .timeInterval() .map(x => x.value + ":" + x.interval) .take(5); const subscription = source.subscribe( x => console.log("Next timeInterval: " + x), err => console.log("Error: " + err), () => console.log("Completed") );
در این مثال با استفاده از متد map، یک خروجی سفارشی تهیه شدهاست. اگر صرفا علاقمند به دریافت مقدار خاصیت interval باشید، میتوان به صورت ذیل نیز عمل کرد:
const source = Observable.timer(0, 1000) .timeInterval() .pluck("interval") .take(5);
تعلیق و از سرگیری مجدد تایمرها
با قطع اشتراک از یک منبع تایمر، سبب توقف کامل آن خواهیم شد. اما اگر برای مدتی بخواهیم آنرا در حالت تعلیق قرار دهیم، میتوان به صورت ذیل عمل کرد:
import { Observable } from "rxjs/Observable"; import "rxjs/add/observable/never"; import "rxjs/add/observable/timer"; import { Subject } from "rxjs/Subject"; tick: number; pauser = new Subject(); tickerSource = new Subject(); startTicker() { Observable.timer(0, 1000) .subscribe(this.tickerSource); this.pauser .switchMap(paused => paused ? Observable.never() : this.tickerSource). subscribe(t => this.tickerFunc(t)); this.pauser.next(false); // resume } tickerFunc(tick) { this.tick = tick; } pauseTicker() { this.pauser.next(true); } resumeTicker() { this.pauser.next(false); }
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.
نظرات مطالب
MVC vs 3-Tier Pattern
با سلام؛ آیا میشه منطق بیزینس لایر رو تو خود مدل پیاده سازی کرد؟ یا مثلا منطق Data access layer رو تو خود Model رو بتونیم پیاده سازی کنیم؟ به عبارتی مثلا قسمت Model برنامه از یه ساختار درختی دیگه ایی برخوردار باشه یعنی یه جورایی N tier رو بتونیم تو هر یک از این بخشها پیاده سازی کنیم ... مثلا وقتی جداول ما تو پوشه مدل ساخته میشه بتونیم methodها و اون بیزینس لایر رو هم تو همین پوشه براش در نظر بگیریم؟
و سوال دوم این که وقتی ما طبق این شکل آخر تو پوشه UI داریم از مدل استفاده میکنیم و کلاسها رو قرار میدیم تو این قسمت پس اون قسمت Data access layer رو برای چی در نظر گرفتیم ؟ آیا میشه گفت که در پوشه مدل کلاس هایی هستند که در واقع بعضی از پارامترهای ارسالی رو برای View در این قسمت از کلاس Data access layer در نظر گرفتیم و این یه زیر مجموعه ایی از این قسمت حساب میشه ؟
در این قسمت قصد داریم ساختار مقدماتی مثال این سری را که لیستی از تصاویر آپلود شدهی توسط کاربران مختلف را نمایش میدهد، بدون افزودن مباحث امنیتی و سطوح دسترسی کاربران وارد شدهی به سیستم، تشکیل دهیم. در قسمتهای بعدی، به تدریج آنرا با قابلیتهای مختلف IdentityServer 4x یکپارچه خواهیم کرد. در اینجا فرض بر این است که حداقل SDK نگارش 2.1.401 را پیشتر نصب کردهاید.
بررسی ساختار WebAPI مقدماتی مثال این سری
این پروژهی مقدماتی که هنوز قسمتهای اعتبارسنجی به آن اضافه نشدهاند، از دو قسمت WebApi و MvcClient تشکیل میشود.
کار قسمت WebApi، ارائهی یک Restful-API برای کار با گالری تصاویر است. برای اجرای آن وارد پوشهی src\WebApi\ImageGallery.WebApi.WebApp شده و ابتدا فایل restore.bat و سپس dotnet_run.bat را اجرا کنید.
در این حالت برنامه بر روی پورت 7001 در دسترس خواهد بود:
این پورت نیز در فایل Properties\launchSettings.json تنظیم شدهاست تا با پورت کلاینت MVC تهیه شده، تداخل نکند.
کار این سرویس، ارائهی ImagesController است که توسط آن میتوان لیستی از تصاویر موجود، اطلاعات یک تصویر و همچنین کار ثبت، ویرایش و حذف تصاویر را انجام داد.
در این Solution، رکوردهای تصاویری که در بانک اطلاعاتی ذخیره میشوند، یک چنین ساختاری را دارند:
همانطور که ملاحظه میکنید در اینجا OwnerId نیز در نظر گرفته شدهاست تا بتوان پس از اعمال مباحث اعتبارسنجی، تصاویر را از کاربری خاص دریافت و همچنین صرفا تصاویر متعلق به او را به آن کاربر خاص نمایش داد.
در این قسمت توسط کلاس ImageConfiguration کار مقدار دهی اولیهی بانک اطلاعاتی به کمک متد HasData مربوط به EF Core 2.1 صورت گرفتهاست و به این ترتیب میتوان برنامه را برای نمایش مقدماتی جاری، بدون داشتن سیستم اعتبارسنجی و مفاهیم کاربران، نمایش داد.
این قسمت از Solution، به نحو زیر تشکیل شدهاست:
- ImageGallery.WebApi.DataLayer
در اینجا کار تشکیل DbContext برنامه و همچنین مقدار دهی اولیهی بانک اطلاعاتی و تنظیمات Migrations قرار گرفتهاند.
- ImageGallery.WebApi.DomainClasses
در این پروژه کلاسهای موجودیتهای متناظر با جداول بانک اطلاعاتی قرار میگیرند.
- ImageGallery.WebApi.Mappings
این پروژه کار تهیه نگاشتهای AutoMapper برنامه را انجام میدهد؛ نگاشتهایی بین Models و DomainClasses که در ImagesController از آنها استفاده میشود.
- ImageGallery.WebApi.Models
در این پروژه همان DTO's پروژه قرار گرفتهاند. جهت رعایت مسایل امنیتی نباید کلاس موجودیت Image فوق را مستقیما در معرض دید API عمومی قرار داد. به همین جهت تعدادی Model در اینجا تعریف شدهاند که هم برای ثبت، ویرایش و حذف اطلاعات بکار میروند و هم جهت گزارشگیری از رکوردهای موجود جدول تصاویر.
- ImageGallery.WebApi.Services
در این پروژه کار با DbContext انجام شده و توسط آن اطلاعات تصاویر به بانک اطلاعاتی اضافه شده و یا واکشی میشوند.
- ImageGallery.WebApi.WebApp
این پروژه، همان پروژهی اصلی است که سایر قسمتهای یاد شده را در کنار هم قرار داده و به صورت یک Restful-API ارائه میدهد.
بررسی ساختار MvcClient مقدماتی مثال این سری
پس از اجرای WebAPI و آماده بودن آن جهت استفاده، اکنون یک کلاینت ASP.NET Core MVC را جهت کار با امکانات ImagesController آن، تدارک دیدهایم.
برای اجرای آن وارد پوشهی src\MvcClient\ImageGallery.MvcClient.WebApp شده و ابتدا فایل restore.bat و سپس dotnet_run.bat را اجرا کنید.
در این حالت برنامه بر روی پورت 5001 در دسترس خواهد بود:
این پورت نیز در فایل Properties\launchSettings.json تنظیم شدهاست.
در اینجا نمایی از اجرای این برنامه را مشاهده میکنید که لیستی از تصاویر را توسط GalleryController، از سرویس ImagesController مربوط به WebAPI، دریافت کرده و سپس نمایش میدهد. در این لیست تصاویر تمام کاربران با هم نمایش داده شدهاند و هنوز امکان فیلتر کردن آنها بر اساس کاربران وارد شدهی به سیستم را نداریم که در قسمتهای بعدی آنها را تکمیل خواهیم کرد.
این قسمت از Solution به نحو زیر تشکیل شدهاست:
- ImageGallery.MvcClient.Services
در اینجا یک Typed HTTP Client مخصوص NET Core 2.1. را تهیه کردهایم. این سرویس جهت دسترسی به آدرس https://localhost:7001 که WebAPI برنامه در آن قرار دارد، تشکیل شدهاست. روش ثبت مخصوص آنرا نیز در فایل آغازین پروژهی MvcClient.WebApp توسط متد services.AddHttpClient ملاحظه میکنید.
- ImageGallery.MvcClient.ViewModels
مدلهای متناظر با ساختار Viewهای Razor برنامهی وب، در اینجا قرار میگیرند.
- ImageGallery.MvcClient.WebApp
این پروژه، همان پروژهی اصلی است که سایر قسمتهای یاد شده را در کنار هم قرار داده و به صورت یک برنامهی MVC قابل مرور در مرورگر، ارائه میدهد.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.
برای اجرای آن ابتدا باید پروژهی WebApi.WebApp را اجرا کنید و سپس پروژهی MvcClient.WebApp.
بررسی ساختار WebAPI مقدماتی مثال این سری
این پروژهی مقدماتی که هنوز قسمتهای اعتبارسنجی به آن اضافه نشدهاند، از دو قسمت WebApi و MvcClient تشکیل میشود.
کار قسمت WebApi، ارائهی یک Restful-API برای کار با گالری تصاویر است. برای اجرای آن وارد پوشهی src\WebApi\ImageGallery.WebApi.WebApp شده و ابتدا فایل restore.bat و سپس dotnet_run.bat را اجرا کنید.
در این حالت برنامه بر روی پورت 7001 در دسترس خواهد بود:
این پورت نیز در فایل Properties\launchSettings.json تنظیم شدهاست تا با پورت کلاینت MVC تهیه شده، تداخل نکند.
کار این سرویس، ارائهی ImagesController است که توسط آن میتوان لیستی از تصاویر موجود، اطلاعات یک تصویر و همچنین کار ثبت، ویرایش و حذف تصاویر را انجام داد.
در این Solution، رکوردهای تصاویری که در بانک اطلاعاتی ذخیره میشوند، یک چنین ساختاری را دارند:
using System; using System.ComponentModel.DataAnnotations; namespace ImageGallery.WebApi.DomainClasses { public class Image { [Key] public Guid Id { get; set; } [Required] [MaxLength(150)] public string Title { get; set; } [Required] [MaxLength(200)] public string FileName { get; set; } [Required] [MaxLength(50)] public string OwnerId { get; set; } } }
در این قسمت توسط کلاس ImageConfiguration کار مقدار دهی اولیهی بانک اطلاعاتی به کمک متد HasData مربوط به EF Core 2.1 صورت گرفتهاست و به این ترتیب میتوان برنامه را برای نمایش مقدماتی جاری، بدون داشتن سیستم اعتبارسنجی و مفاهیم کاربران، نمایش داد.
این قسمت از Solution، به نحو زیر تشکیل شدهاست:
- ImageGallery.WebApi.DataLayer
در اینجا کار تشکیل DbContext برنامه و همچنین مقدار دهی اولیهی بانک اطلاعاتی و تنظیمات Migrations قرار گرفتهاند.
- ImageGallery.WebApi.DomainClasses
در این پروژه کلاسهای موجودیتهای متناظر با جداول بانک اطلاعاتی قرار میگیرند.
- ImageGallery.WebApi.Mappings
این پروژه کار تهیه نگاشتهای AutoMapper برنامه را انجام میدهد؛ نگاشتهایی بین Models و DomainClasses که در ImagesController از آنها استفاده میشود.
- ImageGallery.WebApi.Models
در این پروژه همان DTO's پروژه قرار گرفتهاند. جهت رعایت مسایل امنیتی نباید کلاس موجودیت Image فوق را مستقیما در معرض دید API عمومی قرار داد. به همین جهت تعدادی Model در اینجا تعریف شدهاند که هم برای ثبت، ویرایش و حذف اطلاعات بکار میروند و هم جهت گزارشگیری از رکوردهای موجود جدول تصاویر.
- ImageGallery.WebApi.Services
در این پروژه کار با DbContext انجام شده و توسط آن اطلاعات تصاویر به بانک اطلاعاتی اضافه شده و یا واکشی میشوند.
- ImageGallery.WebApi.WebApp
این پروژه، همان پروژهی اصلی است که سایر قسمتهای یاد شده را در کنار هم قرار داده و به صورت یک Restful-API ارائه میدهد.
بررسی ساختار MvcClient مقدماتی مثال این سری
پس از اجرای WebAPI و آماده بودن آن جهت استفاده، اکنون یک کلاینت ASP.NET Core MVC را جهت کار با امکانات ImagesController آن، تدارک دیدهایم.
برای اجرای آن وارد پوشهی src\MvcClient\ImageGallery.MvcClient.WebApp شده و ابتدا فایل restore.bat و سپس dotnet_run.bat را اجرا کنید.
در این حالت برنامه بر روی پورت 5001 در دسترس خواهد بود:
این پورت نیز در فایل Properties\launchSettings.json تنظیم شدهاست.
در اینجا نمایی از اجرای این برنامه را مشاهده میکنید که لیستی از تصاویر را توسط GalleryController، از سرویس ImagesController مربوط به WebAPI، دریافت کرده و سپس نمایش میدهد. در این لیست تصاویر تمام کاربران با هم نمایش داده شدهاند و هنوز امکان فیلتر کردن آنها بر اساس کاربران وارد شدهی به سیستم را نداریم که در قسمتهای بعدی آنها را تکمیل خواهیم کرد.
این قسمت از Solution به نحو زیر تشکیل شدهاست:
- ImageGallery.MvcClient.Services
در اینجا یک Typed HTTP Client مخصوص NET Core 2.1. را تهیه کردهایم. این سرویس جهت دسترسی به آدرس https://localhost:7001 که WebAPI برنامه در آن قرار دارد، تشکیل شدهاست. روش ثبت مخصوص آنرا نیز در فایل آغازین پروژهی MvcClient.WebApp توسط متد services.AddHttpClient ملاحظه میکنید.
- ImageGallery.MvcClient.ViewModels
مدلهای متناظر با ساختار Viewهای Razor برنامهی وب، در اینجا قرار میگیرند.
- ImageGallery.MvcClient.WebApp
این پروژه، همان پروژهی اصلی است که سایر قسمتهای یاد شده را در کنار هم قرار داده و به صورت یک برنامهی MVC قابل مرور در مرورگر، ارائه میدهد.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.
برای اجرای آن ابتدا باید پروژهی WebApi.WebApp را اجرا کنید و سپس پروژهی MvcClient.WebApp.
پس از آشنایی با طرحبندی صفحات و امکانات متداول Twitter Bootstrap، در این قسمت به کامپوننتها و اجزای تعاملی آن مانند منوها، برگهها و امثال آن خواهیم پرداخت.
منوهای پایین افتادنی (Dropdown menus) در Twitter Bootstrap
برای کار با منوها، حداقل نیازهایی که باید به صفحه اضافه شوند، فایل css مرتبط با Twitter Bootstrap، فایل اسکریپتهای جیکوئری و فایل bootstrap.min.js است.
در ادامه، ساختار متداول یک منوی نمونه ایجاد شده را ملاحظه میکنید:
در اینجا به یک div، کلاس dropdown نسبت داده شده است؛ از این جهت که میتوان این منو را به انواع و اقسام المانهای صفحه نیز نسبت داد. به این ترتیب، منو از محل قرارگیری این المان باز خواهد شد. در ادامه روال متداول تعریف منو را در Bootstrap، ملاحظه میکنید. کلاس dropdown-toggle سبب خواهد شد تا با کلیک ماوس بر روی لینک، منو ظاهر شود. کلاس caret نیز سبب نمایش مثلث رو به پایین، جهت مشخص سازی وجود منو در این محل خاص، تعریف شده است.
المانهای منو، توسط لیستها تعریف میشوند. این لیست باید با کلاس dropdown-menu شروع شود. سپس هر عضو این لیست، یک آیتم منو را تشکیل خوهد داد. اگر نیاز به خط جدا کنندهای در این میان باشد، باید از کلاس divider استفاده کرد.
در اینجا همچنین امکان تعریف منوهای تو در تو نیز وجود دارد. برای اینکار تنها کافی است یک لیست دیگر را در این میان اضافه کرد. کلاس dropdown-submenu به عضو لیست دربرگیرنده زیر منو، اضافه میشود. زیر منو نیز دارای کلاس dropdown-menu خواهد بود.
چند نکته تکمیلی در مورد منوها:
- همانطور که در قسمت بررسی دکمههای Bootstrap نیز عنوان شد، برای تبدیل یک المان به دکمه فقط کافی است کلاس btn را به آن انتساب دهیم. در اینجا نیز میتوان کلاس لینک منوی پایین افتادنی را به صورت btn btn-primary dropdown-toggle تعریف کرد تا ظاهر آن تبدیل به یک دکمه Bootstrap شود.
- در اولین div مثال فوق، کلاس dropdown ذکر شده است. میتوان این کلاس را به dropup تغییر داد تا نحوه باز شدن منوی تعریف شده رو به بالا باشد؛ بجای رو به پایین.
- برای ساخت split button که تشکیل شده است از دو دکمه اصلی و دکمه کوچکی در کنار آن که یک زیرمنو را نمایش میدهد، باید به نحو زیر عمل کرد:
تنها نکتهای که در اینجا نسبت به حالت قبل اضافه شده است، استفاده از کلاس btn-group است. این کلاس سبب میشود تا دکمههای تعریف شده در کنار هم به شکل یک toolbar مرتب شوند.
تعریف برگهها در Twitter Bootstrap
برای تعریف منوهای راهبری سایت به شکل برگه، کار با استفاده از کلاس nav و تعریف یک لیست ساده شروع میشود:
- اضافه شدن کلاس nav، بولتهای لیست را حذف و ویژگی تغییر رنگ زمینه هر یک از عناصر لیست را با حرکت اشارهگر ماوس بر روی آن فعال میکند.
- برای اعمال اشکال مختلف به کلاس nav، میتوان از سایر کلاسهای مرتبط استفاده کرد. اگر از nav-tabs استفاده شود، این لیست عمودی تبدیل به یک لیست افقی خواهد شد. اگر کلاس active به یکی از آیتمهای آن نسبت داده شود (بیانگر صفحه جاری)، این گزینه حالت انتخاب شده را پیدا میکند.
- در هر حالت، برای تبدیل چیدمان افقی به عمودی، از کلاس nav-stacked استفاده نمائید.
- کلاس nav-pills، برای نمایش عنصر فعال، از یک مستطیل با گوشههای گرد استفاده میکند.
- اگر میخواهید این گوشهها گرد نباشند، از nav-list استفاده کنید.
- در هر کدام از حالتها میتوان از یک گزینه اختیاری با کلاس nav-header برای نمایش متنی خاکستری بالای منوی راهبری استفاده کرد.
- همانطور که در آخرین لیست اضافه شده در مثال فوق ملاحظه میکنید، امکان تعریف منوهای پایین افتادنی در اینجا نیز میسر است. در این حالت کلاس li تعریف شده باید dropdown تعریف شود. مابقی نکات آن مانند قبل است.
تعریف یک Tab به همراه محتوای برگههای آن در داخل یک صفحه
در حالت تعریف منوهای راهبری فوق، هر عنصر برگههای تعریف شده به یک صفحه جدید لینک شده بود. اگر نیاز باشد تا هر برگه، محتوایی داخل همان صفحه داشته باشد باید به نحو ذیل عمل کرد:
در اینجا کار با کلاس tabbable شروع میشود. سپس از یک لیست با کلاس nav nav-tabs برای تعریف سرتیترهای برگهها استفاده میکنیم.
اگر به مثال فوق دقت کنید، اینبار hrefهای تعریف شده به tab1 و tab2 اشاره میکنند. ایندو در حقیقت id محتوای متناظر هستند که در قسمت div ایی با کلاس tab-content به div هایی با id مساوی tab1 و tab2 انتساب داده شدهاند.
شیوه دوم تعریف منوی راهبری سایت به کمک Twitter Bootstrap
اگر علاقمند باشید که منوی راهبری بالای سایت دارای یک حاشیه به همراه مسایل طراحی واکنشگرا باشد، میتوان از کامپوننت navbar مجموعه Bootstrap استفاده کرد:
navbar نیز همانند بسیاری از کامپوننتهای Bootstrap، بر اساس ul و liها کار میکند. در ابتدای کار، کلاس nav را به تگ ul انتساب میدهیم. این مورد با مباحث قسمت قبلی در مورد تدارک برگهها، تفاوتی نمیکند. به این ترتیب بولتهای لیست حذف شده و همچنین تغییر رنگ پس زمینه، با حرکت اشارهگر ماوس بر روی آیتمها را سبب میشود. سپس کل این ul و li ها، داخل تگ nav قرار میگیرند با کلاسی مساوی navbar. تا اینجا لیست عمودی تبدیل به یک لیست افقی میشود.
در ادامه برای اضافه کردن حاشیهای به اطراف این منوی راهبری و تمایز آن از سایر قسمتهای صفحه، یک div با کلاس navbar-inner را اضافه خواهیم کرد.
عموما مرسوم است که در منوی راهبری اصلی سایت، نام سایت یا متنی مشخص نیز در قسمتی از این منو ظاهر شود. برای انجام اینکار، کافی است یک لینک را با کلاس brand اضافه کنیم.
این موارد، اصول کلی تهیه یک منوی راهبری اصلی سایت را توسط Bootstrap بیان میکنند. در ادامه به چند نکته تکمیلی خواهیم پرداخت:
- اگر به تگ nav، کلاس navbar-static-top را اضافه کنیم، فاصله بین منو و اطراف صفحه را حذف میکند.
- اگر علاقمند هستیم که منوی بالای صفحه حتی با اسکرول به پایین محتوای نمایش داده شده، همواره نمایان باشد و بر روی محتوا قرار گیرد، میتوان به تگ nav، کلاس navbar-fixed-top را اضافه کرد. یا اگر نیاز است این منو در پایین صفحه به صورت ثابت نمایش داده شود از navbar-fixed-bottom میشود استفاده کرد.
در این حالت خاص ممکن است منو، قسمتی از ابتدای محتوای صفحه را مخفی کند و این محتوا زیر منو واقع شود. برای بهبود آن باید margin-top مرتبط با body را مقدار دهی کنید (حدود 40px کافی است).
- استفاده از کلاس navbar-inverse (همانند مثال فوق)، سبب معکوس شدن رنگ زمینه منوی راهبری میشود.
- همچنین در اینجا نیز میتوان یک dropdown-menu را به نحوی که ملاحظه میکنید، به منوی اصلی سایت افزود.
- امکان قرار دادن فرمهای خاصی مانند فرم جستجو نیز در منوی راهبری وجود دارد. در این حالت از کلاس navbar-search مانند مثال فوق استفاده خواهد شد. کلاس pull-right این المان را به سمت راست صفحه (در اینجا به سمت چپ با توجه به RTL بودن قالب)، هدایت میکند و کلاس input-append سبب میشود تا دکمه و تکست باکس تعریف شده در یک سطر و کنار هم قرارگیرند تا ظاهر بهتری حاصل شود. در حالت کلی کلاس navbar-form نیز برای این نوع فرمها درنظر گرفته شده است.
- اگر دقت کرده باشید، کل محتوای منو، به همراه فرم تعریف شده، در یک div با کلاس nav-collapse قرار گرفته است. هدف از اینکار ناپدید شدن خودکار قسمتهای طولانی یا نچندان مهم، در حین مرور سایت با اندازههای مرورگر کوچکتر مانند مرورگرهای موبایل است. همچنین اگر بخواهیم در این حالت که قسمتهایی ناپدید شدهاند، یک دکمه با سه سطر که بیانگر منویی مخفی است را نمایش دهیم میتوان از کلاس btn-navbar استفاده کرد؛ که نمونهای از آنرا در مثال فوق ملاحظه میکنید. این دکمه بر اساس data-target مشخص شده، سبب مخفی یا ظاهر شدن قسمتی از صفحه میگردد.
تعیین موقعیت کاربر در صفحه به کمک breadcrumbs در Bootstrap
عموما مرسوم است در یک سایت چند سطحی، موقعیت جاری کاربر، به نحوی به او اعلام شود. یکی از این روشها استفاده از breadcrumbs است که نمونهای از آنرا در مثال فوق ملاحظه میکنید.
- در اینجا نیز برای تعریف breadcrumbs از ul و li استفاده شده است به همراه کلاس breadcrumb.
- برای ایجاد فاصله و نمایش یک خط جداکننده، از کلاس divider کمک گرفته شده است.
- صفحه جاری با کلاس active مشخص گردیده؛ بدون تعریف لینک.
تعریف قسمت صفحه بندی یک لیست یا گرید به کمک Bootstrap
برای نمایش زیباتر قسمت صفحه بندی یک لیست یا گرید میتوان از یک سری ul و li مزین شده با کلاس pagination استفاده کرد. کلاس pagination-centered، آنرا در وسط صفحه قرار خواهد داد (حالتهای چپ و راست نیز وجود دارند). کلاس pagination-large، این قسمت را اندکی بزرگتر از معمول نمایش میدهد.
در اینجا کلاس disabled، سبب غیرفعال نمایش داده شدن لینک به یک صفحه و کلاس active، بیانگر نمایش صفحه جاری است.
نمایش برچسبها و اعلانات با زمینههای مختلف به کمک Bootstrap
برای نمایش برچسبهای کوتاه با زمینهای متفاوت از کلاس label استفاده میشود. افزودن کلاسهایی مانند label-success و امثال آن که در مثال ذیل ذکر شدهاند، رنگهای متفاوتی را سبب خواهند شد. برای گرد کردن بیش از حد گوشه این برچسبها از کلاس badge استفاده میشود.
همانطور که ملاحظه میکنید برای نمایش اعلانی به کاربر میتوان از کلاس alert استفاده کرد. افزودن کلاسهایی مانند alert-danger، رنگهای متفاوتی را ارائه خواهند داد و اگر علاقمندید که کاربر بتواند این اخطار را با کلیک بر روی دکمه ضربدر کنار آن حذف کند، از یک دکمه با کلاس close و data-dismiss مساوی alert استفاده کنید. این اطلاعات به صورت خودکار توسط اسکریپتهای bootstrap پردازش میشوند.
منوهای پایین افتادنی (Dropdown menus) در Twitter Bootstrap
برای کار با منوها، حداقل نیازهایی که باید به صفحه اضافه شوند، فایل css مرتبط با Twitter Bootstrap، فایل اسکریپتهای جیکوئری و فایل bootstrap.min.js است.
در ادامه، ساختار متداول یک منوی نمونه ایجاد شده را ملاحظه میکنید:
<div class="container-fluid"> <div class="row-fluid"> <div class="span12"> <div class="dropdown"> <a class="dropdown-toggle" data-toggle="dropdown" href="#">منوی پایین افتادنی <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="http://www.site1.com">site1</a></li> <li><a href="http://www.site2.com">site2</a></li> <li><a href="http://www.site3.com">site3</a></li> <li class="divider"></li> <li class="dropdown-submenu"><a href="#">سایر موارد</a> <ul class="dropdown-menu"> <li><a href="http://www.site4.com">site4</a></li> <li><a href="http://www.site5.com">site5</a></li> </ul> </li> </ul> </div> </div> </div> </div>
در اینجا به یک div، کلاس dropdown نسبت داده شده است؛ از این جهت که میتوان این منو را به انواع و اقسام المانهای صفحه نیز نسبت داد. به این ترتیب، منو از محل قرارگیری این المان باز خواهد شد. در ادامه روال متداول تعریف منو را در Bootstrap، ملاحظه میکنید. کلاس dropdown-toggle سبب خواهد شد تا با کلیک ماوس بر روی لینک، منو ظاهر شود. کلاس caret نیز سبب نمایش مثلث رو به پایین، جهت مشخص سازی وجود منو در این محل خاص، تعریف شده است.
المانهای منو، توسط لیستها تعریف میشوند. این لیست باید با کلاس dropdown-menu شروع شود. سپس هر عضو این لیست، یک آیتم منو را تشکیل خوهد داد. اگر نیاز به خط جدا کنندهای در این میان باشد، باید از کلاس divider استفاده کرد.
در اینجا همچنین امکان تعریف منوهای تو در تو نیز وجود دارد. برای اینکار تنها کافی است یک لیست دیگر را در این میان اضافه کرد. کلاس dropdown-submenu به عضو لیست دربرگیرنده زیر منو، اضافه میشود. زیر منو نیز دارای کلاس dropdown-menu خواهد بود.
چند نکته تکمیلی در مورد منوها:
- همانطور که در قسمت بررسی دکمههای Bootstrap نیز عنوان شد، برای تبدیل یک المان به دکمه فقط کافی است کلاس btn را به آن انتساب دهیم. در اینجا نیز میتوان کلاس لینک منوی پایین افتادنی را به صورت btn btn-primary dropdown-toggle تعریف کرد تا ظاهر آن تبدیل به یک دکمه Bootstrap شود.
- در اولین div مثال فوق، کلاس dropdown ذکر شده است. میتوان این کلاس را به dropup تغییر داد تا نحوه باز شدن منوی تعریف شده رو به بالا باشد؛ بجای رو به پایین.
- برای ساخت split button که تشکیل شده است از دو دکمه اصلی و دکمه کوچکی در کنار آن که یک زیرمنو را نمایش میدهد، باید به نحو زیر عمل کرد:
<div class="container-fluid"> <div class="row-fluid"> <div class="span12"> <div class="btn-group"> <a class="btn btn-primary" href="#">Split button</a> <a class="btn btn-primary dropdown-toggle" data-toggle="dropdown" href="#"><span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="http://www.site1.com">site1</a></li> <li><a href="http://www.site2.com">site2</a></li> </ul> </div> </div> </div> </div>
تعریف برگهها در Twitter Bootstrap
برای تعریف منوهای راهبری سایت به شکل برگه، کار با استفاده از کلاس nav و تعریف یک لیست ساده شروع میشود:
<div class="container"> <div class="row"> <h2> Navigational menus </h2> <div class="span3"> <ul class="nav nav-tabs"> <li class="nav-header">nav nav-tabs</li> <li class="active"><a href="#">Home</a></li> <li><a href="#">Contact</a></li> <li><a href="#">Login</a></li> </ul> </div> <div class="span3"> <ul class="nav nav-tabs nav-stacked"> <li class="nav-header">nav nav-tabs nav-stacked</li> <li class="active"><a href="#">Home</a></li> <li><a href="#">Contact</a></li> <li><a href="#">Login</a></li> </ul> </div> <div class="span3"> <ul class="nav nav-pills"> <li class="nav-header">nav nav-pills</li> <li class="active"><a href="#">Home</a></li> <li><a href="#">Contact</a></li> <li><a href="#">Login</a></li> </ul> </div> <div class="span3"> <ul class="nav nav-list"> <li class="nav-header">nav nav-list</li> <li class="active"><a href="#">Home</a></li> <li><a href="#">Contact</a></li> <li><a href="#">Login</a></li> <li class="dropdown"><a class="dropdown-toggle" data-toggle="dropdown" href="#">Menu ...<span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="http://www.site1.com">site1</a></li> <li><a href="http://www.site2.com">site2</a></li> </ul> </li> </ul> </div> </div> </div>
- اضافه شدن کلاس nav، بولتهای لیست را حذف و ویژگی تغییر رنگ زمینه هر یک از عناصر لیست را با حرکت اشارهگر ماوس بر روی آن فعال میکند.
- برای اعمال اشکال مختلف به کلاس nav، میتوان از سایر کلاسهای مرتبط استفاده کرد. اگر از nav-tabs استفاده شود، این لیست عمودی تبدیل به یک لیست افقی خواهد شد. اگر کلاس active به یکی از آیتمهای آن نسبت داده شود (بیانگر صفحه جاری)، این گزینه حالت انتخاب شده را پیدا میکند.
- در هر حالت، برای تبدیل چیدمان افقی به عمودی، از کلاس nav-stacked استفاده نمائید.
- کلاس nav-pills، برای نمایش عنصر فعال، از یک مستطیل با گوشههای گرد استفاده میکند.
- اگر میخواهید این گوشهها گرد نباشند، از nav-list استفاده کنید.
- در هر کدام از حالتها میتوان از یک گزینه اختیاری با کلاس nav-header برای نمایش متنی خاکستری بالای منوی راهبری استفاده کرد.
- همانطور که در آخرین لیست اضافه شده در مثال فوق ملاحظه میکنید، امکان تعریف منوهای پایین افتادنی در اینجا نیز میسر است. در این حالت کلاس li تعریف شده باید dropdown تعریف شود. مابقی نکات آن مانند قبل است.
تعریف یک Tab به همراه محتوای برگههای آن در داخل یک صفحه
در حالت تعریف منوهای راهبری فوق، هر عنصر برگههای تعریف شده به یک صفحه جدید لینک شده بود. اگر نیاز باشد تا هر برگه، محتوایی داخل همان صفحه داشته باشد باید به نحو ذیل عمل کرد:
<div class="container"> <div class="row"> <div class="span12"> <div class="tabbable"> <ul class="nav nav-tabs"> <li class="active"><a data-toggle="tab" href="#tab1">Home</a></li> <li><a data-toggle="tab" href="#tab2">About</a></li> </ul> <div class="tab-content"> <div class="tab-pane active" id="tab1"> data ... data ... </div> <div class="tab-pane" id="tab2"> data2 ... data2 ... </div> </div> </div> </div> </div> </div>
در اینجا کار با کلاس tabbable شروع میشود. سپس از یک لیست با کلاس nav nav-tabs برای تعریف سرتیترهای برگهها استفاده میکنیم.
اگر به مثال فوق دقت کنید، اینبار hrefهای تعریف شده به tab1 و tab2 اشاره میکنند. ایندو در حقیقت id محتوای متناظر هستند که در قسمت div ایی با کلاس tab-content به div هایی با id مساوی tab1 و tab2 انتساب داده شدهاند.
شیوه دوم تعریف منوی راهبری سایت به کمک Twitter Bootstrap
اگر علاقمند باشید که منوی راهبری بالای سایت دارای یک حاشیه به همراه مسایل طراحی واکنشگرا باشد، میتوان از کامپوننت navbar مجموعه Bootstrap استفاده کرد:
<div class="container"> <div class="row"> <div class="span12"> <nav class="navbar navbar-inverse"> <div class="navbar-inner"> <a href="/" class="brand">نام سایت در اینجا</a> <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </a> <div class="nav-collapse collapse"> <ul class="nav"> <li><a href="/about">درباره</a></li> <li><a href="/contact">تماس</a></li> <li class="dropdown"><a class="dropdown-toggle" data-toggle="dropdown" href="#">Menu ...<span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="http://www.site1.com">site1</a></li> <li><a href="http://www.site2.com">site2</a></li> </ul> </li> </ul> <form class="navbar-search pull-right input-append" action="/search"> <input class="span2" type="search" /> <button class="btn" type="button"> جستجو</button> </form> </div> </div> </nav> </div> </div> </div>
navbar نیز همانند بسیاری از کامپوننتهای Bootstrap، بر اساس ul و liها کار میکند. در ابتدای کار، کلاس nav را به تگ ul انتساب میدهیم. این مورد با مباحث قسمت قبلی در مورد تدارک برگهها، تفاوتی نمیکند. به این ترتیب بولتهای لیست حذف شده و همچنین تغییر رنگ پس زمینه، با حرکت اشارهگر ماوس بر روی آیتمها را سبب میشود. سپس کل این ul و li ها، داخل تگ nav قرار میگیرند با کلاسی مساوی navbar. تا اینجا لیست عمودی تبدیل به یک لیست افقی میشود.
در ادامه برای اضافه کردن حاشیهای به اطراف این منوی راهبری و تمایز آن از سایر قسمتهای صفحه، یک div با کلاس navbar-inner را اضافه خواهیم کرد.
عموما مرسوم است که در منوی راهبری اصلی سایت، نام سایت یا متنی مشخص نیز در قسمتی از این منو ظاهر شود. برای انجام اینکار، کافی است یک لینک را با کلاس brand اضافه کنیم.
این موارد، اصول کلی تهیه یک منوی راهبری اصلی سایت را توسط Bootstrap بیان میکنند. در ادامه به چند نکته تکمیلی خواهیم پرداخت:
- اگر به تگ nav، کلاس navbar-static-top را اضافه کنیم، فاصله بین منو و اطراف صفحه را حذف میکند.
- اگر علاقمند هستیم که منوی بالای صفحه حتی با اسکرول به پایین محتوای نمایش داده شده، همواره نمایان باشد و بر روی محتوا قرار گیرد، میتوان به تگ nav، کلاس navbar-fixed-top را اضافه کرد. یا اگر نیاز است این منو در پایین صفحه به صورت ثابت نمایش داده شود از navbar-fixed-bottom میشود استفاده کرد.
در این حالت خاص ممکن است منو، قسمتی از ابتدای محتوای صفحه را مخفی کند و این محتوا زیر منو واقع شود. برای بهبود آن باید margin-top مرتبط با body را مقدار دهی کنید (حدود 40px کافی است).
- استفاده از کلاس navbar-inverse (همانند مثال فوق)، سبب معکوس شدن رنگ زمینه منوی راهبری میشود.
- همچنین در اینجا نیز میتوان یک dropdown-menu را به نحوی که ملاحظه میکنید، به منوی اصلی سایت افزود.
- امکان قرار دادن فرمهای خاصی مانند فرم جستجو نیز در منوی راهبری وجود دارد. در این حالت از کلاس navbar-search مانند مثال فوق استفاده خواهد شد. کلاس pull-right این المان را به سمت راست صفحه (در اینجا به سمت چپ با توجه به RTL بودن قالب)، هدایت میکند و کلاس input-append سبب میشود تا دکمه و تکست باکس تعریف شده در یک سطر و کنار هم قرارگیرند تا ظاهر بهتری حاصل شود. در حالت کلی کلاس navbar-form نیز برای این نوع فرمها درنظر گرفته شده است.
- اگر دقت کرده باشید، کل محتوای منو، به همراه فرم تعریف شده، در یک div با کلاس nav-collapse قرار گرفته است. هدف از اینکار ناپدید شدن خودکار قسمتهای طولانی یا نچندان مهم، در حین مرور سایت با اندازههای مرورگر کوچکتر مانند مرورگرهای موبایل است. همچنین اگر بخواهیم در این حالت که قسمتهایی ناپدید شدهاند، یک دکمه با سه سطر که بیانگر منویی مخفی است را نمایش دهیم میتوان از کلاس btn-navbar استفاده کرد؛ که نمونهای از آنرا در مثال فوق ملاحظه میکنید. این دکمه بر اساس data-target مشخص شده، سبب مخفی یا ظاهر شدن قسمتی از صفحه میگردد.
تعیین موقعیت کاربر در صفحه به کمک breadcrumbs در Bootstrap
<div class="container"> <div class="row"> <div class="span12"> <ul class="breadcrumb"> <li><a href="/level1">سطح یک</a> <span class="divider">/</span></li> <li><a href="/level2">سطح دو</a> <span class="divider">/</span></li> <li class="active">سطح سه</li> </ul> </div> </div> </div>
- در اینجا نیز برای تعریف breadcrumbs از ul و li استفاده شده است به همراه کلاس breadcrumb.
- برای ایجاد فاصله و نمایش یک خط جداکننده، از کلاس divider کمک گرفته شده است.
- صفحه جاری با کلاس active مشخص گردیده؛ بدون تعریف لینک.
تعریف قسمت صفحه بندی یک لیست یا گرید به کمک Bootstrap
<div class="container"> <div class="row"> <div class="span12"> <div class="pagination pagination-centered pagination-large"> <ul> <li class="disabled"><a href="#">Prev</a></li> <li class="active"><a href="/page/1">1</a></li> <li><a href="/page/2">2</a></li> <li><a href="/page/3">3</a></li> <li><a href="/page/4">4</a></li> <li><a href="#">Next</a></li> </ul> </div> </div> </div> </div>
برای نمایش زیباتر قسمت صفحه بندی یک لیست یا گرید میتوان از یک سری ul و li مزین شده با کلاس pagination استفاده کرد. کلاس pagination-centered، آنرا در وسط صفحه قرار خواهد داد (حالتهای چپ و راست نیز وجود دارند). کلاس pagination-large، این قسمت را اندکی بزرگتر از معمول نمایش میدهد.
در اینجا کلاس disabled، سبب غیرفعال نمایش داده شدن لینک به یک صفحه و کلاس active، بیانگر نمایش صفحه جاری است.
نمایش برچسبها و اعلانات با زمینههای مختلف به کمک Bootstrap
برای نمایش برچسبهای کوتاه با زمینهای متفاوت از کلاس label استفاده میشود. افزودن کلاسهایی مانند label-success و امثال آن که در مثال ذیل ذکر شدهاند، رنگهای متفاوتی را سبب خواهند شد. برای گرد کردن بیش از حد گوشه این برچسبها از کلاس badge استفاده میشود.
<div class="container"> <div class="row"> <div class="span12"> <section> <ul class="unstyled"> <li><span class="label">پیش فرض</span></li> <li><span class="label label-success">موفقیت آمیز</span></li> <li><span class="label label-warning">اخطار</span></li> <li><span class="label label-importatnt">مهم است</span></li> <li><span class="label label-info">اطلاعات</span></li> <li><span class="label label-inverse">رنگ معکوس</span></li> </ul> </section> <section> <ul class="unstyled"> <li><span class="badge">پیش فرض</span></li> <li><span class="badge badge-success">موفقیت آمیز</span></li> <li><span class="badge badge-warning">اخطار</span></li> <li><span class="badge badge-importatnt">مهم است</span></li> <li><span class="badge badge-info">اطلاعات</span></li> <li><span class="badge badge-inverse">رنگ معکوس</span></li> </ul> </section> <div> <div class="alert"> <button class="close" data-dismiss="alert">×</button> نمایش اعلانات </div> <div class="alert alert-danger"> <button class="close" data-dismiss="alert">×</button> نمایش اعلانات </div> <div class="alert alert-info"> <button class="close" data-dismiss="alert">×</button> نمایش اعلانات </div> <div class="alert alert-success"> <button class="close" data-dismiss="alert">×</button> نمایش اعلانات </div> <div class="alert alert-error"> <button class="close" data-dismiss="alert">×</button> نمایش اعلانات </div> </div> </div> </div> </div>
همانطور که ملاحظه میکنید برای نمایش اعلانی به کاربر میتوان از کلاس alert استفاده کرد. افزودن کلاسهایی مانند alert-danger، رنگهای متفاوتی را ارائه خواهند داد و اگر علاقمندید که کاربر بتواند این اخطار را با کلیک بر روی دکمه ضربدر کنار آن حذف کند، از یک دکمه با کلاس close و data-dismiss مساوی alert استفاده کنید. این اطلاعات به صورت خودکار توسط اسکریپتهای bootstrap پردازش میشوند.
- میتوان دسترسی داشت. پس از لاگین، برنامه را در چند برگهی مجزا باز کنید، باز هم کار میکند و کاربر جاری تشخیص داده میشود. این محدودیت دسترسی مربوط به دومین جاری برنامه است و نه برگههای آن (مفهوم پیاده سازی sand box یا قرنطینهی امنیتی).
- در مورد جزئیات محلهای ذخیره سازی:
- در مورد علت استفادهی از local storage و مزایا و معایب آن (نگاهی به محل ذخیره سازی JWT و نکات مرتبط با آن ) در اینجا : معرفی JSON Web Token
در این قسمت روش جایگزین کردن متد css جیکوئری را با کدهای خالص جاوا اسکریپتی بررسی میکنیم.
کار با Inline Styles
در این مثال میخواهیم با استفاده از جاوا اسکریپت، المانهای h2 و h3 را یافته و سپس h2ها را آبی و h3ها را سبز کنیم:
برای تغییر inline style المانها، از خاصیت style آنها استفاده میشود که در نهایت این شیوهنامههای جدید توسط ویژگی style به همان المان اضافه میشوند:
خاصیت style جزو استاندارد DOM Level 2 است که در سال 2000 تصویب شدهاست (از زمان IE 8.0 به بعد در دسترس است). باید دقت داشت که از این روش بیشتر در کتابخانههای جاوا اسکریپتی برای شرایطی خاص، جهت تغییر پویای رابط کاربری استفاده میشود و هر تغییری که در اینجا اعمال شود، مقادیر قبلی موجود را بازنویسی میکند.
همچنین اگر بخواهیم به یک المان چندین شیوهنامه را انتساب دهیم، روش کار به صورت زیر است:
پس از یافتن المانهای مدنظر، تنها کافی است نام شیوهنامهی مدنظر را به خاصیت style اضافه و مقدار دهی کنیم. در اینجا نام شیوهنامهای که «کبابی» باشد، مانند font-weight، به صورت camel case مانند fontWeight درج خواهد شد؛ هرچند از همان نام اصلی نیز میتوان به صورت زیر استفاده کرد:
روش دیگری نیز برای انجام این تغییرات چندتایی وجود دارد:
خاصیت style یک المان، از نوع اینترفیس CSSStyleDeclaration است که دارای خاصیت استاندارد cssText نیز میباشد. توسط این خاصیت میتوان چندین شیوهنامه را به صورت یکجا به عنصری انتساب داد و یا تمام آنها را خواند.
کار با Style Sheets
Inline styles تنها روش کار با شیوهنامهها نیست. روش صحیح و قابل مدیریت کار با شیوهنامهها استفاده از فایلهای style sheets است. برای مثال تغییرات قبل را میتوان در فایلی به نام styles.css و با محتوای زیر ایجاد کرد:
و سپس آنرا به صفحه متصل نمود:
و یا حتی میتوان این شیوه نامه را به صورت inline نیز به ابتدای صفحه اضافه نمود:
اما ممکن است در برنامه بخواهیم امکان تغییر پویای قالب را به کاربران بدهیم. در یک چنین حالتی اعمال این نوع شیوهنامهها توسط جاوا اسکریپت مفهوم پیدا میکند:
اولین سطر، اولین تگ style اضافه شده به صفحه را یافته (این style میتواند inline و یا لینک شدهی توسط یک فایل باشد) و سپس شیوه نامهی جدیدی را توسط متد insertRule، در انتهای آن به صورت پویا درج میکند.
مخفی کردن و نمایش دادن المانها در صفحه
جیکوئری به همراه متدهای hide و show است که کار مخفی کردن و یا نمایش دادن مجدد یک المانرا انجام میدهند:
در کل روشهای زیادی برای مخفی کردن یک المان وجود دارند. برای مثال میتوان opacity آنرا به صفر تنظیم کرد و یا position آنرا به absolute و سپس آنرا در مختصاتی خارج از صفحه قرار داد. اما عموما خاصیت display را به none تنظیم میکنند. همچنین در استاندارد W3C HTML5، خاصیت جدید hidden از نوع boolean نیز به المانها اضافه شدهاند که دقیقا برای همینمنظور بکار میرود. مزیت مهم این خاصیت نه فقط استاندارد بودن آن، بلکه بالابردن دسترسی پذیری المانهای صفحه توسط برنامههای «screen reader» مخصوص معلولین است. بنابراین با استفاده از جاوا اسکریپت خالص برای مخفی کردن یک المان میتوان نوشت:
این روش 25 بار سریعتر از متد hide جیکوئری است! از این جهت که jQuery در پشت صحنه مدام متد window.getComputedStyle را برای موارد خاص و بحرانی کار با شیوهنامهها فراخوانی میکند (در تمام متدهایی که با CSS کار میکنند) و این متد تاثیر منفی بر روی کارآیی برنامه دارد.
و چون خاصیت hidden از نوع Boolean است، ذکر آن در یک المان یعنی تنظیم آن به true و حذف آن، یعنی تنظیم آن به false یا نمایش مجدد المان در اینجا:
اندازهگیری تاثیر شیوهنامهها بر روی طول و عرض المانها
CSS Box Model یک چنین تعریفی را دارد:
زمانیکه از متدهای ()width و ()height جیکوئری بر روی المانی استفاده میشود، صرفا طول و عرض قسمت «content» را دریافت خواهید کرد.
برای این منظور در جاوا اسکریپت خالص این خواص در اختیار ما است:
روش اندازه گیری Content + Padding توسط جاوا اسکریپت خالص:
این خواص هرچند اخیرا به استانداردهای CSS به صورت رسمی اضافه شدهاند، اما از زمان IE 6.0 پشتیبانی میشدهاند و با متدهای ()innerWidth و ()innerHeight جیکوئری قابل مقایسه هستند.
روش اندازه گیری Content + Padding + Border توسط جاوا اسکریپت خالص:
این خواص از زمان IE 8.0 پشتیبانی میشدهاند.
کار با Inline Styles
<h1>News</h1> <div>Welcome to our site!</div> <h2>World</h2> <h3>Title 1</h3> <div>description 1.</div> <h2>Science</h2> <h3>Title 2</h3> <div>description 2.</div>
var headings = document.querySelectorAll('h2, h3'); for (var i = 0; i < headings.length; i++) { if (headings[i].tagName === 'H2') { headings[i].style.color = 'blue'; } else { headings[i].style.color = 'green'; } }
<h2 style="color: blue">….</h2> <h3 style="color: green">….</h3>
همچنین اگر بخواهیم به یک المان چندین شیوهنامه را انتساب دهیم، روش کار به صورت زیر است:
<h2>World</h2> ... <h2>Science</h2> <script> var headings = document.querySelectorAll('h2'); for (var i = 0; i < headings.length; i++) { headings[i].style.color = 'blue'; headings[i].style.fontWeight = 'bold'; } </script>
headings[i].style['font-weight'] = 'bold';
var headings = document.querySelectorAll('h2'); for (var i = 0; i < headings.length; i++) { headings[i].style.cssText = 'color: blue; font-weight: bold'; }
کار با Style Sheets
Inline styles تنها روش کار با شیوهنامهها نیست. روش صحیح و قابل مدیریت کار با شیوهنامهها استفاده از فایلهای style sheets است. برای مثال تغییرات قبل را میتوان در فایلی به نام styles.css و با محتوای زیر ایجاد کرد:
h2 { color: blue; } h3 { color: green; }
<link href="styles.css" rel="style sheet">
<style> h2 { color: blue; } h3 { color: green; } </style>
var sheet = document.styleSheets[0]; sheet.insertRule('h2 { font-style: italic; }', sheet.cssRules.length - 1);
مخفی کردن و نمایش دادن المانها در صفحه
جیکوئری به همراه متدهای hide و show است که کار مخفی کردن و یا نمایش دادن مجدد یک المانرا انجام میدهند:
// hide an element $element.hide(); // show it again $element.show();
element.setAttribute('hidden', '');
و چون خاصیت hidden از نوع Boolean است، ذکر آن در یک المان یعنی تنظیم آن به true و حذف آن، یعنی تنظیم آن به false یا نمایش مجدد المان در اینجا:
element.removeAttribute('hidden');
اندازهگیری تاثیر شیوهنامهها بر روی طول و عرض المانها
CSS Box Model یک چنین تعریفی را دارد:
زمانیکه از متدهای ()width و ()height جیکوئری بر روی المانی استفاده میشود، صرفا طول و عرض قسمت «content» را دریافت خواهید کرد.
برای این منظور در جاوا اسکریپت خالص این خواص در اختیار ما است:
<style> .box { padding: 10px; margin: 5px; border: 3px solid; display: inline-block; } </style> <span class="box">a box</span>
// returns 38 var clientHeight = document.querySelector('.box').clientHeight; // returns 55 var clientWidth = document.querySelector('.box').clientWidth;
روش اندازه گیری Content + Padding + Border توسط جاوا اسکریپت خالص:
// returns 44 var offsetHeight = document.querySelector('.box').offsetHeight; // returns 61 var offsetWidth = document.querySelector('.box').offsetWidth;
بعد از آپدیت قسمت نظرات با ajax اسکریپت از کار میفته.توی این مثال از متد On چطوری باید استفاده کنیم؟ توی فایل jquery.StarRating باید تغییرات بدیم یا توی اسکریپتی که داخل صفحه مینویسیم؟
بازخوردهای دوره
استفاده از AOP Interceptors برای حذف کدهای تکراری کش کردن اطلاعات در لایه سرویس برنامه
سلام
مهندس بسیار عالی و خسته نباشید.
یک سوال برای من پیش آمد، آیا نمیشد مانند مثال سوم این پست قسمت کار با کش با Action بازنویسی شود؟ یا اینکه فقط قصد آموزش بوده که به این شکل کد نوشته شده است؟
مهندس بسیار عالی و خسته نباشید.
یک سوال برای من پیش آمد، آیا نمیشد مانند مثال سوم این پست قسمت کار با کش با Action بازنویسی شود؟ یا اینکه فقط قصد آموزش بوده که به این شکل کد نوشته شده است؟
اشتراکها