مطالب
تولید اطلاعات تصادفی توسط GenFu
گاها برای تولید اطلاعات تصادفی، خصوصا هنگام نوشتن تست‌ها، زمان زیادی بیهوده تلف شده و حجم زیادی کد اضافه تولید میشود. کتابخانه‌ای بنام GenFu ایجاد شده که وظیفه ایجاد داده‌های تصادفی را بر عهده گرفته‌است. این کتابخانه متن باز (Open Source) بوده و می‌توانید آن را از مخزن گیت‌هاب دریافت نمایید.

در مطلب جاری قصد ایجاد اطلاعات تصادفی برای کلاس زیر را داریم :
public class Person
{
    public int ID { get; set; }

    public string Firstname { get; set; }

    public string Lastname { get; set; }

    public string Email { get; set; }

    public string PhoneNumber { get; set; }

    public override string ToString()
    {
        return $"{ID}: {Firstname} {Lastname} - {Email} - {PhoneNumber}";
    }
}

نصب GenFu

برای نصب کتابخانه GenFu از دستور زیر در Package Manager Console استفاده میکنیم :
Install-Package GenFu

1. ایجاد یک شخص

برای ایجاد شخصی جدید همراه با اطلاعاتی تصادفی به شکل زیر عمل خواهیم کرد :
var person = A.New<Person>();
Console.WriteLine(person);
نتیجه کد فوق به این صورت خواهد بود :
18: Diedra Morgan - Zachary.Garcia@telus.net - (531) 273-9001
اگر دقت کنید متوجه میشوید که GenFu بصورت خودکار داده‌هایی مرتبط با Property هایی که نام گذاری کردید‌، ایجاد کرده‌است.
برای Email، داده‌ای با فرمت صحیح ایمیل و برای PhoneNumber هم شماره تلفنی با فرمت صحیح تولید شده است.

2. ایجاد چند شخص

برای ایجاد لیستی از اشخاص نیز میتوانید از متد ListOf استفاده کرده و تعداد اشخاص مورد نیازتان را به آن ارسال کنید ( پیشفرض 25 ) :
var people = A.ListOf<Person>(5);
people.ForEach(Console.WriteLine);
کد فوق باعث ایجاد 5 شخص با اطلاعات تصادفی متفاوتی خواهد شد:
97: Maria MacKenzie - Alexandra.Johnson@rogers.ca - (670) 787-3053
34: Alexander Scott - Isaiah.Price@gmail.com - (730) 645-4946
66: Kevin Perez - Gabrielle.Alexander@hotmail.com - (230) 758-8233
81: Maria Evans - Vanessa.Bell@rogers.ca - (508) 572-4343
79: Tyler Parker - Alyssa.Taylor@telus.net - (297) 357-7617

تا به اینجای کار GenFu بخوبی جوابگوی نیازهایمان بوده‌است. اما اگر پیشفرض‌ها جوابگو نبود و بخواهیم فرمت داده‌های تولید شده را تغییر دهیم چطور ؟
برای اینکار میتوانیم از متد Configure استفاده کرده و نحوه ایجاد داده را برای Property هایی که مشخص میکنیم، تغییر دهیم.

3. ایجاد چند شخص و مقدارهی یک property با مقدار ثابت

اگر بخواهیم داده‌های ایجاد شده را داخل دیتابیس لحاظ کنیم، نیاز داریم تا ID آن‌ها را برابر 0 قرار دهیم تا دیتابیس مشکلی برای ثبتشان نداشته باشد. برای ایجاد لیستی از اشخاص که ID آن‌ها برابر 0 باشد :
A.Configure<Person>().Fill(x => x.ID, 0);

var people = A.ListOf<Person>(5);
people.ForEach(Console.WriteLine);
نتیجه :
0: Darron Gonzalez - Benjamin.Daeninck@hotmail.com - (405) 418-7783
0: Melanie Garcia - Jennifer.Griffin@microsoft.com - (711) 277-8826
0: James Hughes - Tristan.Ward@live.com - (734) 400-8322
0: Miranda Torres - Ross.Davis@rogers.ca - (495) 479-8147
0: David Hughes - Jillian.Alexander@live.com - (361) 617-6642
در این حالت بدون هیچگونه مشکلی میتوانید داده‌های ایجاد شده را داخل دیتابیس ذخیره نمایید.

4. ایجاد چند شخص و مقداردهی یک property با متد

حالت دیگری که میتوانید نحوه مقداردهی یک Property را تنظیم کنید، استفاده از متد یا delegate است:
var i = 1;

A.Configure<Person>()
    .Fill(c => c.ID, () => i++);

var people = A.ListOf<Person>(5);
people.ForEach(Console.WriteLine);

نتیجه :
1: Paul Long - Carlos.Kelly@telus.net - (202) 573-6278
2: Jesse Iginla - Liberty.Moore@gmail.com - (589) 791-3606
3: Raymundo Price - Ang.Taylor@live.com - (336) 400-1601
4: Elizabeth Getzlaff - Leslie.Campbell@att.com - (662) 582-9010
5: Abigail Bailey - Tristan.Ross@live.com - (225) 661-7023
همانطور که می‌بینید، ID اشخاص بصورت تصاعدی مقداردهی شده است.

5. ایجاد چند شخص و مقداردهی یک property با مقادیر property‌های دیگر

همچنین میتوانید از مقادیر Property‌های دیگر برای مقداردهی یک Property استفاده کنید :
A.Configure<Person>()
    .Fill(c => c.ID, 0)
    .Fill(c => c.Email,
        c => $"{c.Firstname}.{c.Lastname}@gmail.com");

var people = A.ListOf<Person>(5);
people.ForEach(Console.WriteLine);
کد فوق باعث تولید اشخاصی میشود که ایمیل آن‌ها برابر (Firstname).(Lastname) خواهد بود :
0: Patrick Perry - Patrick.Perry@gmail.com - (796) 460-6576
0: Rebecca Main - Rebecca.Main@gmail.com - (757) 472-3332
0: Kimberly Carter - Kimberly.Carter@gmail.com - (436) 484-8273
0: Sara Lewis - Sara.Lewis@gmail.com - (424) 717-7682
0: Lauren Ross - Lauren.Ross@gmail.com - (277) 294-5776

6. استفاده از Extension‌های درون ساخت GenFu برای مقداردهی

GenFu دارای Extension هایی بوده که باعث میشوند اطلاعات یک Property با مقادیر قابل درک و مشخصی پر شوند.
مثال :
A.Configure<Person>()
    .Fill(x => x.Firstname).AsPersonTitle();

var people = A.ListOf<Person>(5);
people.ForEach(Console.WriteLine);  
نتیجه :
64: Miss. Ratzlaff - Bryce.Simmons@att.com - (386) 309-2414
7: Air Marshall Yarobi - Ariana.Russell@att.com - (459) 238-0717
96: Air Marshall Taylor - Luke.Olsen@gmail.com - (775) 401-5281
28: Doctor Cox - Leah.Diaz@att.com - (569) 464-7961
99: Master Phillips - Chloe.Scott@hotmail.com - (578) 221-9021

7. GenFu WireFrame

در نهایت GenFu دارای پکیج جانبی به اسم Wireframes است که شامل HTML Helper هایی است که با استفاده از آن‌ها میتوانید المان‌های HTML مانند P, Image, Table و ... را با مقادیری برای تست بعنوان Placeholder ایجاد کنید. 
برای نصب و مطالعه بیشتر درباره GenFu WireFrames این لینک را ببینید.
مطالب
نحوه ایجاد Sequence و استفاده آن در Sql Server 2012
قبل از ایجاد Sequence در Sql Server 2012، توضیح مختصری را درباره آن می‌دهم.
در واقع Sequence روشی برای تولید اعداد ترتیبی با قابلیت افزایش یا کاهش عدد‌های دلخواه می‌باشد که توسط کاربر یا برنامه نویس ایجاد می‌شود. بنابراین Sequenceها User-Defined می‌باشند.
در اینجا ممکن است سئوالی پیش بیاید که اینکار توسط Identity هم قابل انجام است، اما چرا استفاده از Sequence توسط مایکروسافت پیشنهاد می‌شود.
بدلایل زیر استفاده از Sequence بهتر می‌باشد:
  1. ممکن است Application شما قبل از درج رکورد، درون یک جدول نیاز به عدد منحصربفردی داشته باشد.
  2. عدد تولید شده بوسیله Sequence را می‌توانید بین جداول یا ستونهای مختلف یک جدول به اشتراک بگذارید.
  3. می‌توانید روند تولید اعداد ترتیبی را Restart نمایید. به عبارت دیگر قابلیت Restart نمودن Sequence وجود دارد.
  4. می‌توانید Sequence خود را براساس Sort یک یا چند فیلد، تنظیم نمایید.

Syntax آن به شرح ذیل می‌باشد:
CREATE SEQUENCE [schema_name . ] sequence_name
    [ AS [ built_in_integer_type | user-defined_integer_type ] ]
    [ START WITH <constant> ]
    [ INCREMENT BY <constant> ]
    [ { MINVALUE [ <constant> ] } | { NO MINVALUE } ]
    [ { MAXVALUE [ <constant> ] } | { NO MAXVALUE } ]
    [ CYCLE | { NO CYCLE } ]
    [ { CACHE [ <constant> ] } | { NO CACHE } ]
    [ ; ]

شرح Syntax :
  • در زمان ایجاد Sequence، نوع آن می‌بایست عددی باشد، چنانچه ،Type آن را مشخص ننمایید، SQL Server، نوع آن را bigint در نظر می‌گیرد.
  • Start With: بدین مفهوم می‌باشد، که Sequence ایجاد شده از چه عددی آغاز شود.
  • INCREMENT BY: مفهومش این است که Sequence به چه مقداری افزایش یا کاهش یابد. به عبارت دیگری عدد تولید شده براساس مقدار Increment by تولید می‌شود.
  • Minvalue: کمترین مقداری که Sequence می‌تواند ایجاد نماید.
  • Maxvalue :بیشترین مقداری که Sequence می‌تواند ایجاد نماید.
  • Cycle :مقداری را که برای Cycle تعیین می‌نماییم، بدین مفهوم است که Sequence پس از چه عددی می‌بایست Restart شود.
  • Cache :عددی که برای Cache در نظر می‌گیریم، مفهومش این است که چه تعداد از اعداد تولید شده توسط Sequence، قبل از استفاده، می‌تواند در Cache قرار گیرد.

در ادامه با یک مثال ساده، یک Sequence ایجاد می‌نماییم:
CREATE SEQUENCE [dbo].[SequenceTest] 
 AS [int]
 START WITH 1
 INCREMENT BY 1
 MINVALUE 1
 MAXVALUE 30
 CYCLE 
 CACHE 
GO
در مثال بالا Start with برابر یک است، یعنی اولین عددی که تولید می‌شود، برابر یک است،INCREMENT BY برابر یک است یعنی در هر بار فراخوانی Sequence یک عدد به عدد تولید شده قبلی افزوده می‌شود. مقدار Minvalue برابر یک است، یعنی کمترین مقداری که Sequence می‌تواند تولید نماید برابر یک است. مقدار Maxvalue برابر 30 است، یعنی بیشترین مقداری که Sequence می‌تواند تولید نماید برابر 30 می‌باشد. و Cycle هم برای Sequence فوق در نظر گرفته شده است، بدین مفهوم ، که Sequence به مقدار 30 برسد، Restart شده و از مقدار یک شروع به تولید اعداد می‌نماید.
برای اینکه بتوانیم مقدار Sequence را بدست آوریم، کافیست از Syntax زیر استفاده نمایید:
NEXT VALUE FOR [ database_name . ] [ schema_name . ]  sequence_name
   [ OVER (<over_order_by_clause>) ]
به عنوان مثال داریم:


اگر Select بالا را تا 30 بار انجام دهید، برای دفعه 31 مقدار آن یک می‌شود، چون در زمان تعریف Cycle ،Sequence  را انتخاب کرده بودیم. در غیر اینصورت برای دفعه 31 با خطا زیر مواجه می‌شوید.
Msg 11728, Level 16, State 1, Line 1
The sequence object 'SequenceTest' has reached its minimum or maximum value. Restart the sequence object to allow new values to be generated.
 یکی از امکانات جالب Sequence  این است که شما می‌توانید Sequence را روی یک فیلد Sort شده تنظیم نمایید، برای روش شدن مطلب به مثال زیر توجه نمایید:
در ابتدا مطابق Script زیر جدولی را ایجاد و مقادیری را درون آن درج می‌نماییم:
create table Kids
( ID int,
Name varchar(50)
);
Go
insert Kids
 values
  (1,'Emma')
, (1,'Tabitha')
, (2,'Kendall')
, (3,'Delaney')
, (4,'Kyle')
, (5,'Jessica')
, (6,'Josh')
, (7,'Kirsten')
, (8,'Amanda')
, (9,'Jimmy')
;
سپس یک Schema به نام Sample ایجاد می‌کنیم:
CREATE SCHEMA Samples ;
GO
در ادامه یک Sequence به نام Test ایجاد می‌کنیم:
CREATE SEQUENCE Samples.Test
    AS tinyint
    START WITH 1
    INCREMENT BY 1 ;
GO
حال Query زیر را اجرا می‌نماییم:
SELECT NEXT VALUE FOR Samples.Test OVER (ORDER BY Name) AS NutID, ID, Name FROM  test1.Kids
WHERE Name LIKE '%e%' ;
در Query بالا روی فیلد Name به صورت صعودی عملیات Sort انجام می‌شود، و سپس Sequence روی آن اعمال می‌گردد، برای روشن‌تر شدن مطلب خروجی را مشاهده نمایید که تعداد رکورد آن 6 میباشد.


امیدوارم مطلب فوق مفید واقع شده باشد.
مطالب
استفاده از نگارش سوم Google Analytics API در سرویس‌های ویندوز یا برنامه‌های وب
در زمان نگارش این مطلب، آخرین نگارش API مخصوص Google Analytics، نگارش سوم آن است و ... کار کردن با آن دارای مراحل خاصی است که حتما باید رعایت شوند. در غیر اینصورت عملا در یک برنامه‌ی وب یا سرویس ویندوز قابل اجرا نخواهند بود. زیرا در حالت متداول کار با API مخصوص Google Analytics، ابتدا یک صفحه‌ی لاگین به Gmail باز می‌شود که باید به صورت اجباری، مراحل آن را انجام داد تا مشخصات تائید شده‌ی اکانت در حال استفاده‌ی از API، در پوشه‌ی AppData ویندوز برای استفاده‌های بعدی ذخیره شود. این مورد برای یک برنامه‌ی دسکتاپ معمولی مشکل ساز نیست؛ زیرا کاربر برنامه، به سادگی می‌تواند صفحه‌ی مرورگری را که باز شده‌است، دنبال کرده و به اکانت گوگل خود وارد شود. اما این مراحل را نمی‌توان در یک برنامه‌ی وب یا سرویس ویندوز پیگیری کرد، زیرا عموما امکان لاگین از راه دور به سرور و مدیریت صفحه‌ی لاگین به Gmail وجود ندارد یا بهتر است عنوان شود، بی‌معنا است. برای حل این مشکل، گوگل راه حل دیگری را تحت عنوان اکانت‌های سرویس، ارائه داده است که پس از ایجاد آن، یک یک فایل X509 Certificate برای اعتبارسنجی سرویس، در اختیار برنامه نویس قرار می‌گیرد تا بدون نیاز به لاگین دستی به Gmail، بتواند از API گوگل استفاده کند. در ادامه نحوه‌ی فعال سازی این قابلیت و استفاده از آن‌را بررسی خواهیم کرد.


ثبت برنامه‌ی خود در گوگل و انجام تنظیمات آن

اولین کاری که برای استفاده از نگارش سوم Google Analytics API باید صورت گیرد، ثبت برنامه‌ی خود در Google Developer Console است. برای این منظور ابتدا به آدرس ذیل وارد شوید:
سپس بر روی دکمه‌ی Create project کلیک کنید. نام دلخواهی را وارد کرده و در ادامه بر روی دکمه‌ی Create کلیک نمائید تا پروفایل این پروژه ایجاد شود.



تنها نکته‌ی مهم این قسمت، بخاطر سپردن نام پروژه است. زیرا از آن جهت اتصال به API گوگل استفاده خواهد شد.
پس از ایجاد پروژه، به صفحه‌ی آن وارد شوید و از منوی سمت چپ صفحه، گزینه‌ی Credentials را انتخاب کنید. در ادامه در صفحه‌ی باز شده، بر روی دکمه‌ی Create new client id کلیک نمائید.


در صفحه‌ی باز شده، گزینه‌ی Service account را انتخاب کنید. اگر سایر گزینه‌ها را انتخاب نمائید، کاربری که قرار است از API استفاده کند، باید بتواند توسط مرورگر نصب شده‌ی بر روی کامپیوتر اجرا کننده‌ی برنامه، یکبار به گوگل لاگین نماید که این مورد مطلوب برنامه‌های وب و همچنین سرویس‌ها نیست.


در اینجا ابتدا یک فایل مجوز p12 را به صورت خودکار دریافت خواهید کرد و همچنین پس از ایجاد client id، نیاز است، ایمیل آن‌را جایی یادداشت نمائید:


از این ایمیل و همچنین فایل p12 ارائه شده، جهت لاگین به سرور استفاده خواهد شد.
همچنین نیاز است تا به برگه‌ی APIs پروژه‌ی ایجاد شده رجوع کرد و گزینه‌ی Analytics API آن‌را فعال نمود:


تا اینجا کار ثبت و فعال سازی برنامه‌ی خود در گوگل به پایان می‌رسد.


دادن دسترسی به Client ID ثبت شده در برنامه‌ی Google Analytics

پس از اینکه Client ID سرویس خود را ثبت کردید، نیاز است به اکانت Google Analytics خود وارد شوید. سپس در منوی آن، گزینه‌ی Admin را پیدا کرده و به آن قسمت، وارد شوید:


در ادامه به گزینه‌ی User management آن وارد شده و به ایمیل Client ID ایجاد شده در قسمت قبل، دسترسی خواندن و آنالیز را اعطاء کنید:


در صورت عدم رعایت این مساله، کلاینت API، قادر به دسترسی به Google Analytics نخواهد بود.


استفاده از نگارش سوم Google Analytics API در دات نت

قسمت مهم کار، تنظیمات فوق است که در صورت عدم رعایت آن‌ها، شاید نصف روزی را مشغول به دیباگ برنامه شوید. در ادامه نیاز است پیشنیازهای دسترسی به نگارش سوم Google Analytics API را نصب کنیم. برای این منظور، سه بسته‌ی نیوگت ذیل را توسط کنسول پاورشل نیوگت، به برنامه اضافه کنید:
 PM> Install-Package Google.Apis
PM> Install-Package Google.Apis.auth
PM> Install-Package Google.Apis.Analytics.v3
پس از نصب، کلاس GoogleAnalyticsApiV3 زیر، جزئیات دسترسی به Google Analytics API را کپسوله می‌کند:
using System;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Google.Apis.Analytics.v3;
using Google.Apis.Analytics.v3.Data;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;

namespace GoogleAnalyticsAPIv3Tests
{
    public class AnalyticsQueryParameters
    {
        public DateTime Start { set; get; }
        public DateTime End { set; get; } 
        public string Dimensions { set; get; } 
        public string Filters { set; get; }
        public string Metrics { set; get; }
    }

    public class AnalyticsAuthentication
    {
        public Uri SiteUrl { set; get; }
        public string ApplicationName { set; get; }
        public string ServiceAccountEmail { set; get; }
        public string KeyFilePath { set; get; }
        public string KeyFilePassword { set; get; }

        public AnalyticsAuthentication()
        {
            KeyFilePassword = "notasecret";
        }
    }

    public class GoogleAnalyticsApiV3
    {
        public AnalyticsAuthentication Authentication { set; get; }
        public AnalyticsQueryParameters QueryParameters { set; get; }

        public GaData GetData()
        {
            var service = createAnalyticsService();
            var profile = getProfile(service);
            var query = service.Data.Ga.Get("ga:" + profile.Id,
                                QueryParameters.Start.ToString("yyyy-MM-dd"),
                                QueryParameters.End.ToString("yyyy-MM-dd"),
                                QueryParameters.Metrics);
            query.Dimensions = QueryParameters.Dimensions;
            query.Filters = QueryParameters.Filters;
            query.SamplingLevel = DataResource.GaResource.GetRequest.SamplingLevelEnum.HIGHERPRECISION;
            return query.Execute();
        }

        private AnalyticsService createAnalyticsService()
        {
            var certificate = new X509Certificate2(Authentication.KeyFilePath, Authentication.KeyFilePassword, X509KeyStorageFlags.Exportable);
            var credential = new ServiceAccountCredential(
                new ServiceAccountCredential.Initializer(Authentication.ServiceAccountEmail)
                {
                    Scopes = new[] { AnalyticsService.Scope.AnalyticsReadonly }
                }.FromCertificate(certificate));

            return new AnalyticsService(new BaseClientService.Initializer
            {
                HttpClientInitializer = credential,
                ApplicationName = Authentication.ApplicationName
            });
        }

        private Profile getProfile(AnalyticsService service)
        {
            var accountListRequest = service.Management.Accounts.List();
            var accountList = accountListRequest.Execute();
            var site = Authentication.SiteUrl.Host.ToLowerInvariant();
            var account = accountList.Items.FirstOrDefault(x => x.Name.ToLowerInvariant().Contains(site));
            var webPropertyListRequest = service.Management.Webproperties.List(account.Id);
            var webPropertyList = webPropertyListRequest.Execute();
            var sitePropertyList = webPropertyList.Items.FirstOrDefault(a => a.Name.ToLowerInvariant().Contains(site));
            var profileListRequest = service.Management.Profiles.List(account.Id, sitePropertyList.Id);
            var profileList = profileListRequest.Execute();
            return profileList.Items.FirstOrDefault(a => a.Name.ToLowerInvariant().Contains(site));
        }
    }
}
در اینجا در ابتدا بر اساس فایل p12 ایی که از گوگل دریافت شد، یک X509Certificate2 ایجاد می‌شود. پسورد این فایل مساوی است با ثابت notasecret که در همان زمان تولید اکانت سرویس در گوگل، لحظه‌ای در صفحه نمایش داده خواهد شد. به کمک آن و همچنین ServiceAccountEmail ایمیلی که پیشتر به آن اشاره شد، می‌توان به AnalyticsService لاگین کرد. به این ترتیب به صورت خودکار می‌توان شماره پروفایل اکانت سایت خود را یافت و از آن در حین فراخوانی service.Data.Ga.Get استفاده کرد.


مثالی از نحوه استفاده از کلاس GoogleAnalyticsApiV3

در ادامه یک برنامه‌ی کنسول را ملاحظه می‌کنید که از کلاس GoogleAnalyticsApiV3 استفاده می‌کند:
using System;
using System.Collections.Generic;
using System.Linq;

namespace GoogleAnalyticsAPIv3Tests
{
    class Program
    {

        static void Main(string[] args)
        {
            var statistics = new GoogleAnalyticsApiV3
            {
                Authentication = new AnalyticsAuthentication
                {
                    ApplicationName = "My Project",
                    KeyFilePath = "811e1d9976cd516b55-privatekey.p12",
                    ServiceAccountEmail = "10152bng4j3mq@developer.gserviceaccount.com",
                    SiteUrl = new Uri("https://www.dntips.ir/")
                },
                QueryParameters = new AnalyticsQueryParameters
                {
                    Start = DateTime.Now.AddDays(-7),
                    End = DateTime.Now,
                    Dimensions = "ga:date",
                    Filters = null,
                    Metrics = "ga:users,ga:sessions,ga:pageviews"
                }
            }.GetData();


            foreach (var result in statistics.TotalsForAllResults)
            {
                Console.WriteLine(result.Key + " -> total:" + result.Value);
            }
            Console.WriteLine();

            foreach (var row in statistics.ColumnHeaders)
            {
                Console.Write(row.Name + "\t");
            }
            Console.WriteLine();

            foreach (var row in statistics.Rows)
            {
                var rowItems = (List<string>)row;
                Console.WriteLine(rowItems.Aggregate((s1, s2) => s1 + "\t" + s2));
            }

            Console.ReadLine();
        }
    }
}

چند نکته
ApplicationName همان نام پروژه‌ای است که ابتدای کار، در گوگل ایجاد کردیم.
KeyFilePath مسیر فایل مجوز p12 ایی است که گوگل در حین ایجاد اکانت سرویس، در اختیار ما قرار می‌دهد.
ServiceAccountEmail آدرس ایمیل اکانت سرویس است که در قسمت ادمین Google Analytics به آن دسترسی دادیم.
SiteUrl آدرس سایت شما است که هم اکنون در Google Analytics دارای یک اکانت و پروفایل ثبت شده‌است.
توسط AnalyticsQueryParameters می‌توان نحوه‌ی کوئری گرفتن از Google Analytics را مشخص کرد. تاریخ شروع و پایان گزارش گیری در آن مشخص هستند. در مورد پارامترهایی مانند Dimensions و Metrics بهتر است به مرجع کامل آن در گوگل مراجعه نمائید:
Dimensions & Metrics Reference

برای نمونه در مثال فوق، تعداد کاربران، سشن‌های آن و همچنین تعداد بار مشاهده‌ی صفحات، گزارشگیری می‌شود.


برای مطالعه بیشتر
Using Google APIs in Windows Store Apps
How To Use Google Analytics From C# With OAuth
Google Analytic’s API v3 with C#
.NET Library for Accessing and Querying Google Analytics V3 via Service Account
Google OAuth2 C#
مطالب
شیوه‌نامه‌های مقدماتی بوت استرپ 4
وقتی صفحه‌ی وبی را باز می‌کنید، تنظیمات بسیاری بر روی ظاهر آن تاثیرگذار هستند. برای مثال خود مرورگر دارای تنظیماتی است که بر روی ظاهر پیش‌فرض عناوین و عناصر مختلف قرار گرفته شده‌ی بر روی صفحه تاثیر گذار است. به این موارد Browser Styles گفته می‌شود که با Custom Styles ما قابلیت بازنویسی را دارند. در این بین، شیوه‌نامه‌های بوت استرپ، بین Browser Styles و شیوه‌نامه‌های سفارشی ما قرار می‌گیرند تا ظاهر بهتری را برای عناصر مختلف صفحه ارائه دهند.


تایپوگرافی مقدماتی بوت استرپ 4

شیوه‌نامه‌های همراه با بوت استرپ، رفتار و تنظیمات پیش‌فرض مرورگر را بازنویسی می‌کنند. این بازنویسی با فایل node_modules\bootstrap\scss\_reboot.scss شروع می‌شود. اگر مجموعه‌ی بوت استرپ را توسط روش معرفی شده‌ی در مطلب «روش‌های مختلف دریافت و نصب بوت استرپ 4» دریافت کرده باشید، کدهای کامل SASS آن، در پوشه‌ی scss این مجموعه، موجود هستند که یکی از آن‌ها فایل reboot است. کار آن نرمال سازی شیوه‌نامه‌ها، به نحوی است که در مرورگرها مختلف و همچنین وسایل نمایشی متفاوت، یکسان به نظر برسند:
 - برای مثال در این فایل از روش اندازه گیری rem استفاده شده‌است تا مدیریت اندازه‌های آن در سکوهای کاری مختلف قابل کنترل شود.
 - در اینجا از margin-top به طور کامل صرفنظر شده‌است، تا بتوان اندازه‌گیری فواصل بین عناصر را بهتر محاسبه کرد. بوت استرپ 4 تنها یک margin را در پایین تمام عناصر صفحه، تنظیم می‌کند. بنابراین آگاهی از وجود این پیش‌فرض، تنظیم فواصل بین عناصر را نیز ساده‌تر می‌کند.
 - در این فایل در همه‌جا از خاصیت inherit استفاده شده‌است تا امکان بازنویسی شیوه‌نامه‌های آن توسط custom styles ما ساده‌تر شود.
 - پیش‌فرض دیگری که در این نگارش از بوت استرپ تنظیم شده‌است، border-box می‌باشد. به این ترتیب اندازه گیری عرض عناصر ساده‌تر می‌شوند. برای مثال اگر عرض یک div را مساوی 200px قرار دهید، یک padding پیش‌فرض نیز برای آن درنظر گرفته شده‌است و padding سفارشی تنظیم شده‌ی برای آن بی‌اثر خواهد بود.
 - در این نگارش، فونت پیش‌فرض صفحه، به فونت پیش‌فرض سیستم تنظیم شده‌است و نه فونت از پیش تعیین شده‌ی خاصی. از این جهت که این قلم‌های سیستمی، دارای ویژگی‌های خاصی هستند که آن‌ها را برای سکوهای کاری مختلف، منحصربفرد می‌کنند.

مثال: نمایش تاثیر بوت استرپ 4 بر روی تایپوگرافی پیش‌فرض مرورگر
<body>
    <div class="container">
        <section class="content" id="mission">
            <h1>Our Commitment <small>to you</small></h1>
            <p>Wisdom Pet Medicine strives to blend the best in traditional and
                <em>alternative medicine</em> in the <strong>diagnosis and
                    treatment</strong> of companion animals including dogs,
                cats, birds, reptiles, rodents, and fish. We apply the wisdom
                garnered in the <mark>centuries old tradition</mark> of
                veterinary medicine, to find the safest treatments
                and&nbsp;cures.</p>
            <p>We strive to be your pet's medical <del>staff</del> experts from
                youth through the senior years. <small>We build preventative
                    health care plans for each and every one of our patients,
                    based on breed, age, and sex, so that your pet receives the
                    most appropriate care at crucial milestones.</small> We
                want to give your pet a long and healthy&nbsp;life.</p>
        </section>

        <section class="content" id="services">
            <h2>Exotic Pets</h2>
            <p>We offer <strong>specialized</strong> care for <em>reptiles,
                    rodents, birds,</em> and other exotic pets.</p>

            <h3>Grooming</h3>
            <p>Our therapeutic <span>grooming</span> treatments help battle
                fleas, allergic dermatitis, and other challenging skin
                conditions.</p>

            <h4>General Health</h4>
            <p>Wellness and senior exams, ultrasound, x-ray, and dental
                cleanings are just a few of our general health services.</p>

            <h5>Nutrition</h5>
            <p>Let our nutrition experts review your pet's diet and prescribe a
                custom nutrition plan for optimum health and disease
                prevention.</p>

            <h6>Pest Control</h6>
            <p>We offer the latest advances in safe and effective prevention
                and treatment of fleas, ticks, worms, heart worm, and other
                parasites.</p>

            <h2>Vaccinations</h2>
            <p>Our veterinarians are experienced in modern vaccination
                protocols that prevent many of the deadliest diseases in pets.</p>
        </section>
    </div>
</body>
با این خروجی:

با اعمال بوت استرپ


بدون اعمال بوت استرپ


در اینجا دو تصویر راملاحظه می‌کنید؛ یکی با اعمال bootstrap.min.css به صفحه‌است و دیگری با حذف آن از صفحه. به این ترتیب مشاهده می‌کنید که صرفا اعمال بوت استرپ به یک صفحه‌ی متداول، کیفیت نمایش آن‌را با بازنویسی شیوه‌نامه‌ی پیش‌فرض مرورگر، به نحو قابل ملاحظه‌ای بهبود بخشیده‌است و آن‌را زیباتر کرده‌است.
در اینجا تنها المان بوت استرپی که به صفحه اضافه شده‌است و جزو استانداردهای HTML نیست، یک div با کلاس container است:
<body>
    <div class="container">
کل محتوای صفحه جهت اعمال شیوه‌نامه‌های بوت استرپ، داخل این div قرار می‌گیرند. اولین تاثیر آن واکنشگرا کردن صفحه‌است و همچنین یک padding را نیز به قسمت‌های چپ و راست صفحه اضافه کرده‌است.
در این مثال تاثیر بوت استرپ را بر روی شیوه‌نامه‌های پیش‌فرض خصوصا  h1 تا h6، مشاهده می‌کنید.
روش دیگر تعریف headings در اینجا، استفاده از کلاس‌هایی با نام‌های مشابه است:
<div class="h1">Test div class H1</div>
علاوه بر آن، کلاس display نیز در اینجا برای تعیین اندازه‌ی headings سفارشی پیش بینی شده‌است که می‌توان از عدد 1 تا 4 را توسط آن تنظیم کرد:
<div class="display-1">Test div class display-1</div>
با این خروجی و اندازه در مقایسه با headings استاندارد که امکان تعریف تیترهایی بزرگ‌تر از اندازه‌های متداول را میسر می‌کنند:


همچنین اگر نیاز به بزرگتر نمایش دادن متن قسمت ابتدایی صفحه وجود داشت، می‌توان از کلاس Lead استفاده کرد:
<p class="lead">Testing a lead class</p>



کلاس‌های کمکی کار با متون در بوت استرپ 4

بوت استرپ 4 به همراه تعدادی کلاس کمکی کار با متون است که نیازهای متداول تایپوگرافی را برآورده می‌کنند:

1) کلاس‌های کمکی محل قرارگیری متون
- کلاس text-justify کار کشیدن و متناسب کردن یک پاراگراف را با گوشه‌های سمت چپ و راست صفحه انجام می‌دهد.
- کلاس text-nowrap از شکسته شدن متن به چندین سطر جلوگیری می‌کند. برای مثال می‌تواند برای نمایش کدها مناسب باشد.
- کلاس متغیر text-xx-pos برای تعیین محل قرارگیری متن کاربرد دارد:
در اینجا ذکر xx اختیاری است و می‌تواند sm، برای اندازه‌های صفحه‌ی بیشتر از 576px و یا md، برای اندازه‌های صفحه‌ی بیشتر از 768px و یا lg، برای اندازه‌های صفحه‌ی بیشتر از 992px و یا xl، برای اندازه‌های صفحه‌ی بیشتر از 1200px باشد. این اندازه‌های یاد شده را در ادامه بیشتر مشاهده خواهید کرد.
همچنین pos می‌تواند left ،center و یا right باشد.
برای مثال کلاس text-sm-center به این معنا است که متن مدنظر در break-point ایی به نام sm، یعنی با اندازه‌ی صفحه‌ی بیشتر از 576px، در وسط صفحه نمایش داده خواهد شد.

2) کلاس‌های نمایش upper-case و lower-case حروف
- کلاس text-lowercase کار نمایش lower-case کل یک پاراگراف اعمالی را انجام می‌دهد.
- کلاس text-uppercase کار نمایش upper-case کل یک پاراگراف اعمالی را انجام می‌دهد.
- کلاس text-capitalize، اولین حرف هر واژه را به صورت بزرگ نمایش می‌دهد.

3) کلاس‌های شیوه‌ی نمایش متون
کلاس font-weight-bold، کلاس font-weight-normal و کلاس font-italic نمایش ضخیم، عادی و یا italic متن را سبب می‌شوند.

مثال: بررسی تاثیر کلاس‌های کمکی کار با متون در بوت استرپ 4
<body>
    <div class="container">
        <section class="content" id="mission">
            <h1 class="text-center text-sm-right text-md-left text-uppercase">Our
                Commitment</h1>
            <p class="lead text-justify">Wisdom Pet Medicine strives to blend
                the best in
                traditional and <em>alternative medicine</em> in the <strong>diagnosis
                    and treatment</strong> of companion animals including dogs,
                cats, birds, reptiles, rodents, and fish. We apply the wisdom
                garnered in the <mark>centuries old tradition</mark> of
                veterinary medicine, to find the safest treatments
                and&nbsp;cures.</p>
            <p class="text-nowrap text-capitalize">We strive to be your pet's
                medical <del>staff</del>
                experts from
                youth through the senior years. <small>We build preventative
                    health care plans for each and every one of our patients,
                    based on breed, age, and sex, so that your pet receives the
                    most appropriate care at crucial milestones.</small> We
                want to give your pet a long and healthy&nbsp;life.</p>
        </section>

        <section class="content" id="services">
            <div class="display-4">Exotic Pets</div>
            <p>We <span class="font-weight-bold">offer</span> <strong class="font-weight-normal">specialized</strong>
                care for <em>reptiles,
                    rodents, birds,</em> and other exotic pets.</p>

            <h3 class="text-left text-md-center text-sm-right">Grooming</h3>
            <p>Our therapeutic <span>grooming</span> treatments help battle
                fleas, allergic dermatitis, and other challenging skin
                conditions.</p>

            <h4>General Health</h4>
            <p>Wellness and senior exams, ultrasound, x-ray, and dental
                cleanings are just a few of our general health services.</p>

            <h5>Nutrition</h5>
            <p>Let our nutrition experts review your pet's diet and prescribe a
                custom nutrition plan for optimum health and disease
                prevention.</p>

            <h6>Pest Control</h6>
            <p>We offer the latest advances in safe and effective prevention
                and treatment of fleas, ticks, worms, heart worm, and other
                parasites.</p>

            <h2>Vaccinations</h2>
            <p>Our veterinarians are experienced in modern vaccination
                protocols that prevent many of the deadliest diseases in pets.</p>
        </section>
    </div>
</body>
با این خروجی


در اینجا اگر کلاس text-right را به heading اضافه کنیم:
<h1 class="text-right">Our Commitment <small>to you</small></h1>
چنین خروجی حاصل می‌شود:


و یا می‌توان این کلاس‌ها را با هم ترکیب کرد:
<h1 class="text-center text-sm-right">Our Commitment <small>to you</small></h1>
این ترکیب به این معنا است:
- متن h1 در حالت عادی در وسط صفحه نمایش داده شود.


- اما متن مدنظر در break-point ایی به نام sm، یعنی با اندازه‌ی صفحه‌ی بیشتر از 576px، در سمت راست صفحه نمایش داده خواهد شد.


این اعداد توسط افزونه‌ی ViewPort نمایش داده شده‌اند.


همچنین تاثیر text-justify را نیز به اولین پاراگراف
<p class="lead text-justify">
به صورت ذیل مشاهده می‌کنید که در مقایسه با تصویر قبلی، سبب کشیده شدن متن و تنظیم آن با سمت راست و چپ صفحه شده‌است:


و یا اگر text-nowrap را به پاراگرافی اعمال کنیم:
<p class="text-nowrap">


سبب نمایش یک سطری آن خواهد شد که در اینجا با پدید آمدن یک اسکرول بار افقی، قابل ملاحظه‌است.


کلاس‌های کمکی کار با لیست‌ها و نقل قول‌ها در بوت استرپ 4

بوت استرپ 4 به همراه کلاس‌هایی کمکی برای کار با لیست‌ها و نقل قول‌ها است؛ مانند:
- کلاس list-unstyled سبب حذف bullets از یک لیست می‌شود.
- برای ایجاد لیست‌های Inline می‌توان از کلاس list-inline بر روی المان UL و سپس list-inline-item بر روی هر LI آن، کمک گرفت.
<body>
    <div class="container">
        <section class="content" id="services">
            <h2>Exotic Pets</h2>
            <p>We offer <strong>specialized</strong> care for <em>reptiles,
                    rodents, birds,</em> and other exotic pets.</p>
            <ul class="list-unstyled">
                <li>Grooming</li>
                <li>General Health</li>
                <li>Nutrition</li>
                <li>Pest Control</li>
                <li>Vaccinations</li>
            </ul>

            <ul class="list-inline">
                <li class="list-inline-item">Grooming</li>
                <li class="list-inline-item">General Health</li>
                <li class="list-inline-item">Nutrition</li>
                <li class="list-inline-item">Pest Control</li>
                <li class="list-inline-item">Vaccinations</li>
            </ul>
        </section>
    </div>
</body>
در این مثال نحوه‌ی حذف bullets و همچنین inline تعریف کردن دو لیست را مشاهده می‌کنید؛ با این خروجی:


در حالت لیست inline، آیتم‌های لیست از چپ به راست در یک سطر نمایش داده می‌شوند. برای مثال می‌تواند برای نمایش breadcrumbs در یک سایت مناسب باشد.
همچنین برای نمایش نقل قول‌ها می‌توان از کلاس blockquote و برای نمایش بهتر امضای آن از کلاس blockquote-footer استفاده کرد:
<body>
    <div class="container">
        <section class="content" id="testimonials">
            <h2>Testimonials</h2>

            <blockquote>
                During the summer, our rabbit, Tonto, began to have severe
                redness and itching on his belly and feet. I'm very thankful to
                the veterinarians and staff at Wisdom for the excellent care
                Tonto received, and for nipping his allergies in the bud, so to
                speak.
                Jane
            </blockquote>

            <blockquote class="blockquote text-right">
                When Samantha, our Siamese cat, began sleeping all the time and
                urinating excessively, we brought her to see the specialists at
                Wisdom. Now, two years later, Samantha is still free from any
                complications of diabetes, and her blood sugar regularly tests
                normal.
                <div class="blockquote-footer">
                    The McPhersons
                </div>
            </blockquote>
        </section>
    </div>
</body>


در اینجا دو blockquote را مشاهده می‌کنید. مورد اول بدون کلاس blockquote است و دومی به همراه این کلاس و یک footer تعریف شده‌است. همچنین می‌توان کلاس‌هایی مانند text-right را نیز به blockquote اضافه کرد.
البته در نگارش 4، حاشیه‌ی خاکستری blockquote که در نگارش سوم آن وجود داشت، حذف شده‌است.


کار با رنگ‌ها در بوت استرپ 4

بوت استرپ، به همراه تعدادی کلاس مخصوص رنگ‌ها است که از آن در همه جا استفاده می‌کند؛ مانند رنگ‌های دکمه‌ها، پس زمینه‌ها و متون.
1) کلاس‌های تعیین رنگ متون:


برای مثال در اینجا بجای Color می‌توان یکی از ثوابت ذیل آن‌را قید کرد؛ مانند text-primary و یا text-danger
این کلاس‌ها برای تعیین رنگ متون و همچنین لینک‌ها کاربرد دارند.


2) کلاس‌های تعیین رنگ پس زمینه:


در اینجا برای نمونه بجای Color می‌توان یکی از ثوابت ذیل آن‌را قید کرد؛ مانند bg-primary و یا bg-danger

مثال: اعمال رنگ‌های زمینه‌ای بوت استرپ
<body>
    <div class="container">
        <section class="content" id="services">
            <h2 class="text-danger">Our Mission</h2>
            <p class="bg-danger text-white">Wisdom Pet Medicine strives to
                blend the best in
                traditional and
                alternative medicine in the diagnosis and treatment of
                companion animals including dogs, cats, birds, reptiles,
                rodents, and fish. We apply the wisdom garnered in the
                centuries old tradition of veterinary medicine, to find the
                safest treatments and cures.</p>

            <ul>
                <li><a class="text-warning" href="#">Grooming</a></li>
                <li><a href="#">General Health</a></li>
                <li><a href="#">Nutrition</a></li>
                <li><a href="#">Pest Control</a></li>
                <li><a href="#">Vaccinations</a></li>
            </ul>

        </section>

        <section class="content" id="testimonials">
            <h2>Testimonials</h2>

            <blockquote class="blockquote bg-faded text-info">
                During the summer, our rabbit, Tonto, began to have severe
                redness and itching on his belly and feet. I'm very thankful to
                the veterinarians and staff at Wisdom for the excellent care
                Tonto received, and for nipping his allergies in the bud, so to
                speak.
                <div class="blockquote-footer">
                    Jane
                </div>
            </blockquote>
        </section>
    </div>
</body>
با این خروجی


در اینجا مثال‌هایی را از اعمال کلاس‌های رنگ‌های بوت استرپ مشاهده می‌کنید. همچنین امکان ترکیب آن‌ها مانند مثال زیر نیز وجود دارد:
<p class="bg-danger text-white">



کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: Bootstrap4_02.zip
مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت سوم - غنی سازی کامپوننت‌ها
در قسمت قبل، مقدمه‌ای بر نحوه‌ی تعریف یک کامپوننت در AngularJS 2.0 عنوان شد و همچنین نحوه‌ی بوت استرپ و آغاز اینگونه برنامه‌ها بررسی گردید. در این قسمت می‌خواهیم امکانات پیشرفته‌تری از کامپوننت‌ها را بررسی کنیم.


روش‌های مختلف تعریف خاصیت template در یک کامپوننت

در قسمت قبل، روش تعریف inline یک template را مشاهده کردید:
template:`
          <div><h1>{{pageTitle}}</h1>
               <div>My First Component</div>
          </div>
 `
در اینجا رشته‌ی قالب نهایی این View، در همان تعاریف متادیتای Component قرار گرفته‌است (روش inline). اگر این رشته تک سطری باشد، از روش متداول ذکر "" برای تعریف رشته‌ها در جاوا اسکریپت استفاده می‌شود و اگر این رشته چند سطری باشد، از back tick مربوط به ES 6 مانند مثال فوق کمک گرفته خواهد شد. استفاده از back ticks و رشته‌های چند سطری، نحوه‌ی تعریف قالب‌های inline را خواناتر می‌کند.
هر چند این روش تعریف قالب‌ها، مزیت سادگی و امکان مشاهده‌ی View را به همراه کدهای مرتبط با آن، در یک فایل میسر می‌کند، اما به دلیل رشته‌ای بودن، مزیت کار کردن با ادیتورهای وب، مانند داشتن intellisense، فرمت خودکار کدها و بررسی syntax را از دست خواهیم داد و با بیشتر شدن حجم این رشته، این مشکلات بیشتر نمایان خواهند شد.
به همین جهت قابلیت دیگری به نام linked template نیز در اینجا درنظر گرفته شده‌است:
 templateUrl: 'product-list.component.html'
در این حالت، محتوای قالب، به یک فایل html مجزا منتقل شده و سپس لینک آن در خاصیت دیگری از متادیتای Component به نام templateUrl ذکر می‌شود.


ساخت کامپوننت نمایش لیست محصولات

در ادامه می‌خواهیم کامپوننتی را طراحی کنیم که آرایه‌ای از محصولات را نمایش می‌دهد. در اینجا مرسوم است هر ویژگی برنامه، در یک پوشه‌ی مجزا قرار گیرد. به همین جهت در ادامه‌ی مثال قسمت قبل که پوشه‌ی app را به ریشه‌ی پروژه اضافه کردیم و سپس main.ts راه انداز و کامپوننت ریشه‌ی سایت یا app.component.ts را در آن تعریف کردیم، در داخل همین پوشه‌ی app، پوشه‌ی جدیدی را به نام products اضافه می‌کنیم. سپس به این پوشه‌ی جدید محصولات، فایل جدیدی را به نام product-list.component.html اضافه کنید. از این فایل جهت تعریف قالب کامپوننت لیست محصولات استفاده خواهیم کرد. در اینجا نیز مرسوم است نام قالب یک Component را به صورت نام ویژگی ختم شده‌ی به کلمه‌ی Component، با پسوند html تعریف کنیم.


پس از اضافه شدن فایل product-list.component.html، محتوای آن‌را به نحو ذیل تغییر دهید:
<div class='panel panel-default'>
    <div class='panel-heading'>
        {{pageTitle}}
    </div>
    <div class='panel-body'>
        <div class='row'>
            <div class='col-md-2'>Filter by:</div>
            <div class='col-md-4'>
                <input type='text' />
            </div>
        </div>
        <div class='row'>
            <div class='col-md-6'>
                <h3>Filtered by: </h3>
            </div>
        </div>
        <div class='table-responsive'>
            <table class='table'>
                <thead>
                    <tr>
                        <th>
                            <button class='btn btn-primary'>
                                Show Image
                            </button>
                        </th>
                        <th>Product</th>
                        <th>Code</th>
                        <th>Available</th>
                        <th>Price</th>
                        <th>5 Star Rating</th>
                    </tr>
                </thead>
                <tbody>
 
                </tbody>
            </table>
        </div>
    </div>
</div>
در اینجا قصد داریم داخل پنل بوت استرپ 3، لیستی از محصولات را به صورت یک جدول نمایش دهیم. همچنین می‌خواهیم قابلیت جستجوی داخل این لیست را نیز فراهم کنیم. فعلا شکل کلی این قالب را به نحو فوق تهیه می‌کنیم. قسمت tbody جدول آن را که قرار است لیست محصولات را رندر کند، در ادامه‌ی بحث تکمیل خواهیم کرد.
تنها نکته‌ی AngularJS 2.0 قالب فوق، اتصال به pageTitle است که نمونه‌ای از آن‌را در قسمت قبل با معرفی اولین کامپوننت مشاهده کرده‌اید.

در ادامه نیاز است برای این قالب و view، یک کامپوننت را طراحی کنیم که متشکل است از یک کلاس TypeScript ایی مزین شده به Component. بنابراین فایل ts جدیدی را به نام product-list.component.ts به پوشه‌ی App\products اضافه کنید؛ با این محتوا:
import { Component } from 'angular2/core';
 
@Component({
    selector: 'pm-products',
    templateUrl: 'app/products/product-list.component.html'
})
export class ProductListComponent {
    pageTitle: string = 'Product List';
}


با جزئیات نحوه‌ی تعریف یک کامپوننت در قسمت قبل در حین معرفی کامپوننت‌ها آشنا شدیم. در اینجا کلاس ProductListComponent با واژه‌ی کلیدی export همراه است تا توسط module loader برنامه قابلیت بارگذاری را پیدا کند. همچنین خاصیت عمومی pageTitle نیز در آن تعریف شده‌است تا در قالب مرتبط مورد استفاده قرار گیرد.
سپس این کلاس، با decorator ویژه‌ای به نام Component مزین شده‌است تا AngularJS 2.0 بداند که هدف از تعریف آن، ایجاد یک کامپوننت جدید است. مقدار selector آن که تشکیل دهنده‌ی یک تگ HTML سفارشی متناظر با آن خواهد شد، به pm-products تنظیم شده‌است و اینبار بجای تعریف inline قالب آن به صورت یک رشته، از خاصیت templateUrl جهت معرفی مسیر فایل html قالبی که پیشتر آماده کردیم، کمک گرفته شده‌است.


نمایش کامپوننت لیست محصولات در صفحه‌ی اصلی سایت

خوب، تا اینجا یک کامپوننت جدید را به نام لیست محصولات، ایجاد کردیم؛ اما چگونه باید آن‌را نمایش دهیم؟
در قسمت قبل که کامپوننت ریشه‌ی برنامه یا AppComponent را تعریف کردیم، نام selector آن را pm-app درنظر گرفتیم و در نهایت این directive سفارشی را به نحو ذیل در body صفحه‌ی اصلی سایت نمایش دادیم:
    <div>
        @RenderBody()
        <pm-app>Loading App...</pm-app>
    </div>
اما این روش، تنها برای root component سایت مناسب است. برای سایر کامپوننت‌های غیر ریشه‌ای (یعنی تمام کامپوننت‌ها)، سه مرحله‌ی زیر باید طی شوند:
الف) تگ سفارشی این دایرکتیو جدید را به کامپوننت ریشه‌ی سایت یا همان AppComponent اضافه می‌کنیم. بنابراین فایل app.component.ts را گشوده و سپس selector کامپوننت لیست محصولات را به قالب آن اضافه کنید:
import { Component } from 'angular2/core';
 
@Component({
    selector: 'pm-app',
    template:`
    <div><h1>{{pageTitle}}</h1>
        <pm-products></pm-products>
    </div>
    `
})
export class AppComponent {
    pageTitle: string = "DNT AngularJS 2.0 APP";
}
همانطور که مشاهده می‌کنید، تگ جدید pm-products بر اساس نام selector کامپوننت لیست محصولات، به قالب کامپوننت ریشه‌ی سایت اضافه شده‌است.
ب) تا اینجا یک دایرکتیو جدید را به نام pm-products به یک کامپوننت دیگر اضافه کرده‌ایم. اما این کامپوننت نمی‌داند که اطلاعات آن‌را باید از کجا تامین کند. برای این منظور خاصیت جدیدی را به نام directives به لیست خاصیت‌های Component ریشه‌ی سایت اضافه می‌کنیم. این خاصیت، آرایه‌ای از دایرکتیوهای سفارشی را قبول می‌کند:
 directives: [ProductListComponent]
ج) بلافاصله که این تغییر را اعمال کنید، در ادیتور TypeScript ایی موجود، ذیل کلمه‌ی ProductListComponent خط قرمز کشیده خواهد شد. چون هنوز مشخص نکرده‌ایم که این شیء جدید باید از کدام ماژول تامین شود و ناشناخته‌است. بنابراین import مربوطه را به ابتدای فایل اضافه می‌کنیم:
import { Component } from 'angular2/core';
import { ProductListComponent } from './products/product-list.component';
 
@Component({
    selector: 'pm-app',
    template:`
    <div><h1>{{pageTitle}}</h1>
        <pm-products></pm-products>
    </div>
    `,
    directives: [ProductListComponent]
})
export class AppComponent {
    pageTitle: string = "DNT AngularJS 2.0 APP";
}
کدهای فوق، کد نهایی کامپوننت ریشه‌ی سایت هستند که به آن selector جدیدی به نام pm-products اضافه شده‌است. سپس directive متناظر آن به لیست دایرکتیوهای کامپوننت جاری اضافه شده و در نهایت این دایرکتیو، از ماژول مرتبط با آن import شده‌است.

این سه مرحله، مراحلی هستند که جهت افزودن هر دایرکتیو جدید به کامپوننتی مشخص، باید طی شوند.

خوب، اکنون اگر برنامه را اجرا کنیم، چنین خروجی را می‌توان مشاهده کرد:


یک نکته
اگر برنامه را اجرا کردید و خروجی را مشاهده نکردید، مطمئن شوید که فایل‌های ts شما کامپایل شده‌اند. فشردن دکمه‌ی ctrl+s مجدد در این فایل‌ها، سبب کامپایل مجدد آن‌ها می‌شوند و یا انتخاب گزینه‌ی Build و سپس ReBuild solution نیز همینکار را انجام می‌دهد.


غنی سازی کامپوننت‌های AngularJS 2.0 با data-binding

در AngularJS 2.0 عملیات binding، کار مدیریت ارتباطات بین یک کلاس کامپوننت و قالب آن‌را انجام می‌دهد. نمونه‌ای از آن‌را پیشتر با خاصیت pageTitle و سپس نمایش آن در قالب کامپوننت متناظر با آن کلاس، مشاهده کرده‌اید. همچنین در اینجا یک قالب می‌تواند متدهای داخل کلاس کامپوننت خود را توسط رخدادها نیز فراخوانی کند.
به نحوه‌ی نمایش {{pageTitle}} اصطلاحا interpolation می‌گویند. در اینجا خاصیت pageTitle اطلاعات خود را از کلاس کامپوننت دریافت می‌کند. به این نوع binding، انقیاد یک طرفه یا one-way binding نیز گفته می‌شوند؛ از خاصیت کلاس شروع شده و به قالب خاتمه می‌یابد.
ویژگی interpolation فراتر است از صرفا نمایش یک خاصیت و می‌تواند حاوی محاسبات نیز باشد:
{{'Title: ' + pageTitle}}
{{2*20+1}}
و یا حتی در آن می‌توان متدی از کلاس کامپوننت را نیز فراخوانی کرد. در مثال زیر فرض شده‌است که متد getTitle، در کلاس متناظر با کامپوننت این قالب، تعریف شده‌است:
{{'Title: ' + getTitle()}}
کار interpolation درج عبارت محاسبه شده‌ی نهایی بین المان‌های html است؛ مانند:
 <h1>{{pageTitle}}</h1>
و یا حتی می‌توان این مقدار نهایی را به خواص المان‌های html نیز نسبت داد:
 <h1 innerText={{pageTitle}}></h1>
در این مثال خاصیت innerText المان h1 توسط interpolation مقدار دهی شده‌است.

بنابراین به صورت خلاصه هر زمانیکه نیاز به نمایش اطلاعات فقط خواندنی (one-way binding) داریم، ابتدا خاصیتی را در کلاس کامپوننت تعریف کرده و سپس مقدار این خاصیت را توسط interpolation، در قالب کامپوننت درج می‌کنیم. حین استفاده از interpolation نیازی به ذکر "" نیست.
در مورد مباحث تکمیلی binding در قسمت‌های بعدی بیشتر بحث خواهیم کرد.


افزودن منطقی سفارشی به قالب یک کامپوننت

دایرکتیوها به صورت المان‌ها و یا ویژگی‌های سفارشی HTML، قابلیت توسعه‌ی امکانات پیش فرض آن‌را دارند. در اینجا می‌توان دایرکتیوهای سفارشی خود را تولید کرد (مانند pm-products فوق) و یا از دایرکتیوهای توکار AngularJS 2.0 استفاده کرد. برای مثال ngIf* و ngFor* جزو structural directives توکار AngularJS 2.0 هستند. ستاره‌ای که پیش از نام این دایرکتیوها قرار گرفته‌است، آن‌‌ها را در گروه structural directives قرار می‌دهد.
کار دایرکتیوهای ساختاری، تغییر ساختار یا همان view کامپوننت‌ها است؛ با افزودن، حذف و یا تغییر المان‌های HTML تعریف شده‌ی در صفحه.

بررسی ngIf*

فایل قالب product-list.component.html را گشوده و تعریف جدول آن‌را به نحو ذیل تغییر دهید:
 <table class='table' *ngIf='products && products.length'>
کار ngIf* نمایش یا عدم نمایش قسمتی از DOM یا document object model بر اساس برآورده شدن منطقی است که توسط آن بررسی می‌شود. اگر حاصل عبارتی که به ngIf* انتساب داده می‌شود به false تعبیر شود، آن المان و فرزندان آن از DOM حذف می‌شوند و اگر این عبارت به true تعبیر شود، آن المان و فرزندانش مجددا به DOM اضافه خواهند شد.
برای نمونه عبارت انتساب داده شده‌ی به ngIf* در مثال فوق به این معنا است که اگر خاصیت و آرایه‌ی products در کلاس کامپوننت این قالب تعریف شده بود و همچنین دارای اعضایی نیز بود، آنگاه این جدول را نمایش بده.
برای آزمایش آن، فایل product-list.component.ts را گشوده و خاصیت عمومی آرایه‌ی products را به نحو ذیل به آن اضافه کنید:
import { Component } from 'angular2/core';
 
@Component({
    selector: 'pm-products',
    templateUrl: 'app/products/product-list.component.html'
})
export class ProductListComponent {
    pageTitle: string = 'Product List';
    products: any[] = [
        {
            "productId": 2,
            "productName": "Garden Cart",
            "productCode": "GDN-0023",
            "releaseDate": "March 18, 2016",
            "description": "15 gallon capacity rolling garden cart",
            "price": 32.99,
            "starRating": 4.2,
            "imageUrl": "app/assets/images/garden_cart.png"
        },
        {
            "productId": 5,
            "productName": "Hammer",
            "productCode": "TBX-0048",
            "releaseDate": "May 21, 2016",
            "description": "Curved claw steel hammer",
            "price": 8.9,
            "starRating": 4.8,
            "imageUrl": "app/assets/images/rejon_Hammer.png"
        }
    ];
}
فعلا چون اینترفیسی را برای شیء محصول تعریف نکرده‌ایم، نوع این آرایه را any یا همان حالت پیش فرض جاوا اسکریپت تعریف می‌کنیم.
همچنین فعلا در اینجا اطلاعات را بجای دریافت از سرور، توسط آرایه‌ی مشخصی از اشیاء تعریف کرده‌ایم. این موارد را در قسمت‌های بعدی بهبود خواهیم بخشید.

اکنون که خاصیت عمومی products تعریف شده‌است، امکان استفاده‌ی از ngIf* ایی که پیشتر تعریف کردیم، میسر شده‌است. در این حالت اگر برنامه را اجرا کنید، قسمت table header تصویر قبلی نمایش سایت، هنوز نمایان است. یعنی ngIf* تعریف شده کار می‌کند؛ چون خاصیت products تعریف شده‌است و همچنین دارای اعضایی است.
برای آزمایش بیشتر، خاصیت products را کامنت کنید و یکبار نیز فایل ts آن‌را ذخیره کنید تا فایل js متناظر با آن کامپایل شود. سپس مجددا برنامه را اجرا کنید. در این حالت دیگر نباید هدر جدول نمایان باشد؛ چون products تعریف نشده‌است.


بررسی ngFor*

تا اینجا بر اساس داشتن لیستی از محصولات یا عدم آن، جدول متناظری را نمایش داده و یا مخفی کردیم. اما این جدول هنوز فاقد ردیف‌های نمایش اعضای آرایه‌ی products است.
برای این منظور مجددا فایل قالب product-list.component.html را گشوده و سپس بدنه‌ی جدول را به نحو ذیل تکمیل کنید:
<tbody>
    <tr *ngFor='#product of products'>
        <td></td>
        <td>{{ product.productName }}</td>
        <td>{{ product.productCode }}</td>
        <td>{{ product.releaseDate }}</td>
        <td>{{ product.price }}</td>
        <td>{{ product.starRating }}</td>
    </tr>
</tbody>
یکی دیگر از دایرکتیوهای ساختاری، ngFor* نام دارد. کار آن تکرار قسمتی از DOM، به ازای تک تک عناصر لیست انتساب داده شده‌ی به آن است.
بنابراین ابتدا قسمتی از عناصر HTML را طوری کنار هم قرار می‌دهیم که جمع آن‌ها یک تک آیتم را تشکیل دهند. سپس با استفاده از ngFor* به AngularJS 2.0 اعلام می‌کنیم که این قطعه را به ازای عناصر لیست دریافتی، تکرار و رندر کند.
برای نمونه در مثال فوق می‌خواهیم ردیف‌های جدول تکرار شوند. بنابراین هر ردیف را به عنوان یک قطعه‌ی تکرار شونده‌ی توسط ngFor* مشخص می‌کنیم. به این ترتیب این ردیف و عناصر فرزند آن، به ازای تک تک محصولات موجود در آرایه‌ی products، تکرار خواهند شد.
علامت # در اینجا (product#) یک متغیر محلی را تعریف می‌کند که تنها در قالب جاری قابل استفاده خواهد بود و همچنین فقط در فرزندان tr تعریف شده قابل دسترسی هستند.
به علاوه در اینجا بجای in از of استفاده شده‌است. این of از ES 6 گرفته شده‌است. زمانیکه از حلقه‌ی جدید for...of استفاده می‌شود، متغیر محلی product حاوی یک عنصر از لیست product خواهد بود؛ اما اگر از حلقه‌ی قدیمی for...in استفاده می‌شد، تنها ایندکس عددی این عناصر در دسترس قرار می‌گرفتند. به همین جهت است که در این حلقه، اکنون product.productName به نام محصول آن عنصر آرایه‌ی دریافتی اشاره می‌کند و قابل استفاده است.

تا اینجا اگر برنامه را اجرا کنید، چنین خروجی را مشاهده خواهید کرد:


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: MVC5Angular2.part3.zip


خلاصه‌ی بحث

از inline templateها جهت معرفی قالب‌های کوتاه استفاده می‌شود. در اینجا از "" برای معرفی قالب یک سطری و یا از back tickهای ES 6، برای تعریف قالب‌های چندسطری استفاده خواهد شد. برای قالب‌های مفصل‌تر، بهتر است Linked templateها استفاده شود؛ با پشتیبانی کامل ادیتورهای موجود از لحاظ تکمیل و بررسی کدها.
برای استفاده از یک کامپوننت در کامپوننتی دیگر، نام selector آن‌را به صورت یک المان جدید HTML در قالب دیگری ذکر کرده و سپس با استفاده از خاصیت directives، نام کلاس متناظر با آن‌را نیز ذکر می‌کنیم. همچنین کار import ماژول آن نیز باید در ابتدای فایل صورت گیرد.
جهت غنی سازی قالب‌ها و کامپوننت‌ها و نمایش اطلاعات فقط خواندنی می‌توان از binding یک طرفه‌ی ویژه‌ای به نام interpolation استفاده کرد. کار آن اتصال یک خاصیت عمومی کلاس کامپوننت، به قالب آن است. interpolation توسط {{}} تعریف می‌شود و می‌تواند شامل محاسبات نیز باشد.
همچنین در ادامه‌ی بحث، نحوه‌ی کار با دو دایرکتیو توکار ساختاری AngularJS 2.0 را نیز بررسی کردیم. این دایرکتیوهای ساختاری نیاز است با ستاره شروع شوند و عبارت انتساب داده شده‌ی به آن‌ها باید داخل "" قرار گیرد (برخلاف interpolation که نیازی به اینکار ندارد). از ngIf* برای حذف یا افزودن یک المان و فرزندان آن از/به DOM استفاده می‌شود. اگر عبارت منتسب به آن به true ارزیابی شود، این المان از صفحه حذف خواهد شد. از ngFor* برای تکرار المانی مشخص به همراه فرزندان آن به تعداد اعضای لیستی که برای آن تعیین می‌گردد، استفاده می‌شود. متغیر محلی این پیمایشگر با # مشخص شده و حلقه‌ی آن با of بجای in تعریف می‌شود.
مطالب
آموزش فریم ورک Vuetify قسمت دوم - UI Components بخش اول

 کاربران امروزه با عناصری که به نحوی خاص درون صفحه عمل می‌کنند، آشنا شده‌اند. بنابراین انتخاب مناسب برای اتخاذ این عناصر زمانی مناسب است که به تکمیل کارآیی و رضایت کاربر کمک کند.

یک کامپوننت در واقع از دو بخش تشکیل شده‌است:

1 - اول اینکه چگونه به نظر می‌رسد ( UI ).

2 – دوم اینکه چگونه کار میکند ( UX ).

این عناصر رابط ( component ) شامل :

Input Controls : check boxes, radio buttons, drop down lists, list boxes, buttons, toggles, text fields, date field

Navigational Components : breadcrumb, slider, search field, pagination, slider, tags, icons

Informational Components : tool tips, icons, progress bar, notifications, message boxes, modal windows

Containers : accordion 

اما باید توجه داشت که فقط به این موارد محدود نمی‌شوند.

در این قسمت به طور مختصر با این دست از کامپوننت‌ها ( UI Component ) آشنا می‌شویم. 


کامپوننت v-alert 

کامپوننت V-Alert برای انتقال اطلاعات مهم به کاربر مورد استفاده قرار می‌گیرد. این کامپوننت چهار نوع اطلاعات را به کاربر گوشزد می‌کند که شامل موفقیت‌ها، اطلاعات، هشدارها و خطاها می‌باشد.

هشدارها می‌توانند یک رنگ خاص را داشته باشند که به طور پیش فرض نمایش داده نمی‌شوند. 

در مثال پایین، کامپوننت v-alert شامل دو مقدار است که برای آن تنظیم شده‌است. مقدار (value) که شامل یک مقدار Boolean است و مقدار (type) که مشخص کننده نوع هشدار است (موفقیت ، اطلاعات ، هشدار و خطا).

در قطعه کد پایین، این چهار نوع اطلاعات قابل نمایش به کاربر مشخص شده‌اند:

<div id="app">
  <v-app id="inspire">
    <div>
      <v-alert :value="true" type="success">
        This is a success alert.
      </v-alert>
  
      <v-alert :value="true" type="info">
        This is a info alert.
      </v-alert>
  
      <v-alert :value="true" type="warning">
        This is a warning alert.
      </v-alert>
  
      <v-alert :value="true" type="error">
        This is a error alert.
      </v-alert>
    </div>
  </v-app>
</div>



برای کامپوننت V-Alert می‌توان properties‌های مختلفی را مشخص نمود که از جمله آنها می‌توان به موارد زیر اشاره کرد:

Color : به وسیله این property می‌توان رنگ پیغام را مشخص نمود. هم به وسیله نام رنگ میتوان رنگ مورد نظر را مشخص کرد و هم به وسیله‌ی کد RGB این کار را انجام داد. dismissible : این تنظیم مشخص می‌کند که پیغام، قابلیت بسته شدن را دارد یا خیر که حاوی یک مقدار Boolean است.

icon : مشخص کننده یک نماد خاص است که درون جعبه پیغام قرار می‌گیرد.

type :  مشخص کننده نوع پیام است که پیشتر در مورد آن توضیح داده شد. 


کامپوننت v-avatar 

کامپوننت v-avatar برای تغییر اندازه تصاویر مورد استفاده قرار می‌گیرد که معمولا جهت نمایش عکس پروفایل استفاده می‌شود.

طریقه استفاده :

avatar، دارای یک اندازه‌ی پویا است که می‌تواند برای هر وضعیتی تغییر کند.

برای این کامپوننت سه properties قابل تنظیم است:

color : به وسیله این property می‌توان رنگ دلخواهی را برای آواتار مشخص نمود. هم به وسیله نام رنگ می‌توان رنگ مورد نظر را مشخص کرد و هم به وسیله کد RGB این کار را صورت داد.

size : به طور پیش‌فرض برای avatar، سایز 48 تنظیم شده‌است که می‌توان این میزان را کم و یا زیاد کرد.

tile : همانند border radius در css عمل می‌کند. با تنظیم این گزینه میتوانیم یک آواتار گرد داشته باشیم.


کامپوننت v-badge

به وسیله این کامپوننت می‌توان نمادهایی را برای نمایش اطلاعاتی به کاربر یا جلب توجه کاربر به یک عنصر خاص، ایجاد نمود.

این کامپوننت نیز properties خاص خود را دارد که از جمله آن می‌توان به color , left , mode , overlab  و غیره اشاره کرد.

قطعه کد پایین نشان دهنده چگونگی عملکرد این کامپوننت است:

<div id="app">
  <v-app id="inspire">
    <div>
      <v-badge color="purple" left overlap>
        <template v-slot:badge>
          <v-icon dark small>
            done
          </v-icon>
        </template>
        <v-icon color="grey lighten-1" large>
          account_circle
        </v-icon>
      </v-badge>
        <v-badge overlap color="orange">
        <template v-slot:badge>
          <v-icon dark small>
            notifications
          </v-icon>
        </template>
        <v-icon large color="grey darken-1">
          account_box
        </v-icon>
      </v-badge>
    </div>
  </v-app>
</div>

نتیجه قطعه کد بالا بدین ترتیب است:


کامپوننت v-bottom-nav 

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

به وسیله این کامپوننت امکان انتقال از یک بخش از صفحه به بخشی دیگر امکان پذیر می‌شود. 

این کامپوننت نیز properties خاص خود را دارد که از جمله آن می‌توان به active-sync (برای نشان دادن فعال یا غیر فعال بودن گزینه انتخاب شده)، fixed ( برای مشخص کردن موقعیت کامپوننت در صفحه) و موارد دیگر اشاره کرد.

تقسیم بندی اجزاء این کامپوننت به شرح زیر است:

1 - محل قرار گیری کامپوننت

2- آیکن غیر فعال

3- برچسب غیر فعال

4 - آیکن فعال

5- برچسب فعال

قطعه کد پایین نشان دهنده چگونگی یک bottom navbar است:

<div id="app">
  <v-app id="inspire">
    <v-card height="200px" flat>
      <div>
        Active: {{ bottomNav }}   // 
      </div>
      <v-bottom-nav :active.sync="bottomNav" :value="true" absolute color="transparent">
        <v-btn color="teal" flat value="recent">
          <span>Recent</span>
          <v-icon>history</v-icon>
        </v-btn>
  
        <v-btn color="teal" flat value="favorites">
          <span>Favorites</span>
          <v-icon>favorite</v-icon>
        </v-btn>
  
        <v-btn color="teal" flat value="nearby">
          <span>Nearby</span>
          <v-icon>place</v-icon>
        </v-btn>
      </v-bottom-nav>
    </v-card>
  </v-app>
</div>



کامپوننت v-btn  

این کامپوننت برای ایجاد یک دکمه چه به صورت متن و یا آیکن مورد استفاده قرار میگیرد. دکمه‌ها به کاربران این امکان را می‌دهند تا اقداماتی را انجام دهند و انتخاب‌های خود را تنها با یک کلیک انجام دهند.

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

کامپوننت v-btn نیز مانند سایر کامپوننت‌ها تنظیمات خاص خود را دارد که از جمله آن می‌توان به کوچکی و بزرگی دکمه، فعال یا غیر فعال بودن دکمه، نوع متن یا آیکن بودن دکمه اشاره نمود. 

حالت‌های مختلفی از دکمه‌ها وجود دارند که میتوانند به بهتر شدن UI برنامه ما کمک کنند. برای مثال می‌توان به موارد زیر اشاره کرد:

button drop-down variants : دکمه‌های کرکره‌ای که معمولا برای نظم و کم جا بودن در صفحه مورد استفاده قرار می‌گیرند.

icons : آیکن‌ها می‌توانند برای محتوای اصلی یک دکمه مورد استفاده قرار بگیرند تا ظاهر زیباتری را به دکمه ما بدهند.

floating : این دکمه‌ها حالت آیکن را دارند؛ با این تفاوت که آیکن مورد نظر، درون یک محتوا قرار می‌گیرد.

loaders : به وسیله این دکمه‌ها می‌توان کاربر را متوجه انجام یک پردازش نمود. به صورت پیشفرض بعد از فشردن این نوع دکمه‌ها محتوای دکمه فشرده شده تغییر ظاهر داده و به شکل یک دایره در حال چرخش در می‌آید. البته می‌توان این پیشفرض را به حالت‌های دیگری نیز تغییر داد.

round : این نوع دکمه‌ها دقیقا کارآیی دکمه‌های معمولی را دارند؛ با این تفاوت که این دکمه‌ها دارای لبه‌هایی گرد هستند.

یک نمونه از ایجاد انواع دکمه‌ها در زیر آمده است:

<div id="app">
  <v-app id="inspire">
    <div>
      <v-btn color="success">Success</v-btn>
      <v-btn color="error">Error</v-btn>
      <v-btn color="warning">Warning</v-btn>
      <v-btn color="info">Info</v-btn>
    </div>
  </v-app>
</div>




کامپوننت v-calendar

یکی از کامپوننت‌هایی که به تازگی به vuetify اضافه شده است، کامپوننت تقویم یا v-calendar است. از این کامپوننت برای نمایش تاریخ، روز، هفته، ماه و سال استفاده می‌شود. یک تقویم دارای یک نوع و یک مقدار است که تعیین می‌کند چه نوع تقویمی، در طول چه مدت زمانی نمایش داده شود.

حالت‌های مختلفی برای نمایش تقویم در صفحه وجود دارد که برای مثال می‌توان به موارد زیر اشاره کرد: 

events : به وسیله این گزینه میتوان برای هر روز یک رخداد خاص را مشخص نمود که به وسیله کلیک بر روی آن، اطلاعات آن رخداد نمایش داده شود.

weekly : می‌توان یک تقویم هفتگی را ایجاد نمود و رخدادهای هفتگی را برای آن تنظیم کرد.

نمونه ایجاد یک تقویم در پایین آمده است:

<div id="app">
  <v-app id="inspire">
    <v-layout wrap>
      <v-flex xs12>
        <v-sheet height="500">
          <v-calendar ref="calendar" v-model="start" :type="type" :end="end" color="primary">
          </v-calendar>
        </v-sheet>
      </v-flex>
  
      <v-flex sm4 xs12>
        <v-btn @click="$refs.calendar.prev()">
          <v-icon dark left>
            keyboard_arrow_left
          </v-icon>
          Prev
        </v-btn>
      </v-flex>
      <v-flex sm4 xs12>
        <v-select v-model="type" :items="typeOptions" label="Type">
        </v-select>
      </v-flex>
      <v-flex sm4 xs12>
        <v-btn @click="$refs.calendar.next()">
          Next
          <v-icon right dark>
            keyboard_arrow_right
          </v-icon>
        </v-btn>
      </v-flex>
    </v-layout>
  </v-app>
</div>
js قطعه کد
new Vue({
  el: '#app',
  data: () => ({
    type: 'month', //مشخص کننده نوع تقویم که در اینجا تقویم به صورت ماهانه است
    start: '2019-01-01',
    end: '2019-01-06',
    typeOptions: [
      { text: 'Day', value: 'day' },
      { text: '4 Day', value: '4day' },
      { text: 'Week', value: 'week' },
      { text: 'Month', value: 'month' },
      { text: 'Custom Daily', value: 'custom-daily' },
      { text: 'Custom Weekly', value: 'custom-weekly' }
    ]
  })
})





نظرات مطالب
نکاتی در مورد نوشتن یک مطلب خوب و گیرا در یک سایت
در مورد بند دوم:
اگه بتونید قابلیت ذخیره بصورت پیش نویس رو برای این سایت تعریف کنید خیلی خوب میشه ، اینجوری می‌تونیم بند دوم رو بیشتر رعایت کنیم. + نویسنده باید مطلب در ورد یا برنامه ای دیگه ای تنظیم کنه و بعد از آماده شدن دوباره سبک و فرمت نوشته رو پاک کنه و دوباره داخل ویرایشگر سایت تنظیم کنه.
مهندس جان تجربیات خودتون رو هم در سرعت انتشار مطالب به ما منتقل کنید. خدایش نوشتن  یک یا چند مطلب فنی در روز کار هرکسی نیست.
نظرات مطالب
ساخت ربات تلگرامی با #C
سلام
شما کلا از چند طریق میتوانید  داده‌های خودتون رو به بات ارسال کنید
  1. از طریق تایپ و ارسال عادی متن که سمت سرور می‌توانید متن رو بگیرید و سمت سرور روش شرط بذارید.
  2. از طریق KeyboardButton  که دکمه‌هایی هستند زیر بات میان و می‌توانید با کلیک بر روی هر دکمه، نوشته داخل دکمه رو ارسال کنید که شبیه روش ۱ فقط با دکمه انجام میشه 
  3. ازطریق InlineKeyboardButton که دکمه‌های شیشه‌ای هستند و در محیط چت نمایش داده میشوند که میتوان با کلیک بر روی هر دکمه، مقداری را که پشت پرده ست کردید، بفرستید.
طوری که من سوال شمارو فهمیدم شما با هرکدام از روش‌های بالا داده را به سرور بفرستید داخل سرور باید روی متن رشته‌ای فرستاده شده شرط بذارید و طبق شرطتون داده را مجدد برای کاربر بفرستید.
همچنین میتوانید این مقاله را هم مطالعه نمائید.
مطالب
استفاده از SQL Loader در Oracle
فرض کنید شما یک فایل txt دارید، که درون آن مشخصات نام و نام خانوادگی یک یا چندین میلیون نفر وجود دارد،و از شما خواسته شده، که این اطلاعات را درون جداول مربوطه، در یک دیتابیس Oracle درج نمایید. برای انجام چنین کاری می‌توانید از SQL * Loader در Oracle استفاده نمایید. که بسیار ابزار قدرتمندی میباشد.
موارد ذیل را به ترتیب انجام می‌دهیم:
1- در ابتدا یک فایل متنی به نام  LoaderTable با پسوند txt ایجاد نمایید و مشخصات زیر را درون آن کپی کنید.
1,Ahmad,Mohammadi
2,Farhad,Farahmandkhah
3,Amin,Esapor
4,Reza,shayesteh
5,Maryam,Ebrahimi
6,Farnaz,Akrami    
در اینجا، چون هدف یادگیری می‌باشد،بنابراین تعداد رکوردهای زیادی در نظر گرفته نشده است،اما شما برای تست می‌توانید،هر تعداد رکورد را درون فایل خود قرار دهید.
 2-سپس جدولی با عنوان Testloader ایجاد می‌کنیم،که شامل سه فیلد میباشد1- شناسه 2-نام 3- نام خانوادگی، همانند Script زیر:
Create Table Testloader
(ID int,
FirstName varchar(255),
LastName Varchar(255))
3-در این مرحله فایلی به نام loader با پسوند ctl ایجاد می‌کنیم.درون فایل فوق،اطلاعات زیر را کپی و فایل خود را ذخیره نمایید:
LOAD DATA
INFILE 'c:\LoaderTable.txt'
Insert INTO TABLE Testloader
FIELDS TERMINATED BY ','
optionally enclosed by '"'
TRAILING NULLCOLS
(
ID,
FirstName,
LastName
)
خط  'INFILE 'c:\LoaderTable.txt بیانگر مسیر فایلی میباشد،که می‌خواهیم درون جدول درج نماییم.
خط ',' FIELDS TERMINATED BY بیانگر این مطلب می‌باشد که، بین مقادیر ستونها با کاما (,) جدا شده است. به عبارت دیگر، انتهای مقدار هر ستون به کاما ختم شده است.
خط '"' optionally enclosed by ، این دستور در این مثال کاربردی ندارد،اما مفهومش این است که، محتویاتی را که بین  یک کوتیشن محصور شده اند، به عنوان یک مقدار در نظر بگیرد.
برای درک دستورTRAILING NULLCOLS، مثالی می‌زنم، در جدول فرضی سه ستون داریم، که شامل شناسه،نام و نام خانوادگی است، حال فرض کنید، چنانچه مقادیر هریک از ستونها در فایل تهی یا خالی باشد، Oracle در زمان درج در جدول، آن رکورد را به عنوان Bad Data در فایلی به نام Bad فایل قرار می‌دهد و درون جدول درج نمی‌نماید، برای آنکه چنین مشکلی پیش نیاید، و در صورتی که، خالی بودن مقدار هریک از فیلد‌ها برای شما اهمیتی ندارد، با قرار دادن TRAILING NULLCOLS ، به Oracle می‌فهمانید، چنانچه رکوردی در فایل وجود داشته باشد، و یکی از مقادیر ستونهایش Null  یا خالی باشد، Oracle عملیات درج آن رکورد را ،در جدول انجام دهد.
4- در این مرحله برای درج محتویات، فایل LoaderTable به جدول Testloader خط فرمان زیر را دریک CMD ویندوز کپی نمایید و آن را اجرا کنید.
D:\>sqlldr userid=Username/Password@Servicename data='c:\LoaderTable.txt' control='c:\loader.ctl' log='c:\log.txt' bad='c:\logbad.bad'
به جای Username ، یوزر دیتابیس خود را درج نمایید، به جای Password ، کلمه عبور و به جای ServiceName، نام Servicename ارتباطی با دیتابیس Oracle را درج نمایید.
در پایان باید بگویم، SQL * Loader یک ابزار بسیار قدرتمند در Oracle محسوب می‌شود، و حالتهای بسیار پیشرفته ای در آن وجود دارد، قصد من در این مقاله فقط ،آشنایی و نحوه استفاده از چنین ابزاری بوده است، برای مطالعه بیشتر می‌توانید به دو سایت زیر مراجعه نمایید:
موفق باشید.
 
نظرات مطالب
چک لیست تهیه یک برنامه ASP.NET MVC
- در فرم‌هایی که اطلاعاتی را به سرور Post می‌کنند الزامی است (خصوصا از لحاظ مسایل امنیتی)
- در گزارشات ... «بهتر» است اعمال شود. مثلا با کم کردن تعداد فیلدها به تعدادی که نمایش داده می‌شوند، می‌توان مصرف حافظه برنامه را کاهش داد. شاید یک جدول شما 20 خاصیت داشته باشد و در یک گزارش فقط 5 مورد آن نمایش داده شود. 15 مورد دیگر صرفا مصرف کننده حافظه خواهند شد اگر از viewModel استفاده نکنید. (ولی ... در کل بهتر است اینگونه باشد)