نظرات مطالب
Blazor 5x - قسمت 34 - توزیع برنامه‌های Blazor بر روی IIS
اگر برنامه خود را توسط دستور dotnet new blazorwasm -o BlazorIIS --hosted --no-https  ایجاد نکردیم و یا گزینه hosting module را در ویژوال استودیو نزدیم، چگونه برنامه blazor wasm خود را بهمراه api آن در یک سایت iss پابلیش کنیم؟

- پکیج زیر را به پروژه سرور خود اضافه کنید
Microsoft.AspNetCore.Components.WebAssembly.Server
- در فایل program.cs پروژه سرور، کد‌های زیر را در پایین UseHttpsRedirection اضافه کنید:
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();

app.UseRouting();
- پایین MapControllers  کد زیر را اضافه کنید:
app.MapFallbackToFile("index.html");
- پروژه کلاینت (پروژه blazor wasm) که دقیقا با پروژه سرور در یک solution رفرنس داده شده است را به پروژه سرور رفرنس دهید.
یعنی پروژه blazor wasm شما باید در پروژه API شما رفرنس داده شود.
نکته: در این جا میتوانید برای پیاده سازی منطق از یک assembly یکسان جهت کنترل مشترک logic بین دو پروژه استفاده کنید.
کار تمام شده است و میتوانید توسط دستو dotnet publish که در مقاله و نظرات، کامل به آن اشاره شده است، استفاده کنید
نظرات مطالب
شروع به کار با EF Core 1.0 - قسمت 1 - برپایی تنظیمات اولیه
این احتمال وجود دارد در صورتی که پروژه را از طریق cli و vscode اجرا میکنید محیط اجرایی بر روی حالت Production قرار گرفته باشد و باعث شود صفحه پیام خطا به شما نشان داده نشود. بعد از اجرای dotnet run در خطوط اولیه زیر در جلوی عبارت Hosting Environment این حالت قابل نمایش است:
> dotnet run
Project TestApp (.NETCoreApp,Version=v1.0) was previously compiled. Skipping compilation.

Hosting environment: Production  
Content root path: C:\Projects\TestApp  
Now listening on: http://localhost:5000  
Application started. Press Ctrl+C to shut down.
برای تغییر این حالت به حالت توسعه کدهای زیر را در CLI وارد نمایید:
>set ASPNETCORE_ENVIRONMENT "Development"

SUCCESS: Specified value was saved.
بعد از تغییر موفقیت آمیز مشکل همچنان ادامه دارد و دلیل آن اینست که این مقدار قبلا توسط Console خوانده شده است و مجددا باید کنسول جدید را اجرا نمایید تا مشکل حل شود.
مطالب
Ef 6 و Ngen : شروعی سریعتر برای برنامه های مبتنی بر Entity Framework
تولید کد Native زمانی اتفاق می‌افتد که کامپایلر JIT، کد اسمبلی‌های MSIL را به کدهای Native در ماشین محلی کامپایل می‌کند و این عمل بلافاصله قبل از اجرای متد برای اولین بار اتفاق می‌افتد. این کد به صورت موقتی بوده و در حافظه‌ای که برای پردازش در نظر گرفته شده ذخیره می‌شود و در پایان هر پردازش توسط سیستم عامل ویرایش می‌شود. کد Native به ازای هر بار شروع یک پردازش تولید می‌شود. ابزار Native Image Generator یا همان Ngen اقدام به تولید کد Native با استفاده از کامپایلر JIT نموده و آن را در هارد دیسک ذخیره می‌نماید. زمانیکه برنامه نیازمند یک اسمبلی CLR است، به جای بارگذاری خود اسمبلی، ایمیج کد Native آن بارگذاری می‌شود. به این نکته نیز توجه داشته باشید که CLR اطلاعاتی در مورد اینکه کدام اسمبلی، ایمیج کد Native است و این ایمیج در کجا و در چه زمانی تهیه شده است، دارد. کد Native باعث بهبود استفاده از حافظه می‌شود، زمانیکه یک اسمبلی بین پروسس‌ها به اشتراک گذاشته شده‌است. تا قبل از EF6 کتابخانه‌های هسته‌ای EF در زمان اجرا جزئی از دات نت فریمورک بودند و تولید کد Native آنها به صورت اتوماتیک انجام می‌شد. اما از نسخه 6، تمامی این کتابخانه‌ها در داخل پکیج Nuget آن ترکیب شده‌اند . پس برای تولید کد Native مربوط به فایل EntityFramework.dll نیازمند ابزار Ngen هستیم.
1- ابتدا یک برنامه‌ی ساده کنسول ویندوز ساخته و از Package Manager Console دستور Install-package entityframework را اجرا نموده تا پکیج Ef به برنامه اضافه گردد.
using System;
using System.Data.Entity;

namespace UsingNgen
{

    public class NgenDbContex : DbContext
    { }

    class Program
    {
        static void Main()
        {
            var nGenCtx = new NgenDbContex();
            Console.WriteLine("Press a key to exit...");
            Console.ReadKey();
        }
    }


}
حال کد ساده بالا را به برنامه اضافه می‌کنیم و برنامه را Build میکنیم.
2- برای ثبت جزئیات اجرای برنامه از ابزار Windows Performance Recorder که جزئی از ویندوز می‌باشد، استفاده می‌کنیم. کافیست عبارت WPR را در نوار جستجوی ویندوز تایپ کنید تا این ابزار در دسترس قرار گیرد. 



برای ضبط جزئیات، روی دکمه‌ی Start کلیک کنید و به محل ذخیره‌ی فایل اجرایی حاصل از Build ویژوال استودیو رفته و آن را اجرا کنید. بعد از اتمام اجرا، جزئیات را ذخیره نمایید.

بعد از ذخیره فایل، در پنجره بالا دکمه‌ای به نام Open in WPA ظاهر می‌شود. WPA مخفف Windows Performance Analyzer می‌باشد. آن را کلیک کنید تا محیط آنالایزر باز شود.

حال در سمت چپ این پنجره انواع آنالایزرها را مشاهده می‌کنید. روی آنالایزر Computation کلیک کنید و از زیرمجموعه‌ی آن، CPU Usage را انتخاب کنید. آمار مربوط به برنامه خودمان را در تصویر بالا مشاهده می‌کنید. کل برنامه 164 میلی ثانیه زمان برده و فایل Clr.dll حدود 47 میلی ثانیه و یک فایل clrjit.dll نیز برای تولید کد JIT وجود دارد. حال برای تسریع در عمل شروع، از تکنیک Ngen به صورت زیر استفاده می‌کنیم.

3- دوباره به نوار جستجوی ویندوز رفته و ابزار Developer Command Prompt for VsXXXX را با امتیاز دسترسی از نوع Admin اجرا کنید. XXXX نسخه‌ی ویژوال استودیو می‌باشد.

حال به محل ذخیره فایل اجرایی برنامه رفته و دستور Ngen Install EntityFramework.dll را تایپ کنید تا یک ایمیج کد Native از entityframework.dll ساخته شود. دوباره ابزار Windows Performance Recorder را لود کرده و روی دکمه Start کلیک کنید و فایل اجرایی برنامه را اجرا نمایید. پس از اتمام عملیات ثبت جزئیات، آن را در Windows Performance Analyzer باز نمایید.

همانطور که مشاهده می‌کنید کل برنامه ما 89 میلی ثانیه زمان برده و Clr.dll 29 ثانیه و به جای clrjit.dll فایل EntityFramework به صورت native تولید شده است.

نظرات مطالب
بررسی روش ارتقاء به NET Core 1.1.
چند نکته در مورد VS 2015 و به روز رسانی‌های جدید NET Core.

آخرین نگارشی را که می‌توانید بدون مشکل با VS 2015 اجرا کنید، 1.0.0-preview2-1-003177 است (واقع در پوشه‌ی C:\Program Files\dotnet\sdk). پس از آن، این سیستم از نگارش JSON ایی فعلی به XML ایی تغییر کرده‌است و ابزارهای آن فقط برای VS 2017 ارائه شده‌اند و VS 2015 از این لحاظ دیگر هیچ پشتیبانی نخواهد داشت (حتی NuGet 4.0 هم برای آن به روز رسانی نشده‌است).

اگر برای مثال SDK مربوط به .NET Core 1.1.1. را نصب کنید و سپس فایل global.json را به 1.0.1 تغییر دهید:
 C:\Users\Vahid>dotnet --version
1.0.1
پروژه با خطای ذیل روبرو شده و در VS 2015 باز نخواهد شد.
 The following error occurred attempting to run the project model server process (1.0.1).
Unable to start the process. No executable found matching command "dotnet-projectmodel-server"
بنابراین فایل global.json را که با VS 2017 منسوخ شده و حذف شده در نظر گرفته شده‌است، دیگر به روز رسانی نکنید.

اگر برای مثال SDK مربوط به .NET Core 1.1.1. را نصب کنید، درون VS 2015 قادر به Restore بسته‌های نیوگت نخواهید شد و با پیام خطای ذیل مواجه می‌شوید:
 \.vs\restore.dg(1,1): error MSB4025: The project file could not be loaded. Data at the root level is invalid. Line 1, position 1.
در این حالت از این پس سه راه را پیش رو خواهید داشت:
1- ارتقاء به VS 2017 و فراموش کردن VS 2015
2- استفاده از VS 2015 و بازیابی بسته‌ها از طریق خط فرمان (چون دیگر ابزارهای VS 2015 با نگارش جدید SDK سازگار نیستند)
 برای این منظور دقت کنید در پنجره‌ی output ویژوال استودیوی 2015، چه فرمانی صادر شده‌است که سبب بروز خطای فوق گردیده‌است:
 "C:\Program Files\dotnet\dotnet.exe" restore "D:\project1\.vs\restore.dg"
از طریق خط فرمان به پوشه‌‌های پروژه‌ها وارد شده (دکمه‌ی shift را نگه داشته و کلیک راست کنید. سپس گزینه‌ی open command window here را انتخاب نمائید) و دستور فوق را اجرا کنید. این دستور از این پس تنها در خط فرمان بدون مشکل اجرا می‌شود و نه در داخل VS 2015.
پس از آن پروژه بدون مشکل Build می‌شود (در داخل VS 2015).
3- و یا ... این SDK جدید 1.0.1 را حذف کنید از سیستم (اگر می‌خواهید با VS 2015 بدون دردسر کار کنید).


و یا کلا به VSCode مهاجرت کنید و VS کامل را فراموش کنید. VSCode با ابزارهای خط فرمان NET Core. کار می‌کند و در این حالت به سادگی می‌توان همواره آخرین نگارش NET Core. را مورد استفاده قرار داد؛ بدون نگرانی از سازگاری ابزارهای ویژوال استودیو با آن. چون اساسا هیچ نوع وابستگی به این ابزارها ندارد. همچنین حجم بسیار کمتری هم داشته و اگر با دریافت VS 2017 مشکل دارید، مهاجرت به VSCode انتخاب بسیار مناسبی است.


خلاصه‌ی بحث
از این پس برای کار کردن بدون دردسر با نگارش‌های جدید NET Core. تنها دو راه را پیش رو دارید: مهاجرت به VS 2017 و یا مهاجرت به VSCode.
مطالب
MongoDb در سی شارپ (بخش پنجم)
یکی از رکن‌های اساسی یک دیتابیس، حفظ اطلاعات موجود بر روی سرور میباشد تا از لحاظ نگهداری و امنیت، تضمین بازگشت اطلاعات سابق وجود داشته باشد. برای پشتیبان گیری از اطلاعات، از فایل جداگانه‌ی دیگری درشاخه Bin استفاده میکنیم که MongoDump نام دارد و یک فایل دامپ را ایجاد میکند. این فایل شامل تعدادی از سوییچ‌های زیر میباشد:

 نام پارامتر
شرح کارکرد
 c- یا collection--
 میتواند پشتیبانی گیری را به یک کالکشن خاص محدود کند.
 d- یا db--
از دیتابیسی مشخص استفاده کند.
 u- یا username-
نام کاربری سرور
 p- یا password--
 کلمه عبور سرور
 dbpath--
 مسیر پوشه‌ای را که دیتاها داخل آن است، دریافت میکند و بجای ایجاد یک Instance مستقیم پشتیبانی گیری را آغاز میکند.
توجه : در این حالت پوشه به طور کامل قفل خواهد شد و سرور نباید در حالت اجرا قرار گرفته باشد.
 DirectoryPerDb--
در صورتیکه هر دیتابیسی دارای محل جداگانه‌ای برای پشتیبان گیری باشد.
 o- یا out--
محل خروجی و ذخیره پشتیبان را مشخص میکند.
 q- یا query--
پشتیبان، در قالب کوئری‌های جی‌سون خواهد بود.
 repair-- اصلاح اسناد در صورت خراب شدن دیتابیس. در این حالت باید مکان ذخیره و نام دیتابیس، با پارامترهای بالا ذکر شود.

به عنوان مثال دستور زیر از دیتابیس publisher و کالکشن Books، پشتیبان تهیه میکند و در مسیر گفته شده آن را ذخیره میکند:

D:\Program Files\MongoDB\Server\3.4\bin>mongodump --db "publisher" --collection books --out "D:\mydumps"
2017-03-04T21:23:04.615+0330    writing publisher.books to
2017-03-04T21:23:04.637+0330    done dumping publisher.books (7 documents)
در این حالت اگر تنها دستور را بدون هیچ  پارامتری صادر کنید:
D:\Program Files\MongoDB\Server\3.4\bin>mongodump
نتیجه پشتیبان گیری از همه دیتابیس‌ها و همه قسمت‌ها به انضمام فایل‌ها در شاخه‌ای به اسم dump در پوشه Bin صورت میگیرد.

برای بازگردانی از دستوری به نام Mongorestore استفاده می‌شود که در شاخه Bin قرار گرفته است و تعدادی از پارامترهای آن به شرح زیر میباشد:

 پارامتر شرح کارکرد
 c- یا collection--
 میتواند پشتیبانی گیری را به یک کالکشن خاص محدود کند.
 d- یا db--
از دیتابیسی مشخصی استفاده کند.  
 u- یا username-
نام کاربری سرور
 p- یا password--
 کلمه عبور سرور 
 port--
شماره پورت سرور
 host-- هاست مونگو ، ترکیب hostname:port میتواند استفاده از سوییچ  port را بی نیاز کند.
 ipv6-- فعال سازی  IPV6
 dbpath--
 مسیر پوشه‌ای را که دیتاها داخل آن است، دریافت میکند و بجای ایجاد یک Instance مستقیم پشتیبانی گیری را آغاز میکند.
توجه : در این حالت پوشه به طور کامل قفل خواهد شد و سرور نباید در حالت اجرا قرار گرفته باشد. 
 DirectoryPerDb--
در صورتی که هر دیتابیسی دارای محل جداگانه‌ای برای پشتیبان گیری باشد.  
keepIndexVersion --
موقع بازگردانی، ایندکس‌ها را با نسخه جدید به روزرسانی نمی‌کند.

مثال زیر کل پشتیبان‌های مسیر مربوطه را بازگردانی میکند:
mongorestore "D:\mydumps"
یا دستور زیر تنها کالکشن خاصی را از یک دیتابیس خاص به روزرسانی میکند:
mongorestore "D:\mydumps\publisher\books.bson" --db publisher -c books
یا دستور زیر کل اطلاعاتی را که mongodb بدون پارامتر، پشتیبان گرفته است، از همان مسیر بازگردانی میکند:
mongorestore
نظرات مطالب
کار با Docker بر روی ویندوز - قسمت سوم - نصب Docker بر روی ویندوز سرور
- بله. از قسمت «دو نوع Windows Containers وجود دارند » انتهای مطلب را مطالعه کنید. پیشنیاز درک آن، مطالعه‌ی دو قسمت قبلی است + WSL2 هم این نوع مشکلات را حل خواهد کرد.
- این مسایل ربطی به فلسفه‌ی وجودی Docker ندارند. هدف از وجود Docker را از قسمت اول این سری شروع به مطالعه کنید.
نظرسنجی‌ها
آیا در تیم توسعه دات نت خود از تکنولوژی‌های اورکستراسیون مانند Kubernetes برای مدیریت و مقیاس‌پذیری Docker استفاده می‌کنید؟
تازه‌کار هستیم و هنوز از این تکنولوژی‌ها استفاده نمی‌کنیم.
به‌صورت محدود در برخی پروژه‌ها از Kubernetes یا تکنولوژی‌های مشابه استفاده می‌کنیم.
در برخی پروژه‌ها از این تکنولوژی‌ها برای مدیریت Docker استفاده می‌شود.
Kubernetes یا مشابه آن در پروژه‌های متعدد دات نت مورد استفاده قرار می‌گیرد.
در تمامی پروژه‌های دات نت ما از تکنولوژی‌های اورکستراسیون برای مقیاس‌پذیری و مدیریت Docker استفاده می‌شود.
مطالب
بسته بندی نرم افزار الکترون
بعد از اینکه برنامه الکترون آماده شد، لازم است آن را به فایل‌های اجرایی پلتفرم‌های مختلف تبدیل کنیم. برای اینکار بسته معروف Electron-packager را مورد استفاده قرار می‌دهیم. برای نصب آن به شکل زیر اقدام کنید:
npm install electron-packager --save-dev
بعد از اینکه نصب شد، در فایل package.json در قسمت scripts، خصوصیت جدیدتری را وارد می‌کنیم:
"build":"electron-packager . myapp --platform=all --arch=all --overwrite"
این دستور شامل حداقل‌های آرگومان‌ها می‌باشد. در تشریح این دستور باید گفت اولین آرگومان، نام دایرکتوری است که برنامه شما در آن نوشته شده است و حاوی فایل package.json است و با قرار دادن علامت "." دایرکتوری جاری را معرفی کرده‌ایم. بعد از آن نام برنامه و فایل اجرایی برنامه است. بعد از آن فلگ‌ها آغاز می‌شوند که اولین فلگ مشخص میکند خروجی خود را برای چه پلتفرمی نیاز دارید و شامل مقادیر زیر می‌شود؛ یا اینکه عبارت all را برای در نظر گرفتن همه مقادیر وارد کنید.
darwin
سیستم عامل مکینتاش
linux
 سیستم عامل لینوکس
win32
 سیستم عامل ویندوز

فلگ بعدی معماری سیستم عامل را نشان میدهد که برای سیستم‌های 32 بیتی مقدار ia32 و برای سیستم‌های 64 بیتی مقدار x64 می‌باشد. ولی در صورتیکه همه مقادیر را در نظر دارید، می‌توانید همانند خط بالا از مقدار all استفاده کنید.

در همه حالات بالا اگر فقط تعدادی از آن‌ها را بخواهید وارد کنید، می‌توانید هر عبارت را با , از هم جدا سازید؛ مانند darwin,linux که برای این دو پلتفرم تنها نسخه اجرایی تولید می‌شود.

فلگ آخر اجباری نیست، ولی برای دفعات بعدی بسیار مناسب است. اگر از قبل یک بسته بندی وجود دارد، بسته بندی جدید بر روی قبلی‌ها رونویسی خواهد شد.

حال با دستور زیر در nodejs، عملیات بسته بندی را آغاز می‌کنیم:

npm run build
بعد از اجرای این دستور برای اولین بار، ممکن است برای هر پلتفرم، کتابخانه‌های مربوطه را دانلود کند تا بر اساس آن، عمل بسته بندی را انجام دهد. سپس در دایرکتوری پروژه، دایرکتوری‌های جدیدی را با نام‌های مشخصی خواهید یافت.
یکی از دیگر فلگ‌ها که کاربردی می‌باشد، برای نادیده گرفتن ورورد یک سری پکیج‌ها به بسته نهایی است که به طور پیش فرض جلوی ورود بسته‌های eletron-prebuilt و electron-packager را میگیرد. ولی اگر دوست دارید تا بسته‌های دیگری را نیز به این لیست اضافه کنید، دستو زیر را به کار ببرید:
--ignore=node_modules/<package_name>
یا
--ignore=node_modules/electron_[0-9]*

فلگ‌های پر استفاده دیگر این بسته:
 aap-version  نسخه برنامه
 app-copyright  متنی برای قانون کپی رایت
 asar موقعی که برنامه‌ای را بسته بندی می‌کنید، در دایرکتوری Resources/App، هنوز سورس برنامه وجود دارد که فایل اجرایی شما بدون آن قادر به ادامه فعالیت نیست. ولی اگر بخواهیم این سورس را در اختیار شخصی قرار ندهیم، باید از ویژگی asar استفاده کنیم. با استفاده از این فلگ، فایلی با نام app.asar جای این دایرکتوری ایجاد خواهد شد و دیگر نیازی نیست تا سورس برنامه همراه آن باشد.
 icon در صورتیکه قصد استفاده از آیکنی بجز آیکون الکترون را دارید.
 out به طور پیش فرض برنامه نهایی در دایرکتوری کاری پروژه اضافه می‌شود. در صورتیکه قصد دارید آن‌را در دایرکتوری بجز دایرکتوری کاری قرار دهید، از این ویژگی استفاده کنید.
 version-string این خصوصیت برای نسخه بندی برنامه است که فقط برای ویندوز کاربرد دارد و شامل خصوصیاتی چون نام محصول، نام سازنده، توصیف برنامه و ... می‌باشد:
--version-string.ProductName="Product"

                   Properties supported:
                   - CompanyName
                   - FileDescription
                   - OriginalFilename
                   - ProductName
                   - InternalName

prune
استفاده از این فلگ باعث می‌شود کلیه بسته‌های معرفی شده در dev-dependency به بسته نهایی اضافه نشوند

دستور بسته بندی بالا را نیز می‌توان به طور خلاصه‌تر نیز نوشت :

electron-packager . --all
این دستور با در نظر گرفتن همه پلتفرم‌ها و معماری آن‌ها، برای دایرکتوری جاری اجرا شده و نام برنامه و دیگر اطلاعات را از طریق فایل package.json به دست می‌آورد.
مطالب
ساخت یک سایت ساده‌‌ی نمایش لیست فیلم با استفاده از Vue.js - قسمت دوم
در قسمت قبلی نحوه کانفیگ اولیه برنامه را به همراه نصب پلاگین‌های مورد نیاز، بررسی نمودیم؛ در ادامه قصد داریم تا چندین کامپوننت , ^ را برای نمایش لیست فیلمها، جزییات فیلم و جستجو، به برنامه اضافه کنیم و به هر کدام یک route را نیز انتساب دهیم. از کامپوننت‌ها برای بخش بندی قسمتهای مختلف سایت استفاده میکنیم. هر بخش برای دریافت و نمایش اطلاعاتی خاص مورد استفاده قرار میگیرد. بر خلاف Angular که به‌راحتی با دستور زیر میتوان برای آن یک کامپوننت ایجاد نمود و هر بخشی (css,js,ts,html) را در یک فایل جداگانه قرار داد:
ng generate component [name]
یا
ng g c [name]
در Vue.js هنوز امکان اینکه بتوان از طریق cli  یک کامپوننت را ایجاد کرد، فراهم نشده‌است. البته پکیج‌هایی برای اینکار تدارک دیده شده‌اند، ولی در این مقاله به‌صورت دستی اینکار انجام میشود و از Single File Component استفاده میکنیم. بصورت پیش فرض برنامه ایجاد شده vue.js دارای یک کامپوننت با نام HelloWorld.vue  در پوشه components  می‌باشد ( چیزی شبیه Hello Dolly در Wordpress)؛ آن را حذف میکنیم و محتویات فایل App.vue را مطابق زیر تغییر میدهیم ( قسمت import کردن کامپوننت HelloWorld.vue را حذف میکنیم) 
<template>
  <v-app>
    <v-toolbar app>
      <v-toolbar-title>
        <span>Vuetify</span>
        <span>MATERIAL DESIGN</span>
      </v-toolbar-title>
      <v-spacer></v-spacer>
      <v-btn
        flat
        href="https://github.com/vuetifyjs/vuetify/releases/latest"
        target="_blank"
      >
        <span>Latest Release</span>
      </v-btn>
    </v-toolbar>

    <v-content>
      <HelloWorld/>
    </v-content>
  </v-app>
</template>

<script>


export default {
  name: 'App',
  components: {
    
  },
  data () {
    return {
      //
    }
  }
}
</script>
 در پوشه components، سه کامپوننت را با نام‌های LatestMovie.vue ، Movie.vue و SearchMovie.vue ایجاد کنید.
محتویات LatestMovie.vue
<template>

  <v-container v-if="loading">
    <div>
      <v-progress-circular
        indeterminate
        :size="150"
        :width="8"
        color="green">
      </v-progress-circular>
    </div>
  </v-container>

  <v-container v-else grid-list-xl>
    <v-layout wrap>
      <v-flex xs4
        v-for="(item, index) in wholeResponse"
        :key="index"
        mb-2>
        <v-card>
          <v-img
            :src="item.Poster"
            aspect-ratio="1"
          ></v-img>

          <v-card-title primary-title>
            <div>
              <h2>{{item.Title}}</h2>
              <div>Year: {{item.Year}}</div>
              <div>Type: {{item.Type}}</div>
              <div>IMDB-id: {{item.imdbID}}</div>
            </div>
          </v-card-title>

          <v-card-actions>
            <v-btn flat
              color="green"
              @click="singleMovie(item.imdbID)"
              >View</v-btn>
          </v-card-actions>

        </v-card>
      </v-flex>
  </v-layout>
  </v-container>
</template>

<script>
import movieApi from '@/services/MovieApi'

export default {
  data () {
    return {
      wholeResponse: [],
      loading: true
    }
  },
  mounted () {
    movieApi.fetchMovieCollection('indiana')
      .then(response => {
        this.wholeResponse = response.Search
        this.loading = false
      })
      .catch(error => {
        console.log(error)
      })
  },
  methods: {
    singleMovie (id) {
      this.$router.push('/movie/' + id)
    }
  }
}
</script>

<style scoped>
  .v-progress-circular
    margin: 1rem
</style>

محتویات Movie.vue
<template>

  <v-container v-if="loading">
    <div>
        <v-progress-circular
          indeterminate
          :size="150"
          :width="8"
          color="green">
        </v-progress-circular>
      </div>
  </v-container>

  <v-container v-else>
    <v-layout wrap>
      <v-flex xs12 mr-1 ml-1>
        <v-card>
          <v-img
            :src="singleMovie.Poster"
            aspect-ratio="2"
          ></v-img>
          <v-card-title primary-title>
            <div>
              <h2>{{singleMovie.Title}}-{{singleMovie.Year}}</h2>
              <p>{{ singleMovie.Plot}} </p>
              <h3>Actors:</h3>{{singleMovie.Actors}}
               <h4>Awards:</h4> {{singleMovie.Awards}}
               <p>Genre: {{singleMovie.Genre}}</p>
            </div>
          </v-card-title>
          <v-card-actions>
            <v-btn flat color="green" @click="back">back</v-btn>
          </v-card-actions>
        </v-card>
      </v-flex>
    </v-layout>

    <v-layout row wrap>
      <v-flex xs12>
        <div>
        <v-dialog
          v-model="dialog"
          width="500">
          <v-btn
            slot="activator"
            color="green"
            dark>
            View Ratings
          </v-btn>
          <v-card>
            <v-card-title
             
              primary-title
            >
              Ratings
            </v-card-title>
            <v-card-text>
              <table style="width:100%" border="1" >
                <tr>
                  <th>Source</th>
                  <th>Ratings</th>
                </tr>
                <tr v-for="(rating,index) in this.ratings" :key="index">
                  <td align="center">{{ratings[index].Source}}</td>
                  <td align="center"><v-rating :half-increments="true" :value="ratings[index].Value"></v-rating></td>
                </tr>
              </table>
            </v-card-text>
            <v-divider></v-divider>
            <v-card-actions>
              <v-spacer></v-spacer>
              <v-btn
                color="primary"
                flat
                @click="dialog = false"
              >
                OK
              </v-btn>
            </v-card-actions>
          </v-card>
        </v-dialog>
      </div>
      </v-flex>
    </v-layout>
  </v-container>
</template>

<script>
import movieApi from '@/services/MovieApi'
export default {
  props: ['id'],

  data () {
    return {
      singleMovie: '',
      dialog: false,
      loading: true,
      ratings: ''
    }
  },

  mounted () {
    movieApi.fetchSingleMovie(this.id)
      .then(response => {
        this.singleMovie = response
        this.ratings = this.singleMovie.Ratings
        this.ratings.forEach(function (element) {
          element.Value = parseFloat(element.Value.split(/\/|%/)[0])
          element.Value = element.Value <= 10 ? element.Value / 2 : element.Value / 20
        }
        )
        this.loading = false
      })
      .catch(error => {
        console.log(error)
      })
  },
  methods: {
    back () {
      this.$router.push('/')
    }
  }
}

</script>

<style scoped>
  .v-progress-circular
    margin: 1rem
</style>

محتویات SearchMovie.vue 
<template>

  <v-container v-if="loading">
    <div>
      <v-progress-circular
        indeterminate
        :size="150"
        :width="8"
        color="green">
      </v-progress-circular>
    </div>
  </v-container>

  <v-container v-else-if="noData">
    <div>
    <h2>No Movie in API with {{this.name}}</h2>
    </div>
  </v-container>

  <v-container v-else grid-list-xl>
    <v-layout wrap>
      <v-flex xs4
        v-for="(item, index) in movieResponse"
        :key="index"
        mb-2>
        <v-card>
          <v-img
            :src="item.Poster"
            aspect-ratio="1"
          ></v-img>

          <v-card-title primary-title>
            <div>
              <h2>{{item.Title}}</h2>
              <div>Year: {{item.Year}}</div>
              <div>Type: {{item.Type}}</div>
              <div>IMDB-id: {{item.imdbID}}</div>
            </div>
          </v-card-title>

          <v-card-actions>
            <v-btn flat
              color="green"
              @click="singleMovie(item.imdbID)"
              >View</v-btn>
          </v-card-actions>

        </v-card>
      </v-flex>
  </v-layout>
  </v-container>
</template>

<script>
// در همه کامپوننتها جهت واکشی اطلاعات ایمپورت میشود
import movieApi from '@/services/MovieApi'

export default {
  // route پارامتر مورد استفاده در 
  props: ['name'],
  data () {
    return {
      // آرایه ای برای دریافت فیلمها
      movieResponse: [],
      // جهت نمایش لودینگ در زمان بارگذاری اطلاعات
      loading: true,
      // مشخص کردن آیا اطللاعاتی با سرچ انجام شده پیدا شده یا خیر
      noData: false
    }
  },
  // تعریف متدهایی که در برنامه استفاده میکنیم
  methods: {
    // این تابع باعث میشود که 
    // route
    // تعریف شده با نام
    // Movie
    // فراخوانی شود و آدرس بار هم تغییر میکنید به آدرسی شبیه زیر
    // my-site/movie/tt4715356 
    singleMovie (id) 
      this.$router.push('/movie/' + id)
    },

    fetchResult (value) {
      movieApi.fetchMovieCollection(value)
        .then(response => {
          if (response.Response === 'True') {
            this.movieResponse = response.Search
            this.loading = false
            this.noData = false
          } else {
            this.noData = true
            this.loading = false
          }
        })
        .catch(error => {
          console.log(error)
        })
    }
  },
  // جز توابع
  // life cycle
  // vue.js
  // میباشد و زمانی که تمپلیت رندر شد اجرا میشود
  // همچنین با هر بار تغییر در 
  // virtual dom
  // این تابع اجرا میشود
  mounted () {
    this.fetchResult(this.name)
  },
  // watch‌ها // کار ردیابی تغییرات را انجام میدهند و به محض تغییر مقدار  پراپرتی 
  // name
  // کد مورد نظر در بلاک زیر انجام میشود
  watch: {
    name (value) {
      this.fetchResult(value)
    }
  }
}
</script>

<style scoped>
  .v-progress-circular
    margin: 1rem
</style>

توضیحی درباره کدهای بالا
برای درخواستهای ا‌‌‌‌‌‌‌‌ی‌جکس از axios استفاده میکنیم و با توجه به اینکه در این برنامه سه کامپوننت داریم، باید در هر کامپوننت axios را import کنیم:
import axios from 'axios'
لذا (DRY) یک فولدر را بنام service در پوشه src  ایجاد میکنیم. یک فایل جاوااسکریپتی را نیز با نام دلخواهی در آن ایجاد و فقط یکبار axios را در آن  import میکنیم و توابع مورد نیاز را در آنجا مینویسیم (هر چند راه‌های بهتر دیگری هم برای کار با axios هست که در حیطه مقاله جاری نیست).

محتویات فایل MovieApi.js در پوشه service
import axios from 'axios'

export default {

  fetchMovieCollection (name) {
    return axios.get('&s=' + name)
      .then(response => {
        return response.data
      })
  },

  fetchSingleMovie (id) {
    return axios.get('&i=' + id)
      .then(response => {
        return response.data
      })
  }
}
فایل main.js برنامه را بشکل زیر تغییر میدهیم و با استفاده از تنظیماتی که برای axios وجود دارد، آدرس baseURL آن را به ازای نمونه وهله سازی شده‌ی vue برنامه، تنظیم میکنیم.
axios.defaults.baseURL = 'http://www.omdbapi.com/?apikey=b76b385c&page=1&type=movie&Content-Type=application/json'

فایل  index.js درون پوشه router را باز میکنیم و محتویات آن را بشکل زیر تغییر می‌دهیم:
import Vue from 'vue'
import VueRouter from 'vue-router'
// برای رجیستر کردن کامپوننت‌ها در بخش روتر، آنها را ایمپورت میکنیم
import LatestMovie from '@/components/LatestMovie'
import Movie from '@/components/Movie'
import SearchMovie from '@/components/SearchMovie'

Vue.use(VueRouter)

export default new VueRouter({
  routes: [
    {
      // مسیری هست که برای این کامپوننت در نظر گرفته شده(صفحه اصلی)بدون پارامتر
      path: '/',
      // نام روت
      name: 'LatestMovie',
      // نام کامپوننت مورد نظر
      component: LatestMovie
    },
    {
      // پارامتری هست که به این کامپوننت ارسال میشه id
      // برای دستیابی به این کامپوننت نیاز هست با آدرسی شبیه زیر اقدام کرد
      // my-site/movie/tt4715356
      path: '/movie/:id',
      name: 'Movie',
      // در کامپوننت جاری یک پراپرتی وجود دارد 
      //id که میتوان با نام 
      // به آن دسترسی پیدا کرد
      props: true,
      component: Movie
    },
    {
      path: '/search/:name',
      name: 'SearchMovie',
      props: true,
      component: SearchMovie
    }
  ],
  // achieve URL navigation without a page reload
  // When using history mode, the URL will look "normal," e.g. http://oursite.com/user/id. Beautiful!
  // در آدرس # قرار نمیگیرد
  mode: 'history'
})

در برنامه ما سه کامپوننت وجود دارد. ما برای هر کدام یک مسیر و نام را برای route آنها تعریف میکنیم، تا بتوانیم با آدرس مستقیم، آنها را فراخوانی کنیم و با دکمه‌های back و forward مرورگر کار کنیم.


نکته:  برای اجرای برنامه و دریافت پکیج‌های مورد استفاده در مثال جاری، نیاز است دستور زیر را اجرا کنید: 
npm install