آموزش JavaScript Front-End Web App
آشنایی با Flex Layout Box Model
برای طراحی ظاهر یک برنامهی وب نیاز است عناصر آنرا در مکانهای مختلفی از صفحه قرار داد که به آن Layout گفته میشود. برای این منظور عموما 4 روش ذیل مرسوم هستند:
1. Table
2. Float, position, clear
3. CSS Grids
4. FlexBox CSS
امروزه دیگر آنچنان روشهای 1 و 2 به صورت مستقیم مورد استفاده قرار نمیگیرند. CSS Grid روش نهایی طراحی Layout در آینده خواهد بود و در حال حاضر تعداد مرورگرهایی که از آن پشتیبانی میکنند، قابل توجه نیست؛ اما از FlexBox در IE 11، کروم 21 و فایرفاکس 22 به بعد پشتیبانی میشود.
FlexBox CSS، سیلان المانهای قرار گرفتهی در داخل آنرا سبب میشود. در اینجا یک container اصلی وجود دارد که در برگیرندهی المانها است. در تصویر فوق دو محور را مشاهده میکنید. محور افقی از چپ به راست ادامه پیدا میکند. محور عمودی نحوهی ارتباط عناصر را مشخص میکند.
اکنون این سؤال مطرح میشود که چه تفاوتی بین یک Grid و FlexBox CSS وجود دارد؟ در یک Grid طراحی دو بعدی سطرها و ستون وجود دارد. اما به FlexBox باید به صورت سیلان یک بعدی سلولها نگاه کرد. برای مثال عناصر قرار گرفتهی درون Container یا به صورت افقی درون آن گسترده شده و قرار میگیرند و یا به صورت عمودی.
نحوهی تفکر و کارکرد با FlexBox چگونه است؟
در اینجا باید به دو مفهوم دقت داشت:
الف) سیلان عناصر درون Container که میتواند افقی و یا عمودی باشد.
ب) اندازهی المانها که میتواند ثابت و یا نسبی باشد.
یک Container، جهت سیلان عناصر درون آنرا مشخص میکند. المانهای آن، اندازه، فاصلهی از یکدیگر و ترتیب قرارگیری را ارائه میدهند. یک flex container میتواند شامل چندین flex container تو در تو نیز باشد.
نحوهی سیلان عناصر در FlexBox چگونه است؟
برای نمونه طرحبندی متداول ذیل را درنظر بگیرید:
نحوه تفکر در مورد طراحی این طرحبندی، باید از بیرون به درون و از بالا به پایین (سیلان عمودی) باشد:
سپس نحوهی سیلان عناصر درون Containerهای تعریف شده را مشخص میکنیم. برای مثال اولین Container دارای سیلان افقی از چپ به راست خواهد بود که عنصر سوم آن به دلیل اندازههای مشخص دو عنصر قبلی، به سطر دوم منتقل شدهاست.
در ادامه به قسمت میانی میرسیم که آن نیز دارای سیلان افقی از چپ به راست است:
در اینجا نیز میتوان سه Container را متصور شد که وسطی دارای سیلان افقی از راست به چپ است و مواردی که بر اساس اندازهی آنها در یک سطر جا نشدهاند، به سطر بعدی منتقل خواهند شد:
و تمام این سیلانها و انتقال به سطرهای بعدی بر اساس اندازهی المانها صورت میگیرد:
البته در این تصویر یک ایراد هم وجود دارد. با توجه به اینکه در ناحیهی میانی سه Container تعریف خواهند شد. Container ایی که در میان آن قرار میگیرد، دارای سیلان خاص خودش است و اندازههای آن باید نسبت به این Container تعریف شوند و نه نسبت به کل ناحیهی میانی. یعنی بجای اینکه 50 درصد، 25، 25 و 50 درصد را داشته باشیم، اینها در اصل 100 درصد، 50 و 50 درصد و سپس 100 درصد هستند.
معرفی کتابخانهی Angular Flex Layout
برای کار با Flex CSS نیاز است:
- مقدار زیادی کد CSS نوشت.
- نیاز به درک عمیقی از Flex Box دارد.
- نیاز است با باگهای مرورگرها و تفاوتهای پیاده سازیهای آنها در مورد FlexBox آشنا بود.
- نیاز به Prefixing دارد.
- برای Angular طراحی نشدهاست.
جهت رفع این مشکلات و محدودیتها، تیم Angular کتابخانهای را به نام Angular Flex Layout مخصوص نگارشهای جدید Angular طراحی کردهاست. این کتابخانه مستقل از Angular Material است اما عموما به همراه آن استفاده میشود.
مزایای کار با کتابخانهی Angular flex layout
- پیاده سازی TypeScript ایی دارد. در اصل یک سری directives مخصوص Angular است که با TypeScript نوشته شدهاست.
- به صورت پویا و inline تمام CSSهای مورد نیاز را تولید و تزریق میکند.
- به همراه یک API استاتیک است و همچنین یک API واکنشگرا
- با Angular CLI نیز یکپارچه شدهاست.
نصب و تنظیم کتابخانهی Angular Flex layout
برای نصب این کتابخانه، در ریشهی پروژه دستور زیر را صادر کنید:
npm install @angular/flex-layout --save
import { FlexLayoutModule } from "@angular/flex-layout"; @NgModule({ imports: [ FlexLayoutModule ], exports: [ FlexLayoutModule ] }) export class SharedModule { }
کار با API استاتیک Angular Flex layout
API استاتیک Angular Flex layout شامل این مزایا و مشخصات است:
- به صورت یکسری دایرکتیو Angular طراحی شدهاست که به HTML قالب کامپوننتها اضافه میشود.
- از data binding پشتیبانی میکند.
- CSS نهایی را به صورت پویا و inline تولید و به صفحه تزریق میکند. Inline CSS تزریق شده به ویژگیهای styles هر المان تزریق میشوند و موارد مشابه را در صورت وجود بازنویسی میکنند.
- از تشخیص تغییرات پشتیبانی میکند.
- به همراه ویژگیهای fxHide و fxShow است.
- کارآیی مطلوبی دارد.
در اینجا برای تعریف container اصلی از دایرکتیوهای زیر استفاده میشود:
- fxLayout جهتهای flex را مشخص میکند.
<div fxLayout="row" fxLayout.xs="column"></div>
- fxLayoutWrap مشخص میکند که آیا المانها باید به سطر و یا ستون بعدی منتقل شوند یا خیر؟
<div fxLayoutWrap></div>
<div fxLayoutGap="10px"></div>
<div fxLayoutAlign="start stretch"></div>
چند مثال:
و یا حالت راست به چپ آن به صورت زیر است:
و برای تعریف آیتمهای قرار گرفتهی درون containers میتوان از دایرکتیوهای زیر استفاده کرد:
- fxflex برای تعیین اندازه و flex المانها
<div fxFlex="1 2 calc(15em + 20px)"></div>
fxFlex="grow shrink basis"
fxFlex="basis"
- shrink به این معنا است که اگر به اندازهی کافی فضا وجود نداشت، این المان نسبت به سایر المانهای دیگر تا چه اندازهای میتواند کوچک شود.
- basis به معنای اندازهی پیشفرض المان است.
در اینجا اندازهها برحسب پیکسل، درصد و یا calcs, em, cw, vh میتوانند تعیین شوند. همچنین یک سری نام مستعار مانند grow, initial, auto, none, nogrow, noshrink هم قابل استفاده هستند.
- fxflexorder برای تعیین ترتیب قرارگیری یک المان
<div fxFlexOrder="2"></div>
<div fxFlexOffset="20px"></div>
<div fxFlexAlign="center"></div>
<div fxFlexFill></div>
چند مثال:
در اینجا سه نمایشی را که در ذیل تعریف divها مشاهده میکنید بر اساس تغییر اندازهی صفحه حاصل شدهاند. چون آیتم دوم دارای مقدار grow مساوی 5 است، به همین جهت با تغییر اندازهی صفحه و دسترسی به مقدار فضای بیشتر، بزرگتر شدهاست.
یک مثال کامل
اگر علاقمند باشید تا توانمندیهای angular flex layout را در قالب یک مثال کامل مشاهده کنید، به آدرس زیر مراجعه نمائید:
https://tburleson-layouts-demos.firebaseapp.com/#/docs
در این مثال با تغییر گزینههای مختلف، کد معادل angular flex layout آن نیز تولید میشود.
همچنین wiki خود پروژه نیز به همراه مثالهای بیشتری است:
https://github.com/angular/flex-layout/wiki
کار با API واکنشگرای Angular Flex layout
در طراحی واکنشگرا، container و عناصر داخل آن بر اساس تغییرات اندازهی صفحه و یا اندازهی وسیلهی نمایشی، تغییر اندازه و همچنین موقعیت میدهند و این تغییرات بر اساس انطباق با viewport وسیلهی نمایشی صورت میگیرند. به همین جهت برای طراحی واکنشگرا نیاز به Flex CSS و همچنین Media Query است. نوشتن Medial Query و ترکیب آن با Flex CSS کار مشکلی است. به همین جهت Angular Flex layout به همراه یک API واکنشگرا نیز هست که در پشت صحنه Flex CSS را بر اساس طراحی متریال و Medial Queries مورد استفاده قرار میدهد.
اگر علاقمند هستید تا اندازههای واکنشگرای استاندارد متریال را ملاحظه کنید، میتوانید به آدرس زیر مراجعه نمائید (قسمت Breakpoint system آن):
https://material.io/design/layout/responsive-layout-grid.html#breakpoints
برای مثال هر اندازهای کمتر از 600px در گروه extra small قرار میگیرد (با مخفف xs). از 600px تا 1024px در بازهی small (با مخفف sm)، از 1024px تا 1440px در بازهی medium (با مخفف md) و از 1440px تا 1920px در بازهی large (با مخفف lg) و بیشتر از آن در بازهی xlrage قرار میگیرند (با مخفف xl). این اعداد و بازهها، پایهی طراحی API واکنشگرای Angular Flex layout هستند. به همین جهت نام این بازهها در این API به صورت مخفف xs, sm, md, lg, xl درنظر گرفته شدهاند و مورد استفاده قرار میگیرند. همچنین اگر اندازههای مدنظر از این بازهها کمتر باشند، میتوان از lt-sm, lt-md, lt-lg, lt-xl نیز استفاده کرد. در اینجا lt به معنای less than است و یا اگر بازههای مورد نیاز بیش از این اندازهها باشند میتوان با gt-xs, gt-sm, gt-md, gt-lg کار کرد. در اینجا gt به معنای greater than است.
به این مخففها «media query alias» هم گفته میشود و اکنون که لیست آنها مشخص است، تنها کافی است آنها را به API استاتیکی که پیشتر بررسی کردیم، اضافه کنیم. برای مثال:
fxLayout.sm = "..." fxLayoutAlign.md = "..." fxHide.gt-sm = "..."
معادل طراحی آن با API استاتیک Angular Flex Layout به صورت زیر است:
اکنون که این طرحبندی دسکتاپ را داریم، چگونه باید آنرا تبدیل به طرحبندی موبایل، مانند شکل زیر کنیم؟
برای اینکار ابتدا fxLayout.xs را به سطر میانی اضافه میکنیم تا هرگاه به این اندازه رسیدیم، بجای ردیف، تبدیل به ستون شود. سپس توسط fxFlexOrder.xs، در اندازهی xs، محل قرارگیری المانهای این ستون را هم مشخص میکنیم:
همانطور که ملاحظه میکنید کار کردن با این API بسیار سادهاست و نیازی به کارکردن مستقیم با Media Queries و یا برنامه نویسی مستقیم ندارد و تمام آن در قالب HTML یک کامپوننت قابل پیاده سازی است.
یک نکته: مثال کاملی که پیشتر در این بحث مطرح شد، به همراه مثال واکنشگرا نیز هست که برای مشاهدهی اثر آنها بهتر است اندازهی مرورگر را کوچک و بزرگ کنید.
مخفی کردن و یا نمایش قسمتی از صفحه بر اساس اندازهی آن
علاوه بر media query alias هایی که عنوان شد، امکان نمایش و یا مخفی سازی قسمتهای مختلف صفحه بر اساس اندازهی صفحهی نمایشی نیز هست:
<div fxShow fxHide.xs="false" fxHide.lg="true"></div>
تغییر اندازهی قسمتی از صفحه بر اساس اندازهی آن
در مثال زیر اگر اندازهی صفحه gt-sm باشد (بیشتر از small)، اندازهی این div به 100 درصد بجای 50 درصد حالتهای دیگر، تنظیم میشود:
<div fxFlex="50%" fxFlex.gt-sm="100%"></div>
حالتهای ویژهی طراحی واکنشگرا در Angular Flex Layout
در API واکنشگرای آن حالتهای ویژهی fxshow, fxhide, ngclass و ngstyle نیز درنظر گرفته شدهاند که امکان فعالسازی آنها در اندازههای مختلف صفحه مسیر است:
<div fxShow [fxShow.xs]="isVisibleOnMobile()"></div> <div fxHide [fxHide.gt-sm]="isVisibleOnDesktop()"></div> <div [ngClass.sm]="{'fxClass-sm': hasStyle}" ></div> <div [ngStyle.xs]="{color: 'blue'}"></div>
امکان کار با API واکنشگرا از طریق برنامه نویسی
برای این منظور میتوان از سرویس ObservableMedia مانند مثال زیر استفاده کرد:
در اینجا به فعالسازی یک بازهی خاص گوش فرا خواهیم داد. برای مثال اگر اندازهی صفحه xs بود، سبب بارگذاری محتوای خاص مرتبط با موبایل خواهیم شد.
برای مطالعهی بیشتر
سازماندهی برنامههای Angular توسط ماژولها
اگر به CoreModule تعریف شده دقت کنید، یک چنین تعریفی در آن ذکر شدهاست:
providers: [ UserRepositoryService ]
اکنون فرض کنید سرویسی را در ماژول معمولی غیر Lazy loaded تعریف کردهاید. Angular این سرویس را در Root Injector ثبت میکند. به این ترتیب این سرویس در کل برنامه قابل دسترسی میشود. اما اگر سرویسی را در یک ماژول Lazy loaded تعریف کنید، Angular کار ایجاد یک Injector جداگانه را برای آن ماژول و در داخل آن انجام میدهد. این Injector مجزا است از Root Injector قرار گرفتهی در App Module. سپس این سرویس در این Injector جدید ثبت میشود. به این ترتیب، وهلهی تهیه شدهی از این سرویس، تنها درون این ماژول Lazy loaded قابل دسترسی است. حتی اگر این دو سرویس ثبت شدهی در Root Injector و Injector مخصوص ماژول Lazy loaded از یک کلاس تهیه شوند، باز هم دو وهلهی مختلف از آن ارائه میشوند. برای مثال اگر UserRepositoryService را در ماژول معمولی و ماژول Lazy loaded مورد استفاده قرار دهیم، دو وهلهی مجزای از آنها تشکیل خواهد شد که دیگر با هم همگام نبوده و کار ردیابی اطلاعات کاربر جاری سیستم را با مشکل مواجه میکنند.
ایجاد وهلهی دوم از یک سرویس، تنها منحصر است به ماژولهای Lazy loaded. اما اگر این سرویس در ماژولهای معمولی مختلفی مورد استفاده قرار گیرد، Angular تنها یک وهله از آنرا ایجاد خواهد کرد. به همین جهت است که در اینجا CoreModule را تعریف کردیم. این ماژول مکانی است که قرار است سرویسهای اشتراکی در کل برنامه در آن قرار گیرند. چون این ماژول هیچگاه Lazy loaded نخواهد شد، هرگاه سرویسی را توسط آن ارائه دهیم، در Root Injector مربوط به App Module ثبت میشود. به این ترتیب تبدیل به یک Singleton Service قابل دسترسی در کل برنامه میشود.
باید دقت داشت که از لحاظ فنی، تفاوتی بین Core Module و یک ماژول معمولی غیر Lazy loaded نیست و بیشتر هدف از آن نظم بخشیدن به تعریف سرویسهایی است که قرار است در طول عمر برنامه و در تمام انواع ماژولهای آن، توسط یک وهله قابل دسترسی شوند.
بنابراین تفاوتی نمیکند که یک سرویس را درون Core Module تعریف کنید و یا یک ماژول معمولی. این سرویس همواره در Root Injecror ثبت خواهد شد؛ مگر اینکه آن ماژول Lazy loaded باشد. در این حالت اگر سرویسی تنها قرار است در یک ماژول خاص استفاده شود، بهتر است آنرا جهت مدیریت بهتر برنامه، درون همان پوشه تعریف کرد. هرچند از لحاظ فنی، این سرویسها نهایتا در Root Injector مربوط به App Module ثبت و ارائه میشوند (از این لحاظ فرقی بین یک Core Module و Feature Modules نیست).
الف) به قسمت application pools در IIS Manager مراجعه کرده و گزینهی add application pool را انتخاب کنید. سپس یک نمونهی جدید را برای مثال به نام no-managed ایجاد کنید که net clr version. آن به گزینهی no managed code اشاره میکند.
ب) پس از publish برنامه مطابق مطلب فوق (برای مثال با اجرای دستور dotnet publish -c Release) و معرفی مسیر پوشهی publish به IIS (برای مثال به عنوان یک Application جدید ذیل default web site با کلیک راست بر روی default web site و انتخاب گزینهی Add application)، این application pool جدید را به برنامهی خود در IIS نسبت دهید. برای اینکار basic settings سایت را باز کرده و بر روی دکمهی select که در کنار نام application pool هست، کلیک کرده و گزینهی no-managed قسمت الف را انتخاب کنید.
نکته 1: برنامههای blazor wasm، یا standalone هستند و یا hosted. مورد standalone یعنی کاری به Web API ندارد و به خودی خود، به صورت یک سایت استاتیک قابل مشاهدهاست. حالت hosted یعنی به همراه web api هم هست و توسط دستور برای مثال «dotnet new blazorwasm -o BlazorIIS --hosted --no-https» ایجاد میشود که به همراه سه پوشهی کلاینت، سرور و shared است. برای توزیع حالت متکی به خود، فقط محتویات پوشهی publish، به عنوان مسیر برنامه، در IIS معرفی خواهند شد. در حالت hosted، مسیر اصلی، پوشهی publish مربوط به پروژهی سرور است؛ یعنی: Server\bin\Release\net5.0\publish. در این حالت پوشهی Client\bin\Release\net5.0\publish باید به داخل همین پوشهی publish سرور کپی شود. یعنی پوشهی publish پروژهی client باید به درون پوشهی publish پروژهی server کپی شود تا با هم یک برنامهی قابل توزیع توسط IIS را تشکیل دهند.
ج) اکنون اگر برنامه را برای مثال با مسیر فرضی جدید http://localhost/blazortest اجرا کنید، خطای 500.19 را دریافت میکنید. علت آنرا در مطلب «بررسی خطاهای ممکن در حین راه اندازی اولیه برنامههای ASP.NET Core در IIS» بررسی کردهایم. باید IIS URL Rewrite ماژول را نصب کرد؛ تا این مشکل برطرف شود. همچنین دلیل دیگر مشاهدهی این خطا، عدم نصب بستهی هاستینگ متناظر با شماره نگارش NET. مورد استفادهاست (اگر برنامهی شما از نوع hosted است و web api هم دارد).
د) پس از آن باز هم برنامه اجرا نمیشود! اگر در خطاها دقت کنید، به دنبال اسکریپتهایی شروع شده از مسیر localhost است و نه از پوشهی جدید blazortest. برای رفع این مشکل باید فایل publish\wwwroot\index.html را مطابق نکتهی base-URL که کمی بالاتر ذکر شد، ویرایش کرد تا blazor بداند که فایلها، در چه مسیری قرار دارند (و در ریشهی سایت واقع نشدهاند):
<head> <base href="/blazortest/" />
پس اگر قصد توسعه SPA با هر فریمورکی مثل angular را داشته باشید، این را در نظر داشته باشید که دیر یا زود هنگام استفاده از افزونههای جیکوئری به مشکل برخواهید خورد.
بیشتر امکانات تو کار ASP.NET MVC را از دست خواهید داد
به هنگام توسعهی برنامه با استفاده از فریم ورکهای SPA، امکانات توکار ASP.NET MVC مثل اعتبارسنجی یکپارچه و strongly typed viewها را از دست خواهید داد. شاید یک سری پروژه در Github پیدا کنید که سعی کردهاند اینها را با یکدیگر سازگار کنند. اما به محض استفاده متوجه میشوید که اگر همهی کارها را خودتان با Angular انجام بدهید راحتتر هستید تا استفاده از کتابخانههای آزمایشی و ناقص.
البته باز هم نمیگویم که اینها تقصیر AngularJS است. ذات توسعهی SPAها، این گونه است و در توسعهی SPA با هر فریمورکی به این مشکلات برخواهید خورد.
حال که یکسری مشکلات عمومی را بررسی کردیم، بدنیست نگاهی اختصاصی به خود AngularJS بیندازیم.
ضعف طراحی
اگر به تعدای از لینکهای سایت ihateangular مراجعه کنید میبینید که هر کسی نظری دارد: یکی میگوید به هیچ وجه Directive ننویسید، یکی دیگر میگوید کنترلر ننویسید و تمامی کارها را در directiveهای سفارشی نوشته شده توسط خودتان انجام بدهید، کلا همه جا علیه performance این فریمورک صحبت میکنند و همگی به پیچیده بودن آن اذعان دارند.
نتیجه گیری
AngularJS فریمورک خیلی خوبی برای نوشتن برنامههای تست پذیر است و کسی منکر قابلیتهای آن نیست. ولی این را نیز در نظر بگیرید که برای تست پذیر بودن، خیلی چیزها از جمله سادگی کار را از دست میدهید. معمولا میگویند که AngularJS کارهای مشکل را ساده میکند و کارهای ساده را مشکل.
پیشنهاد من این است که اگر هنوز AngularJS را فرا نگرفتهاید، حداقل یادگیری آن را تا انتشار نسخهی 2 آن به تعویق بیندازید. اگر AngularJS را بلد هستید، دیگر آن را در پروژهای استفاده نکنید؛ چون دیگر کدهای شما در نسخهی 2 کار نخواهد کرد و احتیاج به انجام تغییرات گستردهای در کدهای نوشته شده قبلی پیدا میکنید.
Entity Framework Core 2.0 introduces global query filters that can be applied to entities when a model is created. It makes it easier to build multi-tenant applications and support soft deleting of entities. This blog post gives a deeper overview of how to use global query filters in real-life applications and how to apply global query filters to domain entities automatically.
Contents
- Get to know our basic Angular TabsComponent (in master branch here)
- Declare a template within a template using
ng-template
in Angular - Pass a reference of an ng-template to a component and render it in Angular
- Pass data to be rendered in a dynamic ng-template using ngTemplateOutletContext in Angular
- Define an anchor point where to render dynamic components in Angular
- Dynamically instantiate an Angular component
- Destroy a dynamically instantiated Angular component