مطلبی را امروز در حین جستجو در سایت اسکریپتهای گریس مانکی دیدم که محض اطلاعات عمومی بد نیست :)
ابتدا گریس مانکی را نصب کنید :)
https://addons.mozilla.org/en-US/firefox/addon/748
یکبار فایرفاکس را ببندید و باز کنید.
اکنون به آدرس زیر رفته و بر روی دکمه install this script در بالای صفحه کلیک کنید:
http://userscripts.org/scripts/show/9671
بعد از نصب آن، به آدرس زیر مراجعه کنید و همین عملیات را تکرار کنید یعنی install this script
http://userscripts.org/scripts/show/36230
خوب، الان دکمه F4 را فشار دهید. یک صفحه مشکی در پائین صفحه باز خواهد شد. (با فشردن مجدد F4 حذف خواهد شد)
تذکر: اگر با فشردن دکمه F4 تغییری را مشاهده نکردید، یکبار فایرفاکس را ببندید و باز کنید تا اسکریپتها کاملا بارگذاری شوند.
بر روی babylon.persian کلیک کنید تا زرد شود (فعال شود)
اکنون بر روی هر کلمهای در صفحه دوبار کلیک کنید تا انتخاب شود، بلافاصله معنای فارسی آنرا در پائین صفحه خواهید دید.
شایان ذکر است که نیازی به نصب babylon نیست و مستقل عمل میکند.
نکته: برای زیاد کردن ارتفاع آن بر روی فلش به سمت بالا کلیک کنید، ماوس را نگه داشته و به سمت بالا حرکت دهید (یا برعکس به سمت پائین)
اسکریپتهای جالبی را با گریس مانکی بر روی فایرفاکس اجرا میکنند. (اگر لینکهای سایت رو پیگیری کرده باشید تشخیص سالم بودن لینکهای رپید شیر در یک صفحه واقعا کارآمد بود و نمونههای بیشماری که در سایت اسکریپتهای آن میتوانید پیدا کنید یا از آنها ایده بگیرید)
یکی از API های کاربردی و جدید در دنیای وب، BroadcastChannel است که امکان ارسال اطلاعات بین windowها ، Tabها و iframeهای مختلف را که در یک دامنه هستند، فراهم میکند. برای
مثال اگر شما در مرورگری در پنجرههای مختلف یک سایت را باز کرده باشید،
با تغییر در یکی از این پنجرهها، قادر خواهید بود سایر پنجرها را هم مطلع کنید تا در
صورت نیاز، مجددا بارگذاری شوند.
چرا از این API استفاده کنیم؟
یکی از
وب سایتهای مورد علاقهی خود را در مرورگر باز کنید. مثلا یوتیوب و لاگین کنید. حالا در
پنجرهی جدیدی، همین وب سایت را مجددا باز کرده و لاگین کنید. حالا در یکی از پنجرهها، از یوتیوب Logout کنید. خب شما حالا در یکی از پنجرهها لاگین هستید و در یکی دیگر Logout کردهاید. حالا
پنجرههای مرورگر شما دارای دو وضعیت متفاوت هستند. Logged-in در برابر Logged-out و این گاهی باعث دردسر خواهد شد.
این وضعیت حتی میتواند باعث خطرهای امنیتی نیز بشود. تصور کنید که کاربری در یک فضای عمومی مثل یک کافی شاپ وارد سایت شما شدهاست و داشبرد مخصوص به خود را باز کردهاست. بنا به دلایلی کاربر قصد ترک محل را کرده و طبیعتا از برنامه شما Logout خواهد کرد . در این حالت اگر این کاربر برنامه شما را در صفحات مختلف مرورگر باز کرده باشد و لاگین نیز کرده باشد، هر کسی که بعد از او قصد استفاده از این کامپیوتر را داشته باشد ، میتواند به اطلاعات کاربر مورد نظر در آن صفحات دسترسی پیدا کند؛ چه این اطلاعات روی صفحه باشد و چه مثلا اطلاعات یک JWT token. چون کاربر فراموش کرده در صفحات دیگر هم Logout کند.
کد نویسی BroadcastChannel API
در نگاه اول، استفاده از این API ممکن است سخت به نظر برسد؛ ولی در واقع خیلی راحت است. برای نمونه قطعه کد زیر را درنظر بگیرید:
<!DOCTYPE html> <body> <!-- The title will change to greet the user --> <h1 id="title">Hey</h1> <input id="name-field" placeholder="Enter Your Name"/> </body> <script> var bc = new BroadcastChannel('gator_channel'); (()=>{ const title = document.getElementById('title'); const nameField = document.getElementById('name-field'); const setTitle = (userName) => { title.innerHTML = 'Hey ' + userName; } bc.onmessage = (messageEvent) => { // If our broadcast message is 'update_title' then get the new title from localStorage if (messageEvent.data === 'update_title') { // localStorage is domain specific so when it changes in one window it changes in the other setTitle(localStorage.getItem('title')); } } // When the page loads check if the title is in our localStorage if (localStorage.getItem('title')) { setTitle(localStorage.getItem('title')); } else { setTitle('please tell us your name'); } nameField.onchange = (e) => { const inputValue = e.target.value; // In the localStorage we set title to the user's input localStorage.setItem('title', inputValue); // Update the title on the current page setTitle(inputValue); // Tell the other pages to update the title bc.postMessage('update_title'); } })() </script>
این کد شامل یک Input باکس و یک title است. وقتی کاربر نام خود را در Input باکس وارد میکند، برنامه آن را در Localstorage با کلیدی به نام userName ذخیره میکند و بعد title صفحه جاری را به سلام + userName تغییر میدهد. مثلا اگر کاربر در Input باکس، عبارت بابک را وارد کند، title صفحه به سلام بابک تغییر داده میشود.
بدون BroadcastChannel، چنانچه کاربر در پنجرههای مختلف مرورگر، برنامه را باز کرده باشد، تغییری در Title آن صفحات داده نخواهد شد؛ مگر اینکه مجددا توسط کاربر بارگذاری شود.
در کد فوق ما یک وهله از BroadcastChannel را به نام gator_channel ایجاد کردهایم و بعد onmessage را مساوی متدی با یک آرگومان جهت دریافت پیام قرار دادهایم. در این متد چک شده که اگر نام پیام، مساوی update_title باشد، متغیر ذخیره شدهی در LocalStorage خوانده شود.
هربار که متد postMessage ، از BroadcastChannel را فراخوانی میکنیم، این متد، باعث اجرای متد onmessage در سایر پنجرهها میشود. پس اگر در پنجرهی جاری در Input باکس، کلمه فرهاد را بنویسیم، متد bc.postMessage('update_title') در پنجره جاری اجرا شده و باعث اجرای متد onmessage در سایر پنجرههایی که سایتمان در آن باز است میشود.
این API در چه حالتهایی کار میکند
برخلاف سایر APIها مثل window.postMessage، شما لازم نیست چیزی در مورد اینکه چند تا صفحه از سایتتان بر روی مرورگر جاری باز شده را بدانید. (توجه کنید که روی عبارت «مرورگر جاری» تاکید میکنم. چون اگر برنامه روی دو مرورگر مثلا Chrome و Firefox به صورت همزمان باز باشد، این API فقط روی صفحات باز مرورگر جاری فراخوانی خواهد شد و نه مرورگر دوم؛ توضیحات بیشتر در ادامه داده شده است)
BroadcastChannel فقط روی مرورگر جاری و صفحاتی از یک دامنه، اجرا خواهد شد. این به این معنا است که شما میتوانید پیامهایتان را از دامنه مثلا : https://alligator.io به دامنه https://alligator.io/js/broadcast_channels ارسال کنید. تنها نکتهای که لازم است تا رعایت شود این است که آبجکت BroadcastChannel در هر دو صفحه، از یک نام برای channel استفاده کرده باشند:
const bc = new BroadcastChannel('alligator_channel'); bc.onmessage = (eventMessage) => { // do something different on each page }
در حالتهای زیر این API کار نخواهد کرد:
هاستهای متفاوت:
https://alligator.io
https://www.aligator.io
پورتهای متفاوت:
https://alligator.io
https://alligator.io :8080
پروتکلهای متفاوت:
https://alligator.io
http://alligator.io
و یا برای مثال اگر مثلا در مرورگر Chrome یکی از صفحات به صورت Incognito باز شده باشد.
سازگاری این API با مرورگرهای مختلف
با توجه به اطلاعات سایت caniuse.com، این API در 75.6% مرورگرها پشتیبانی میشود. ولی مرورگرهای Safari و Internet Explorer از این API پشتیبانی نمیکنند. همچنین امکان استفاده از این API توسط کتابخانه sysend.js نیز فراهم شدهاست.
چه نوع پیامهایی را میتوانید به کمک این API ارسال کنید
- تمامی تایپها (Boolean,Null, Undefined,Number,BigInt, String) به غیر از symbol
- آبجکتهای Boolean و String
- Dates
- Regular Expressions
- Blobs
- Files, File Lists
- Array Buffer, ArrayBufferViews
- ImageBitmaps, ImageDates
- Arrays,Objects,Maps and Sets
قطعه کد زیر، بجای string، یک object را ارسال میکند:
bc.onmessage = (messageEvent) => { const data = messageEvent.data // If our broadcast message is 'update_title' then get the new title from localStorage switch (data.type) { case 'update_title': if (data.title){ setTitle(data.title); } else setTitle(localStorage.getItem('title')); break default: console.log('we received a message') } }; // ... Skipping Code bc.postMessage({type: 'update_title', title: inputValue});
چه کارهایی را میتوانید به کمک این API انجام دهید
چیزهای زیادی را میتوان مجسم کرد. محتملترین گزینه، به اشتراک گذاری state جاری برنامه است. برای مثال اگه از کتابخانههای flux یا redux برای مدیریت state برنامه استفاده میکنید، به کمک این API میتوانید state جاری را در تمامی صفحات باز برنامه، بروز رسانی کنید. حتی میتوانید به چیزی شبیه به machine state فکر کنید.
یا مثلا آخرین وضعیت سبد خرید کالای مشتری و یا موجودی کالاها، در یک سایت خرید آنلاین. همچنین به اشتراک گذاری فایلهای حجیم مثل عکس و غیره جهت جلوگیری از دانلود مجدد آنها در سایر صفحات.
به کمک دستور ()bc.close در هر زمانی میتوانید channel باز شده را ببندید و مجددا بسته به وضعیت برنامه، آن را باز کنید.
IdentityServer4.1.0.0 منتشر شد
EF5 منتشر شد
تزئین مسیر انتخاب شده در منوی سایت
برای بهبود ظاهر برنامه نیاز است منوی سایت را به نحوی تغییر دهیم که مشخص کند، اکنون کاربر کدام گزینه را انتخاب کردهاست. این مورد شامل سلسه مراتب مسیریابیها نیز میشود؛ برای مثال فعالسازی حالت انتخاب شدهی منوی سایت، به همراه برگهی انتخاب شده در یکی از Child Routes.
برای پیاده سازی این قابلیت، دایرکتیو ویژهای به نام routerLinkActive تدارک دیده شدهاست. این دایرکتیو را میتوان به یک anchor tag و یا المان والد آن انتساب داد. مقدار آنرا نیز میتوان به یکی از کلاسهای CSS برنامه مانند کلاس active تعریف شدهی در بوت استرپ تنظیم کرد. هر زمانیکه این مسیریابی فعال شود، مسیریاب به صورت خودکار این کلاس را با درج آن، به المان مرتبط اضافه میکند و برعکس.
برای نمونه فایل src\app\product\product-edit\product-edit.component.html را گشوده و سپس تغییرات ذیل را اعمال کنید:
<div class="wizard"> <a [routerLink]="['info']" routerLinkActive="active"> Basic Information </a> <a [routerLink]="['tags']" routerLinkActive="active"> Search Tags </a> </div>
یک نکته: از آنجائیکه در اینجا مقدار active یک string است و نه یک خاصیت یا عبارت متغیر، به همین جهت نیازی نیست تا این دایرکتیو را به صورت [routerLinkActive] تعریف کنیم.
همانطور که مشاهده میکنید، همین دو تنظیم ساده سبب مشخص شدن برگهی انتخابی شدهاند.
منوی بالای سایت نیز چنین تنظیماتی را نیاز دارد. برای این منظور به فایل src\app\app.component.html که دربرگیرندهی منوی سایت است مراجعه کرده و تغییرات ذیل را اعمال میکنیم:
<ul class="nav navbar-nav"> <li routerLinkActive="active"> <a [routerLink]="['/home']">Home</a> </li> <li routerLinkActive="active"> <a [routerLink]="['/products']">Product List</a> </li> <li routerLinkActive="active"> <a [routerLink]="['/products', 0, 'edit']">Add Product</a> </li> </ul>
همانطور که مشاهده میکنید، در این حالت انتخاب منوی نمایش لیست محصولات، سبب تزئین آن به حالت انتخاب شده نیز گردیدهاست.
مشکل! در همین حالت که مسیر نمایش لیست محصولات انتخاب شدهاست، لینک افزودن یک محصول جدید را نیز انتخاب کنید:
اینبار هر دو گزینه با هم انتخاب شدهاند. علت اینجا است که این دو مسیر دارای root URL segment یکسانی هستند؛ یا همان products/ در اینجا. به همین جهت routerLinkActive هر دو را به عنوان فعال انتخاب کردهاست. برای مدیریت میدان دید آن میتوان از دایرکتیو دیگری به نام routerLinkActiveOptions استفاده کرد:
<li routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }"> <a [routerLink]="['/products']">Product List</a> </li>
اکنون کاربران بهتر میتوانند درک کنند در کجای برنامه قرار دارند.
افزودن آیکن خطا به برگهای که دارای مشکل اعتبارسنجی است
در ادامه میخواهیم اگر برگهای دارای مشکلات اعتبارسنجی بود، آیکن خطایی را در کنار برچسب آن برگه نمایش دهیم. به این ترتیب مدیریت چندین برگه برای کاربران سادهتر خواهد شد و به سادگی میتوانند برگههای مشکل دار را پیدا کنند.
در انتهای مطلب «مسیریابی در Angular - قسمت پنجم - تعریف Child Routes» متد isValid را تعریف کردیم. این متد مسیر یک tab را دریافت کرده و اگر اعتبارسنجی آن مشکلی نداشت، مقدار true را بر میگرداند. از این متد جهت نمایش آیکن خطای اعتبارسنجی برگهها استفاده خواهیم کرد.
<div class="wizard"> <a [routerLink]="['info']" routerLinkActive="active"> Basic Information <span [ngClass]="{'glyphicon glyphicon-exclamation-sign': !isValid('info')}"></span> </a> <a [routerLink]="['tags']" routerLinkActive="active"> Search Tags <span [ngClass]="{'glyphicon glyphicon-exclamation-sign': !isValid('tags')}"></span> </a> </div>
رخدادهای مسیریابی
هر زمانیکه کاربری مسیرهای مختلف برنامه را پیمایش میکند، مسیریاب تعدادی رخداد را نیز تولید خواهد کرد. از این رخدادها جهت تحت نظر قرار دادن، عیبیابی و یا اجرای منطقی میتوان استفاده کرد. این رخدادها شامل موارد ذیل هستند:
- NavigationStart، با آغاز پیمایش یک مسیر رخ میدهد.
- RoutesRecognized، با تشخیص و تطابق یک مسیر، با یکی از المانهای تعریف شدهی در تنظیمات مسیریابی رخ میدهد.
- NavigationEnd، با پایان پیمایش یک مسیر رخ میدهد.
- NavigationCancel، در صورت لغو پیمایش یک مسیریابی توسط محافظهای مسیرها و یا هدایت به یک جهت دیگر رخ میدهد.
- NavigationError، با شکست پیمایش یک مسیر رخ میدهد.
این رخدادها با فعالسازی تنظیم enableTracing تنظیمات مسیریابی به true فعال میشوند. برای این منظور فایل src\app\app-routing.module.ts را گشوده و به نحو ذیل تغییر دهید:
@NgModule({ imports: [RouterModule.forRoot(routes/*, { useHash: true }*/, { enableTracing: true })],
در اینجا ترتیب اجرای رخدادهای متفاوت پیمایش مسیر نمایش لیست محصولات را مشاهده میکنید.
- Router به هر مسیر، یک id خود افزایش یابنده را به صورت خودکار نسبت میدهد. برای نمونه، این مسیر خاص، id:2 را یافتهاست. از این id میتوان برای دسترسی به مجموعهای از رخدادها استفاده کرد.
- در این خروجی، url همان آدرس اصلی مسیر است و urlAfterRedirects به معنای مسیری است که پس از تنظیم redirect در تنظیمات مسیریابی (در صورت وجود) حاصل شدهاست.
- یکی از روشهایی که برای دیباگ مسیریابیها میتوان استفاده کرد، همین فعالسازی enableTracing است.
کار با رخدادهای مسیریابی با کدنویسی
به رخدادهایی که در کنسول developer tools مرورگر مشاهده کردید، با کدنویسی نیز میتوان دسترسی یافت. برای مثال میتوان یک تصویر چرخنده یا لطفا منتظر بمانید را در آغاز پیمایش یک مسیریابی نمایش داد و سپس در پایان پیمایش این مسیریابی، آنرا مخفی کرد. این events نیز از نوع Observable بوده و برای کار با آنها باید مشترکشان شد:
this.router.events.subscribe((routerEvent: Event) => { if (routerEvent instanceof NavigationStart) { //... } });
در مثال جاری این سری، در «مسیریابی در Angular - قسمت چهارم - پیش واکشی اطلاعات»، سبب شدیم تا کل اطلاعات مورد نیاز یک مسیر، پیش از نمایش آن از سرور دریافت شوند تا به این صورت ابتدا یک قاب خالی نمایش داده نشده و پس از مدتی تکمیل شود. هرچند تجربهی کاربری این روش بهتر از روش قبلی است، اما هنوز هم کاربر تاخیری را در ابتدا حس خواهد کرد (به اندازهی زمان delay تنظیم شده)، بدون اینکه راهنمایی به او ارائه شود. در این حالت بهتر است در ابتدای کار، یک تصویر چرخنده نمایش داده شود تا کاربر متوجه شود، نیاز است اندکی منتظر بماند.
در اینجا میخواهیم این تصویر چرخنده برای تمام مسیرهای برنامه فعال شود. به همین جهت گوش فرادادن به رخدادها را در نقطهی آغازین برنامه و یا همان src\app\app.component.ts انجام میدهیم:
import { Router, Event, NavigationStart, NavigationEnd, NavigationError, NavigationCancel } from '@angular/router'; import { AuthService } from './user/auth.service'; import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { pageTitle: string = 'Routing Lab'; loading: boolean = true; constructor(private authService: AuthService, private router: Router) { router.events.subscribe((routerEvent: Event) => { this.checkRouterEvent(routerEvent); }); } checkRouterEvent(routerEvent: Event): void { if (routerEvent instanceof NavigationStart) { this.loading = true; } if (routerEvent instanceof NavigationEnd || routerEvent instanceof NavigationCancel || routerEvent instanceof NavigationError) { this.loading = false; } } logOut(): void { this.authService.logout(); this.router.navigateByUrl('/welcome'); } }
- ابتدا وابستگیهای لازم آن import شدهاند.
- سپس میخواهیم خاصیت عمومی loading را در شروع به پیمایش یک مسیر، به true تنظیم کنیم و اگر این پیمایش به هر نحوی خاتمه یافت، آنرا false خواهیم کرد.
اکنون برای استفادهی از این خاصیت عمومی و نمایش تصویر چرخنده، نیاز است قالب src\app\app.component.html را ویرایش کنیم:
<span class="glyphicon glyphicon-refresh glyphicon-spin spinner" *ngIf="loading"></span>
/* Spinner */ .spinner { font-size:300%; position:absolute; top: 50%; left: 50%; z-index:10 } .glyphicon-spin { -webkit-animation: spin 1000ms infinite linear; animation: spin 1000ms infinite linear; } @-webkit-keyframes spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg); } } @keyframes spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg); } }
اکنون مسیرهایی که دارای route resolver هستند (مانند نمایش جزئیات/ویرایش یک محصول)، به همراه یک spinner نمایش داده خواهند شد و سایر مسیرها ابتدا نمایش داده خواهند شد و سپس اطلاعات آنها از سرور دریافت میشود (مانند مسیر نمایش لیست محصولات که دارای route resolver نیست).
البته میتوان این true/false کردن loading را به ابتدا و انتهای کار یک Observable، مانند حالت نمایش لیست محصولات نیز منتقل کرد. اما در این حالت باید span مرتبط را نیز به قالب همان کامپوننت انتقال داد و دیگر سراسری نخواهد بود.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: angular-routing-lab-06.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کردهاید. سپس از طریق خط فرمان به ریشهی پروژه وارد شده و دستور npm install را صادر کنید تا وابستگیهای آن دریافت و نصب شوند. در آخر با اجرای دستور ng s -o برنامه ساخته شده و در مرورگر پیش فرض سیستم نمایش داده خواهد شد.
1) فراموش میکنیم تا اسکریپت اصلی jQuery را به درستی پیوست و مسیردهی کنیم.
2) مسیر Generic handler دیگری را ذکر میکنیم.
3) مسیرهای تصاویری را که Image slider باید نمایش دهد، کاملا بیربط ذکر میکنیم.
4) خروجی JSON نامربوطی را بازگشت میدهیم.
5) یکبار هم یک استثنای عمدی دستی را در بین کدها قرار خواهیم داد.
و ... بعد سعی میکنیم با استفاده از Firebug عیوب فوق را یافته و اصلاح کنیم؛ تا به یک برنامه قابل اجرا برسیم.
معرفی برنامهای که کار نمیکند!
یک برنامه ASP.NET Empty web application را آغاز کنید. سپس سه پوشه Scripts، Content و Images را به آن اضافه نمائید. در این پوشهها، اسکریپتهای نمایش دهنده تصاویر، Css آن و تصاویری که قرار است نمایش داده شوند، قرار میگیرند:
سپس یک فایل default.aspx و یک فایل OrbitHandler.ashx را نیز به پروژه با محتویات ذیل اضافه کنید: (در این دو فایل، 5 مورد مشکل ساز یاد شده لحاظ شدهاند)
محتویات فایل OrbitHandler.ashx.cs مطابق کدهای ذیل است:
using System.Collections.Generic; using System.IO; using System.Web; using System.Web.Script.Serialization; namespace OrbitWebformsTest { public class Picture { public string Title { set; get; } public string Path { set; get; } } public class OrbitHandler : IHttpHandler { IList<Picture> PicturesDataSource() { var results = new List<Picture>(); var path = HttpContext.Current.Server.MapPath("~/Images"); foreach (var item in Directory.GetFiles(path, "*.*")) { var name = Path.GetFileName(item); results.Add(new Picture { Path = /*"Images/" + name*/ name, Title = name }); } return results; } public void ProcessRequest(HttpContext context) { var items = PicturesDataSource(); var json = /*new JavaScriptSerializer().Serialize(items)*/ string.Empty; throw new InvalidDataException("همینطوری"); context.Response.ContentType = "text/plain"; context.Response.Write(json); } public bool IsReusable { get { return false; } } } }
همچنین کدهای صفحه ASPX ایی که قرار است (به ظاهر البته) از این Generic handler استفاده کند به نحو ذیل است:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="default.aspx.cs" Inherits="OrbitWebformsTest._default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> <link href="Content/orbit-1.2.3.css" rel="stylesheet" type="text/css" /> <script src="Script/jquery-1.5.1.min.js" type="text/javascript"></script> <script src="Scripts/jquery.orbit-1.2.3.min.js" type="text/javascript"></script> </head> <body> <form id="form1" runat="server"> <div id="featured"> </div> </form> <script type="text/javascript"> $(function () { $.ajax({ url: "Handler.ashx", contentType: "application/json; charset=utf-8", success: function (data) { $.each(data, function (i, b) { var str = '<img src="' + b.Path + '" alt="' + b.Title + '"/>'; $("#featured").append(str); }); $('#featured').orbit(); }, dataType: "json" }); }); </script> </body> </html>
مراحل عیب یابی برنامهای که کار نمیکند!
ابتدا برنامه را در فایرفاکس باز کرده و سپس افزونه Firebug را با کلیک بر روی آیکن آن، بر روی سایت فعال میکنیم. سپس یکبار بر روی دکمه F5 کلیک کنید تا مجددا مراحل بارگذاری سایت تحت نظر افزونه Firebug فعال شده، طی شود.
اولین موردی که مشهود است، نمایش عدد 3، کنار آیکن فایرباگ میباشد. این عدد به معنای وجود خطاهای اسکریپتی در کدهای ما است.
برای مشاهده این خطاها، بر روی برگه Console آن کلیک کنید:
بله. مشخص است که مسیر دهی فایل jquery-1.5.1.min.js صحیح نبوده و همین مساله سبب بروز خطاهای اسکریپتی گردیده است. برای اصلاح آن سطر زیر را در برنامه تغییر دهید:
<script src="Scripts/jquery-1.5.1.min.js" type="text/javascript"></script>
مجددا دکمه F5 را فشرده و سایت را با تنظیمات جدید اجرا کنید. اینبار در برگه Console و یا در برگه شبکه فایرباگ، خطای یافت نشدن Generic handler نمایان میشوند:
برای رفع آن به فایل default.aspx مراجعه و بجای معرفی Handler.ashx، نام OrbitHandler.ashx را وارد کنید.
مجددا دکمه F5 را فشرده و سایت را با تنظیمات جدید اجرا کنید.
اگر به برگه کنسول دقت کنیم، بروز استثناء در کدها تشخیص داده شده و همچنین در برگه Response پاسخ دریافتی از سرور، جزئیات صفحه خطای بازگشتی از آن نیز قابل بررسی و مشاهده است.
اینبار به فایل OrbitHandler.ashx.cs مراجعه کرده و سطر throw new InvalidDataException را حذف میکنیم. در ادامه برنامه را کامپایل و مجددا اجرا خواهیم کرد.
با اجرای مجدد سایت، تبادل اطلاعات صحیحی با فایل OrbitHandler.ashx برقرار شده است، اما خروجی خاصی قابل مشاهده نیست. بنابراین بازهم سایت کار نمیکند.
برای رفع این مشکل، متد ProcessRequest را به نحو ذیل تغییر خواهیم داد:
public void ProcessRequest(HttpContext context) { var items = PicturesDataSource(); var json = new JavaScriptSerializer().Serialize(items); context.Response.ContentType = "text/plain"; context.Response.Write(json); }
بله. تمام تنظیمات به نظر درست هستند، اما در برگه شبکه فایرباگ تعدادی خطای 404 و یا «یافت نشد»، مشاهده میشوند. مشکل اینجا است که مسیرهای بازگشت داده شده توسط متد Directory.GetFiles، مسیرهای مطلقی هستند؛ مانند c:\path\images\01.jpg و جهت نمایش در یک وب سایت مناسب نمیباشند. برای تبدیل آنها به مسیرهای نسبی، اینبار کدهای متد تهیه منبع داده را به نحو ذیل ویرایش میکنیم:
IList<Picture> PicturesDataSource() { var results = new List<Picture>(); var path = HttpContext.Current.Server.MapPath("~/Images"); foreach (var item in Directory.GetFiles(path, "*.*")) { var name = Path.GetFileName(item); results.Add(new Picture { Path = "Images/" + name, Title = name }); } return results; }
اینبار اگر برنامه را اجرا کنیم، بدون مشکل کار خواهد کرد.
بنابراین در اینجا مشاهده کردیم که اگر «برنامهای مبتنی بر jQuery کار نمیکند»، چگونه باید قدم به قدم با استفاده از فایرباگ و امکانات آن، به خطاهایی که گزارش میدهد و یا مسیرهایی را که یافت نشد بیان میکند، دقت کرد تا بتوان برنامه را عیب یابی نمود.
سؤال مهم: اجرای کدهای jQuery Ajax فوق، چه تغییری را در صفحه سبب میشوند؟
اگر به برگه اسکریپتها در کنسول فایرباگ مراجعه کنیم، امکان قرار دادن breakpoint بر روی سطرهای کدهای جاوا اسکریپتی نمایش داده شده نیز وجود دارد:
در اینجا همانند VS.NET میتوان برنامه را در مرورگر اجرا کرده و تگهای تصویر پویای تولید شده را پیش از اضافه شدن به صفحه، مرحله به مرحله بررسی کرد. به این ترتیب بهتر میتوان دریافت که آیا src بازگشت داده شده از سرور فرمت صحیحی دارد یا خیر و آیا به محل مناسبی اشاره میکند یا نه. همچنین در برگه HTML آن، عناصر پویای اضافه شده به صفحه نیز بهتر مشخص هستند: