فکر کنم یک نگاهی هم به نرم افزار MGTEK Help Producer کنید بد نباشد.
از قابلیت این نرم افزار این است که به Word اضافه می شود فارسی را پشتیبانی می کند و نیز انواع فرمت خروجی دارد از جمله Chm
Declare @MyDate JalaliDate = '1392/02/11 21:38:24' Select @MyDate.ToString() as MyDateTime , @MyDate.GetDate() as MyDate , @MyDate.GetTime() as MyTime , @MyDate.Year as MyYear , @MyDate.Month as MyMonth , @MyDate.Day as MyDay , @MyDate.Hour as MyHour , @MyDate.Minute as MyMinute , @MyDate.Second as MySecond , @MyDate.JalaliDateAdd('Year',1) as NextYear , @MyDate.JalaliDateAdd('Month',1) as NextMonth , @MyDate.JalaliDateAdd('Day',1) as NextDay , @MyDate.JalaliDateAdd('Hour',1) as NextHour , @MyDate.JalaliDateAdd('Minute',1) as NextMinute , @MyDate.JalaliDateAdd('Second',1) as NextSecond
با این همه، همانسان که در پرسوجوی بالا هم میبینید؛ ناگزیر شدم تاریخ و زمان را خودم وارد کنم و هرآینه در واقعیت شما جهت پرسوجو روی زمان کنونی، ناگزیر به استفاده از یک تابع برای تبدیل تقویم میلادی به خورشیدی هستیم. به نظر شما بهتر نیست دست به کار شویم و تابعی برای تبدیل تاریخ میلادی به خورشیدی بنویسیم؟
برای اینکار پروژهای را که در دو درس 1 ساختیم باز کنید و سپس روی نام پروژه در Solution Explorer راستکلیک کرده و Add New Item را انتخاب کنید.
using System; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using Microsoft.SqlServer.Server; public partial class UserDefinedFunctions { [Microsoft.SqlServer.Server.SqlFunction] public static JalaliDate GetCurrentDateTime() { System.Globalization.PersianCalendar pers = new System.Globalization.PersianCalendar(); DateTime CurrentDate = DateTime.Now; JalaliDate jl; jl.Year = (Int16)pers.GetYear(CurrentDate); jl.Month = (byte)pers.GetMonth(CurrentDate); jl.Day = (byte)pers.GetDayOfMonth(CurrentDate); jl.Hour = (byte)pers.GetHour(CurrentDate); jl.Minute = (byte)pers.GetMinute(CurrentDate); jl.Second = (byte)pers.GetSecond(CurrentDate); return jl; } }
Declare @MyDate JalaliDate = dbo.GetCurrentDateTime()
Select dbo.GetCurrentDateTime().ToString()
foreach (string item in HttpContext.Current.Request.Headers) { if (string.IsNullOrWhiteSpace(item)) continue; if (item == "HTTP_X_NEWRELIC_TRANSACTION" || item == "HTTP_X_NEWRELIC_ID") { HttpContext.Current.Response.StatusCode = 403; //Forbidden HttpContext.Current.Response.End(); } }
dotnet new blazor --interactivity None
@page "/ssr-page/{Data:int}" <PageTitle>An SSR component</PageTitle> <h1>An SSR component rendered by a Minimal-API!</h1> <div> Data: @Data </div> @code { [Parameter] public int Data { get; set; } }
var builder = WebApplication.CreateBuilder(args); // ... builder.Services.AddRazorComponents(); // ... // http://localhost:5227/ssr-component?data=2 // or it can be called directly http://localhost:5227/ssr-page/2 app.MapGet("/ssr-component", (int data = 1) => { var parameters = new Dictionary<string, object?> { { nameof(SsrTest.Data), data }, }; return new RazorComponentResult<SsrTest>(parameters); }); app.UseStaticFiles(); app.UseAntiforgery(); app.MapRazorComponents<App>(); app.Run(); // ...
<LayoutView Layout="@typeof(MainLayout)"> <PageTitle>Home</PageTitle> <h2>Welcome to your new app.</h2> </LayoutView>
// ... builder.Services.AddRazorComponents() .AddInteractiveServerComponents(); // ... app.MapRazorComponents<App>().AddInteractiveServerRenderMode(); // ...
<script src="_framework/blazor.web.js"></script>
@rendermode InteractiveServer <h1>Counter</h1> <p role="status">Current count: @_currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { private int _currentCount; private void IncrementCount() { _currentCount++; } }
// http://localhost:5227/server-interactive-component app.MapGet("/server-interactive-component", () => new RazorComponentResult<Counter>());
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Interactive server component</title> <base href="/"/> </head> <body> <h1>Interactive server component</h1> <Counter/> <script src="_framework/blazor.web.js"></script> </body> </html>
// http://localhost:5227/server-interactive-component app.MapGet("/server-interactive-component", () => new RazorComponentResult<CounterInteractive>());
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; namespace WebApi8x.Services; public class BlazorStaticRendererService { private readonly HtmlRenderer _htmlRenderer; public BlazorStaticRendererService(HtmlRenderer htmlRenderer) => _htmlRenderer = htmlRenderer; public Task<string> StaticRenderComponentAsync<T>() where T : IComponent => RenderComponentAsync<T>(ParameterView.Empty); public Task<string> StaticRenderComponentAsync<T>(Dictionary<string, object?> dictionary) where T : IComponent => RenderComponentAsync<T>(ParameterView.FromDictionary(dictionary)); private Task<string> RenderComponentAsync<T>(ParameterView parameters) where T : IComponent => _htmlRenderer.Dispatcher.InvokeAsync(async () => { var output = await _htmlRenderer.RenderComponentAsync<T>(parameters); return output.ToHtmlString(); }); }
builder.Services.AddScoped<HtmlRenderer>(); builder.Services.AddScoped<BlazorStaticRendererService>();
app.MapGet("/static-renderer-service-test", async (BlazorStaticRendererService renderer, int data = 1) => { var parameters = new Dictionary<string, object?> { { nameof(SsrTest.Data), data }, }; var html = await renderer.StaticRenderComponentAsync<SsrTest>(parameters); return Results.Content(html, "text/html"); });
طراحی فایل MRT
فایل MRT در واقع یک قالب (Template) خالی از مقادیر متغیر است که در StimulSoft Studio به طراحی آن میپردازیم و در برنامه خود، این مقادیر متغیر را با اطلاعات دلخواه خود جایگزین میکنیم. تصویر زیر یک نمونه از یک گزارش خالی است که ابتدا آن را طراحی کرده و سپس در برنامه آن را مورد استفاده قرار میدهیم:
برای اینکه فایل MRT بتواند دیتاهای لازمی را که به آن پاس میدهیم، بخواند و در جای مشخص شده قرار بدهد، باید یک BussinessObject برای آن ایجاد کنیم. بعد از اینکه یک گزارش جدید ایجاد کردید، در سمت راست به قسمت Dictionary بروید و در قسمت BussinessObject گزینه NewBussinessObject را انتخاب کنید. یک نام و نام مستعار که عموما هم یکی است، برای آن انتخاب کنید. در زیر همان پنجره شما میتوانید ستونهای اطلاعاتی خود را تعریف کنید. در اینجا من میخواهم اطلاعات یک راننده را به همراه خودروی وی، نشان دهم. برای همین، من دو موجودیت راننده و خودروی راننده را دارم. پس اسم Business Object را DriverReport میگذارم و ستونهای اطلاعاتی فقط راننده (بدون در نظر گرفتن خودروی وی) را وارد میکنم.
در همین کادر بالا شما میتوانید تصیم بگیرید که آیا میخواهید اطلاعات خودرو را به همراه دیگرستونهای اطلاعاتی راننده، ایجاد کنید یا اینکه برای خودرو یک نوع مجزا انتخاب کنید. اگر تنها یک خودرو برای راننده باشد، شاید راحتتر باشید همانند اطلاعات راننده با آن رفتار کنید. ولی اگر مثلا بخواهید خودرویهای گذشته راننده را هم جز لیست داشته باشید، بهتر است یک Business Object جدید متعلق و زیر مجموعه Business Object راننده ایجاد کنید. در اینجا چون تنها یک خودرو است، من آن اطلاعات آن را به همراه راننده، ارسال میکنم. شکل زیر ساختار درختی از گزارش بالاست:
شکل زیر هم یک ساختار دیگر از یک گزارش است که شامل Business objectهای مختلف میشود:
سپس همین فیلدها را به سمت صفحه خالی بکشانید. با دو بار کلیک روی فیلدهای قرار گرفته در صفحه، با نحوه بایند کردن مقادیر آشنا میشوید؛ هر فیلدی که قرار است دیتای آن بایند شود، باید به شکل زیر در بخش Expression پنجره باز شده، نوشته شود:
{driverReport.LastName}
راه اندازی گزارش ساز در ASP.Net MVC
اولین کاری که میکنیم، ورود سه dll اصلی به پروژه است:
Stimulate.Base
Stimulate.Report
Stimulate.Report.MVC
در مرحله بعد یک متد ساخته و یک ویوو را برای صفحه گزارش گیری ایجاد میکنیم:
public ActionResult Report(int id) { return View(); }
@Html.Stimulsoft().StiMvcViewer(new StiMvcViewerOptions() { Localization = "~/content/reports/fa.xml", Actions = { GetReportSnapshot = "LoadReportSnapshot", ViewerEvent = "ViewerEvent", ExportReport = "ExportReport", PrintReport = "PrintReport", } }
@Html.Stimulsoft().StiMvcViewer(new StiMvcViewerOptions() { Localization = "~/content/reports/fa.xml", ActionGetReportSnapshot = "LoadReportSnapshot", ActionViewerEvent = "ViewerEvent", ActionExportReport = "ExportReport", ActionPrintReport = "PrintReport", }
var driver = new { FirstName = "علی", LastName = "یگانه مقدم", NationalCode = "12500000000", FatherName = "حسین", Model = "نام خودرو", MotorNumber = 415244, ProductionYear = 1394, Capacity = 4 };
var driver = new { FirstName = "علی", LastName = "یگانه مقدم", NationalCode = "12500000000", FatherName = "حسین", car = new { Model = "نام خودرو", MotorNumber = 415244, ProductionYear = 1394, Capacity = 4 } };
var report = new StiReport(); report.Load(Server.MapPath("~/Content/Reports/driver.mrt")); report.RegBusinessObject("driverReport", driver); report.Dictionary.Variables.Add("today", DateTime.Today.ToPersianString(PersianDateTimeFormat.Date));
var report = new StiReport(); report.RegBusinessObject("driverReport", driver); report.Dictionary.Variables.Add("today", DateTime.Today.ToPersianString(PersianDateTimeFormat.Date)); report.Load(Server.MapPath("~/Content/Reports/driver.mrt"));
public ActionResult LoadReportSnapshot() { var driver = new { FirstName = "علی", LastName = "یگانه مقدم", NationalCode = "12500000000", FatherName = "حسین", Model = "نام خودرو", MotorNumber = 415244, ProductionYear = 1394, Capacity = 4 }; var report = new StiReport(); report.Load(Server.MapPath("~/Content/Reports/driver.mrt")); report.RegBusinessObject("driverReport", driver); report.Dictionary.Variables.Add("today", DateTime.Today.ToPersianString(PersianDateTimeFormat.Date)); return StiMvcViewer.GetReportSnapshotResult(HttpContext, report); }
public virtual ActionResult ViewerEvent() { return StiMvcViewer.ViewerEventResult(); }
public virtual ActionResult PrintReport() { return StiMvcViewer.PrintReportResult(this.HttpContext); }
public virtual ActionResult ExportReport() { return StiMvcViewer.ExportReportResult(this.HttpContext); }
.stiMvcViewerReportPanel table{ direction:ltr !important; }
حال حتما پیش خود میگویید که این روش برای اطلاعات ایستا و تمرینی مناسب است و من چگونه باید پارامترهای ارسالی به اکشن متد Report را به اکشن متد LoadReportSnapshot ارسال کنم. برای این منظور استفاده از SessionStateها زیاد توصیه شدهاست:
public virtual ActionResult Report(int id) { TempData["id"]=id; return View(); } public virtual ActionResult LoadReportSnapshot() { var driverId = (int)TempData ["id"]; //..... }
public virtual ActionResult Report(int id) { return View(); } public virtual ActionResult LoadReportSnapshot(int id) { //..... }
Server = { GlobalReportCache = false }
statusBarColor textColorPrimary colorAccent ColorPrimary WindowBackground
[Activity(Theme = "@style/NameThem")]
[Activity(Theme = "@style/NameThem")]
<?xml version="1.0" encoding="utf-8" ?> <resources> <style name="MainTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="windowNoTitle">true</item> <item name="windowActionBar">false</item> </style> </resources>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<?xml version="1.0" encoding="utf-8" ?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <group android:checkableBehavior="single"> <item android:id="@+id/menuItemHome" android:title="صفحه اصلی"></item> <item android:id="@+id/menuItemInsertProduct" android:title="ورود کالا جدید" ></item> <item android:id="@+id/menuItemListProduct" android:title="مشاهده کالاها"></item> <item android:id="@+id/menuItemExit" android:title="خروج"></item> </group> </menu>
<android.support.v7.widget.Toolbar android:layout_width="match_parent" android:id="@+id/toolbar1" android:background="#33B86C" android:minHeight="?android:attr/actionBarSize" android:layout_height="wrap_content"> </android.support.v7.widget.Toolbar>
<?xml version="1.0" encoding="utf-8" ?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <group android:checkableBehavior="single"> <item android:id="@+id/menuItemHome" android:title="صفحه اصلی"></item> <item android:id="@+id/menuItemInsertProduct" android:title="ورود کالا جدید" ></item> <item android:id="@+id/menuItemListProduct" android:title="مشاهده کالاها"></item> <item android:id="@+id/menuItemExit" android:title="خروج"></item> </group> </menu>
xmlns:app="http://schemas.android.com/apk/res-auto"
SupportActionBar.SetDisplayShowTitleEnabled(false);
var navigationview = this.FindViewById<NavigationView>(Resource.Id.navigationView1); navigationview.NavigationItemSelected += Navigationview_NavigationItemSelected; private void Navigationview_NavigationItemSelected(object sender, NavigationView.NavigationItemSelectedEventArgs e) { Intent intent = null; switch (e.MenuItem.ItemId) { case Resource.Id.menuItemHome: break; case Resource.Id.menuItemExit: Finish(); break; case Resource.Id.menuItemInsertProduct: break; case Resource.Id.menuItemListProduct: break; } }
<?xml version="1.0" encoding="utf-8" ?> <merge xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/toolbarlayout"> <android.support.v7.widget.Toolbar android:layout_width="match_parent" android:id="@+id/toolbar1" android:background="#33B86C" android:minHeight="?android:attr/actionBarSize" android:layout_height="wrap_content"> <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/imageButton1" android:background="@drawable/mainmenu" android:layout_gravity="end" /> </android.support.v7.widget.Toolbar> <android.support.v4.widget.DrawerLayout android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/drawerLayout1" android:fitsSystemWindows="true"> <android.support.design.widget.NavigationView android:minWidth="25px" android:minHeight="25px" android:layout_width="200dp" android:layout_height="match_parent" android:layout_gravity="end" app:menu="@menu/menu" android:id="@+id/navigationView1" android:fitsSystemWindows="true" /> </android.support.v4.widget.DrawerLayout> </merge>
private void Navigationview_NavigationItemSelected(object sender, NavigationView.NavigationItemSelectedEventArgs e) { Intent intent = null; switch (e.MenuItem.ItemId) { case Resource.Id.menuItemHome: intent = new Intent(this, typeof(MainActivity)); break; case Resource.Id.menuItemExit: Finish(); break; case Resource.Id.menuItemInsertProduct: intent = new Intent(this, typeof(InsertActivity)); break; case Resource.Id.menuItemListProduct: intent = new Intent(this, typeof(ListProductsActivity)); break; } if (intent != null) { } }
public class BaseAcitivity : AppCompatActivity { protected void InitieToolbar() { var toolbar = this.FindViewById<widgetV7.Toolbar>(Resource.Id.toolbar1); this.SetSupportActionBar(toolbar); //SupportActionBar.SetDisplayShowTitleEnabled(false); var imagebutton = toolbar.FindViewById<ImageButton>(Resource.Id.imageButton1); imagebutton.Click += Imagebutton_Click; var navigationview = this.FindViewById<NavigationView>(Resource.Id.navigationView1); navigationview.NavigationItemSelected += Navigationview_NavigationItemSelected; } private void Navigationview_NavigationItemSelected(object sender, NavigationView.NavigationItemSelectedEventArgs e) { Intent intent = null; switch (e.MenuItem.ItemId) { case Resource.Id.menuItemHome: intent = new Intent(this, typeof(MainActivity)); break; case Resource.Id.menuItemExit: Finish(); break; case Resource.Id.menuItemInsertProduct: intent = new Intent(this, typeof(InsertActivity)); break; case Resource.Id.menuItemListProduct: intent = new Intent(this, typeof(ListProductsActivity)); break; } if (intent != null) StartActivity(intent); } private void Imagebutton_Click(object sender, EventArgs e) { var drawerlayout = this.FindViewById<DrawerLayout>(Resource.Id.drawerLayout1); if (drawerlayout.IsDrawerOpen(Android.Support.V4.View.GravityCompat.End) == false) { drawerlayout.OpenDrawer(Android.Support.V4.View.GravityCompat.End); } else { drawerlayout.CloseDrawer(Android.Support.V4.View.GravityCompat.End); } } }
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
public class Fragment1 : Android.Support.V4.App.Fragment { public override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); } public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.Inflate(Resource.Layout.FragmentLayout1, container, false); } }
var tablayout = FindViewById<Android.Support.Design.Widget.TabLayout>(Resource.Id.tabLayout1); var viewpager = FindViewById<ViewPager>(Resource.Id.viewPager1); tablayout.SetupWithViewPager(viewpager);
class TabFragmentAdapter : FragmentPagerAdapter { public TabFragmentAdapter(FragmentManager fm) : base(fm) { } public override int Count => 3; public override Fragment GetItem(int position) { switch (position) { case 0: return new Fragment1(); case 1: return new Fragment2(); case 2: return new Fragment3(); default: return new Fragment1(); } } //int f1() { return 100; } //int f1 => 100; }
viewpager.Adapter = new TabFragmentAdapter(this.SupportFragmentManager);
tablayout.GetTabAt(0).SetIcon(Resource.Drawable.iconCall);
View iconlayout1 = LayoutInflater.Inflate(Resource.Layout.custom_TabIconLayout, null); var txt = iconlayout1.FindViewById<TextView>(Resource.Id.tabTextIcon); txt.Text = "تماس"; txt.SetCompoundDrawablesWithIntrinsicBounds(Resource.Drawable.iconCall, 0, 0, 0); tablayout.GetTabAt(0).SetCustomView(iconlayout1);
در این آموزش هدف ما ایجاد برنامهای بر اساس TodoMVC است که میتواند خودش را با یک دیتابیس آنلاین همگام سازی کند.
مطمئن باشید بیشتر از 10 دقیقه وقت شمارا نخواهد گرفت !
فایل index.html را باز کنید و فایلهای PouchDB را به آن اضافه کنید :
<script src="//cdn.jsdelivr.net/pouchdb/2.2.0/pouchdb.min.js"></script> <script src="js/base.js"></script> <script src="js/app.js"></script>
حالا PouchDB نصب شده و آماده به کار است . ( در نسخه نهایی بهتر است از فایلهای local استفاده کنید )
بقیه کارها باید در فایل app.js انجام شود . برای شروع باید بانک اطلاعاتی جدیدی را برای درج اطلاعات خودمان ایجاد کنیم . برای ایجاد بانک اطلاعاتی خیلی ساده یک instance جدید از آبجکت PouchDB میسازیم .
var db = new PouchDB('todos'); var remoteCouch = false;
نیازی نیست که برای بانک خود، نما (Schema) ایجاد کنید! بعد از اینکه اسم را مشخص کنید، بانک آماده به کار است.
اولین کاری که باید انجام دهیم این است که یک متد را ایجاد کنیم و توسط آن اطلاعات خودمان را در بانک اطلاعاتی ذخیره کنیم. نام متد را addTodo انتخاب میکنیم و کارش این است که وقتی کاربر کلید Enter را فشار داد، اطلاعات را داخل بانک اطلاعاتی ذخیره کند. متد مورد نظر به این صورت هست:
function addTodo(text) { var todo = { _id: new Date().toISOString(), title: text, completed: false }; db.put(todo, function callback(err, result) { if (!err) { console.log('Successfully posted a todo!'); } }); }
در PouchDB هر پرونده نیاز دارد تا یک فیلد unique با نام _id داشته باشد. اگر دادهای بخواهد در بانک اطلاعاتی ثبت شود و دارای _id مشابه باشد، با آن به صورت یک update رفتار خواهد شد. در اینجا ما از تاریخ با عنوان id استفاده کردیم که در این مورد خاص میباشد. شما میتواند از db.post() نیز استفاده کنید؛ اگر یک id را به صورت random میخواهید. تنها چیزی که اجباری است در هنگام ساختن یک پرونده، همین _id است و بقیه موارد کاملا اختیاری هستند.
در اینجا ما از یک function کمکی به نام redrawTodosUI استفاده خواهیم کرد که وظیفهاش این است تا یک array را دریافت کرده و آن را هر طور که مشخص کردید نمایش دهد. البته آن را به سلیقه خودتان میتوانید آماده کنید.
تنها کاری که باید انجام دهیم این است که اطلاعات را از بانک اطلاعاتی استخراج کرده و به function مورد نظر پاس دهیم.
در اینجا میتوانیم به سادگی از db.allDocs برای خواندن اطلاعات از بانک اطلاعاتی استفاده کنیم.
خاصیت include_docs به PouchDB این دستور را میدهد که ما درخواست دریافت همه اطلاعات داخل پروندهها را داریم و descending هم نحوه مرتب سازی را که بر اساس _id هست، مشخص میکند تا بتوانیم جدیدترین اطلاعات را اول دریافت کنیم .
function showTodos() { db.allDocs({include_docs: true, descending: true}, function(err, doc) { redrawTodosUI(doc.rows); }); }
هر بار که ما اطلاعات جدیدی را در بانک اطلاعاتی وارد میکنیم، نیازمند این هستیم تا اطلاعات جدید را به صورت خودکار دریافت و نمایش دهیم. برای این منظور میتوانیم به روش زیر عمل کنیم :
var remoteCouch = false; db.info(function(err, info) { db.changes({ since: info.update_seq, live: true }).on('change', showTodos); });
حالا هر بار که اطلاعات جدیدی در بانک اطلاعات ثبت شود، به صورت خودکار نمایش داده خواهد شد. خاصیت live مشخص میکند که این کار میتواند بی نهایت بار انجام شود.
وقتی که کاربر یک آیتم Todo را کامل میکند، یک چک باکس را علامت میزند و اعلام میکند که این کار انجام شده است.
function checkboxChanged(todo, event) { todo.completed = event.target.checked; db.put(todo); }
این بخش شبیه به قسمت ثبت اطلاعات است، با این تفاوت که باید شامل یک فیلد _rev ( اضافه بر _id ) نیز باشد. در غیر اینصورت تغییرات ثبت نخواهد شد. این کار برای این است که اشتباهی، اطلاعاتی در بانک ثبت نشود.
برای حذف اطلاعات باید از متد db.remove به همراه آبجکت مورد نظر استفاده کنید .
function deleteButtonPressed(todo) { db.remove(todo); }
مانند بخش ویرایش نیز باید در اینجا هم _id و هم _rev مشخص شود.
باید توجه داشته باشید در اینجا همان آبجکتی را که از بانک فراخوانی کردهایم، به این متد پاس میدهیم.
البته شما میتونید آبجکت خودتان را نیز ایجاد کنید {_id: todo._id, _rev: todo._rev} اما خوب همان روش قبلی عاقلانهتر است و احتمال خطای کمتری دارد .
حالا میخواهیم همگام سازی را اجرا کنیم و برای این کار نیاز هست یا CouchDB را به صورت Local نصب کنیم یا از سرویسهای آنلاین مثل IrisCouch استفاده کنید .
برای اینکه به صورت مستقیم با CouchDB در ارتباط باشید باید مطمئن شوید که CORS فعال هست.
در اینجا فقط نام کاربری و رمز ورود را مشخص کنید. به صورت پیش فرض CouchDB به صورت Admin Party نصب میشود و نیازی به User و password ندارد. مگر اینکه برایش مشخص کنید.
همچنین شما باید myname.iriscouch.com را با سرور خودتان جایگزین کنید که اگر به صورت local کار میکنید 127.0.0.1:5984 است.
$ export HOST=http://username:password@myname.iriscouch.com $ curl -X PUT $HOST/_config/httpd/enable_cors -d '"true"' $ curl -X PUT $HOST/_config/cors/origins -d '"*"' $ curl -X PUT $HOST/_config/cors/credentials -d '"true"' $ curl -X PUT $HOST/_config/cors/methods -d '"GET, PUT, POST, HEAD, DELETE"' $ curl -X PUT $HOST/_config/cors/headers -d \ '"accept, authorization, content-type, origin"'
به فایل app.js برگردید . در اینجا باید آدرس بانک اطلاعاتی آنلاین را مشخص کنیم.
var db = new PouchDB('todos'); var remoteCouch = 'http://user:pass@mname.iriscouch.com/todos';
فراموش نکنید که نام کاربری و رمز ورود را بسته به نیاز خود تغییر دهید.
حالا میتونیم function که وظیفه همگام سازی اطلاعات را به عهده دارد بنویسیم :
function sync() { syncDom.setAttribute('data-sync-state', 'syncing'); var opts = {live: true}; db.replicate.to(remoteCouch, opts, syncError); db.replicate.from(remoteCouch, opts, syncError); }
db.replicate() به PouchDB میگوید که که همه اطلاعات را "به" یا "از" remoteCouch انتقال دهد.
ما دوبار این درخواست را دادیم. در بار اول اطلاعات به داخل سرور ریموت منتقل میشود و در بار دوم اطلاعات local جایگزین میشوند.
یک callback هم وقتی که این کار به پایان برسد اجرا خواهد شد .
میتوانید برنامه خود را در دو مرورگر مختلف اجرا کنید تا نتیجه کار را ببینید.
شما توانستید اولین برنامه خود را توسط PouchDB ایجاد کنید. البته این یک برنامه ساده بود و در دنیای واقعی نیاز هست تا خیلی کارهای بیشتری را انجام دهید. اما باز هم اصول اولیه کار را یاد گرفتید و ادامه راه را میتوانید تنهایی ادامه دهید .