مطالب
کنترل عمومی فایل‌های آپلودی در ASP.NET MVC
در مطلب «محدود کردن کاربر‌ها به آپلود فایل‌هایی خاص در ASP.NET MVC» تصمیم گیری بر اساس یک لیست سفید صورت می‌گیرد. برای مثال کاربران فقط قرار است تصویرهایی از نوع png یا jpg را ارسال کنند. اکنون نیاز است حالت کلی‌تری را درنظر بگیریم که کاربر قرار است هر نوع فایل دلخواهی را ارسال کند. در اینجا نباید امکان آپلود هر نوع فایلی، خصوصا فایل‌های اجرایی ASP.NET یا هر نوع موتور اجرایی دیگری که ممکن است روی سرور نصب باشد (مثلا PHP)، وجود داشته باشد. برای این منظور فیلتر دیگری به نام AllowUploadSafeFiles طراحی شده است که سورس آن‌را در ذیل مشاهده می‌کنید:
using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Web.Mvc;

namespace SecurityModule
{
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public sealed class AllowUploadSafeFilesAttribute : ActionFilterAttribute
    {
        static readonly IList<string> ExtToFilter = new List<string> { 
            ".aspx", ".asax", ".asp", ".ashx", ".asmx", ".axd", ".master", ".svc", ".php" ,        
            ".php3" , ".php4", ".ph3", ".ph4", ".php4", ".ph5", ".sphp", ".cfm", ".ps", ".stm",
            ".htaccess", ".htpasswd", ".php5", ".phtml", ".cgi", ".pl", ".plx", ".py", ".rb", ".sh", ".jsp",
            ".cshtml", ".vbhtml", ".swf" , ".xap", ".asptxt"
        };

        static readonly IList<string> NameToFilter = new List<string> { 
           "web.config" , "htaccess" , "htpasswd", "web~1.con"
        };

        static bool canUpload(string fileName)
        {
            if (string.IsNullOrWhiteSpace(fileName))
                return false;

            fileName = fileName.ToLowerInvariant();
            var name = Path.GetFileName(fileName);
            var ext = Path.GetExtension(fileName);

            if (string.IsNullOrWhiteSpace(name))
                throw new InvalidOperationException("Uploaded file should have a name.");

            return !ExtToFilter.Contains(ext) &&
                   !NameToFilter.Contains(name) &&
                   !NameToFilter.Contains(ext) &&
                   //for "file.asp;.jpg" files
                   ExtToFilter.All(item => !name.Contains(item));
        }

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var files = filterContext.HttpContext.Request.Files;
            foreach (string file in files)
            {
                var postedFile = files[file];
                if (postedFile == null || postedFile.ContentLength == 0) continue;

                if (!canUpload(postedFile.FileName))
                    throw new InvalidOperationException(string.Format("You are not allowed to upload {0} file.", Path.GetFileName(postedFile.FileName)));
            }

            base.OnActionExecuting(filterContext);
        }
    }
}
در این فیلتر، یک سری پسوند خطرناک مانند aspx، asp و امثال آن فیلتر می‌شوند و اجازه آپلود نخواهند یافت. همچنین فایل‌هایی مانند web.config یا نام داسی آن معادل web~1.con نیز فرصت آپلود نخواهد یافت.
استفاده از این فیلتر سفارشی به نحو زیر است:
[AllowUploadSafeFiles]
public ActionResult UploadFile(HttpPostedFileBase file)
مطالب
روش کار با فایل‌های ایستا در برنامه‌های React
روش ذکر فایل‌های ایستا در کامپوننت‌های جاوا اسکریپتی برنامه‌های React

React، برای مدیریت پروژه‌ی خود، از Webpack استفاده می‌کند و در این حالت، کار با فایل‌های ایستا مانند تصاویر و قلم‌های وب، شبیه به کار با فایل‌های CSS خواهد بود؛ یعنی این نوع فایل‌ها را باید در فایل‌های جاوا اسکریپتی برنامه، import کرد. به این ترتیب Webpack کار یکی سازی این فایل‌ها را با bundle نهایی تولید شده، انجام می‌دهد.

یک مثال: فرض کنید فایل button.css به صورت زیر تعریف شده‌است:
.Button {
    padding: 20px;
}

برای استفاده‌ی از این فایل css در یک کامپوننت، ابتدا آن‌را import کرده و سپس از classNameهای تعریف شده‌ی در آن استفاده می‌کنیم:
import React, { Component } from 'react';
import './Button.css'; 

class Button extends Component {
  render() {
    return <div className="Button" />;
  }
}
در حالت توسعه، با هر تغییری در این فایل css، بارگذاری مجدد برنامه به صورت خودکار صورت گرفته و نتیجه‌ی نهایی رندر خواهد شد. در حالت نهایی ارائه‌ی برنامه، تمام فایل‌های css، با هم یکی شده و نگارش minified آن‌ها، به bundle نهایی برنامه اضافه می‌شوند. توصیه شده‌است یک فایل css را در چندین کامپوننت برنامه import نکنید. بهتر است یک کامپوننت دکمه را ایجاد کنید که فایل css مشخصی را import می‌کند و سپس از آن کامپوننت در قسمت‌های مختلف برنامه استفاده کنید.

البته برخلاف حالت کار با CSS imports، با import یک فایل ایستا، یک رشته در اختیار ما قرار می‌گیرد که از آن می‌توان در کدهای خود استفاده کرد. برای کاهش تعداد رفت و برگشت‌های به سرور، اگر فایل‌های تصویری با فرمت‌های bmp, gif, jpg, jpeg و  png، کمتر از 10,000 بایت باشند، از data URI آن‌ها بجای مسیر نهایی استفاده خواهد شد. این مورد شامل فایل‌های svg نمی‌شود.

یک مثال:
import React from 'react';
import logo from './logo.svg'; 

console.log(logo);

function Header() {
  return <img src={logo} alt="Logo" />;
}

export default Header;
در اینجا ابتدا یک فایل تصویری، import شده‌است. سپس می‌توان از رشته‌ی متناظر با آن، به عنوان src المان تصویر استفاده کرد.

این مورد برای تصاویر ذکر شده‌ی در فایل‌های CSS نیز صادق است:
.Logo {
    background-image: url(./logo.png);
}
Webpack در فایل‌های CSS به دنبال مسیرهای فایل‌های ثابت شروع شده‌ی با /. می‌گردد و مسیرهای این نوع فایل‌ها را با مسیر نهایی ارائه‌ی برنامه، به صورت خودکار جایگزین و اصلاح می‌کند. همچنین نیازی هم به نگرانی در مورد کش شدن این فایل‌های ثابت وجود ندارد؛ چون webpack از نام‌های خاصی به همراه hash این نوع فایل‌ها، برای جایگزینی نهایی استفاده خواهد کرد و اگر محتوای فایل‌های ثابت تغییر کنند، این هش نیز تغییر کره و مرورگر نگارش جدید آن‌ها را دریافت می‌کند. مزیت دیگر کار با webpack، ارائه‌ی خطاهای زمان کامپایل برنامه است. برای مثال اگر فایل‌های ثابت مورد استفاده‌ی در برنامه به درستی مسیر دهی نشده باشند، یک خطای زمان کامپایل صادر می‌شود.

یک مثال: فرض کنید در برنامه‌ی ASP.NET Core خود که با React یکی شده‌است، فایل project_folder/ClientApp/src/images/progress_bar.gif را قرار داده‌اید. روش import آن با توجه به مسیرهای نسبی برنامه به صورت زیر است:
import progressBar from '../images/progress_bar.gif';
و روش فراخوانی آن در کدهای یک کامپوننت به نحو زیر خواهد بود:
<img alt="loading..." src={progressBar} />


روش ذکر فایل‌های ایستا در کامپوننت‌های تایپ اسکریپتی برنامه‌های React

اگر از تایپ‌اسکریپت استفاده می‌کنید، چنین importهایی سبب بروز خطای «'Cannot find module './logo.png» می‌شوند. برای رفع این مشکل، فایلی را به نام assets.d.ts به پروژه‌ی خود اضافه کرده و آن‌را به صورت زیر تکمیل کنید:
declare module "*.gif";
declare module "*.jpg";
declare module "*.jpeg";
declare module "*.png";
declare module "*.svg";
به این ترتیب پسوند‌های فایل‌های مختلف ایستا، به عنوان فرمت‌های مجاز ماژول‌ها، قابل استفاده می‌شوند.


نحوه‌ی پردازش پوشه‌ی ویژه‌ی public در برنامه‌های React

اگر فایلی در پوشه‌ی ویژه‌ی public برنامه‌های react قرار گیرد، توسط webpack پردازش نخواهد شد. در این حالت این نوع فایل‌ها بدون هیچ نوع تغییری به پوشه‌ی build نهایی کپی می‌شوند. بنابراین برای کار با فایل‌های ایستای قرار گرفته‌ی در پوشه‌ی public باید از متغیر خاصی به نام PUBLIC_URL استفاده کرد. برای مثال درون فایل index.html، چنین تعریفی را می‌توان مشاهده کرد:
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
اگر نیاز به استفاده‌ی از فایلی درون پوشه‌ی src و یا node_modules دارید، باید آن‌ها را به این پوشه کپی کنید تا جزئی از پروسه‌ی build شوند. متغیر PUBLIC_URL در زمان اجرای دستور npm run build، با مسیر صحیحی جایگزین خواهد شد.
برای دسترسی به این مسیر در کامپوننت‌های برنامه نیز می‌توان از متغیر محیطی process.env.PUBLIC_URL استفاده کرد:
render() {
    return <img src={process.env.PUBLIC_URL + '/img/logo.png'} />;
}
البته روش توصیه شده، همان ذکر importهای فایل‌های ایستا است و بهتر است از این متغیر محیطی زیاد استفاده نکنید؛ چون پردازش ثانویه‌ای بر روی آن‌ها صورت نمی‌گیرد و یا minified نمی‌شوند. نبود آن‌ها سبب بروز خطای زمان کامپایل نخواهد شد و همچنین هیچ hash ای به نام نهایی آن‌ها به صورت خودکار اضافه نمی‌گردد که ممکن است سبب بروز مشکلات کش شدن طولانی مدت این فایل‌های ایستا شود.

بنابراین اکنون این سؤال مطرح می‌شود که چه زمانی بهتر است از پوشه‌ی public استفاده شود؟
- اگر می‌خواهید نام فایل نهایی ایستای مدنظر مانند manifest.json، بدون تغییر باقی بماند.
- هزاران فایل ایستا را دارید و می‌خواهید این مسیرها را به صورت پویا در برنامه فراخوانی کنید (و قرار نیست جزئی از bundle نهایی شوند).
- می‌خواهید فایل‌های js خاصی را خارج از سیستم bundle اصلی قرار دهید؛ چون به هر دلیلی این نوع فایل‌ها با سیستم webpack سازگاری ندارند و نباید توسط آن پردازش شوند. در این حالت باید این نوع فایل‌ها را با تگ script به فایل index.html به صورت دستی معرفی کنید.
مطالب
C# 6 - Index Initializers
زمان زیادی از ارائه‌ی امکان Collection Initializer برای ایجاد یک متغیر از نوع Collection می‌گذرد؛ برای نمونه به مثال زیر توجه کنید:
enum USState {...}
var AreaCodeUSState = new Dictionary<string, USState>
    {
        {"408", USState.California},
        {"701", USState.NorthDakota},
        ...
    };
در پشت صحنه، کامپایلر، Collection Initializer را می‌گیرد، با استفاده از یک <Dictionary<TKey, TValue و با فراخوانی متد Add آن بر روی لیست Collection Initializer شروع به درج آن در دیکشنری ساخته شده می‌کند. Collection Initializer فقط بر روی کلاس هایی که در آن‌ها IEnumerable پیاده سازی شده باشد امکان پذیر است چرا که کامپایلر کار اضافه کردن مقادیر اولیه را به ()IEnumerable.Add می‌سپارد.

اکنون در C# 6.0 ما می‌توانیم از Index Initializer استفاده کنیم:
enum USState {...}
var AreaCodeUSState = new Dictionary<string, USState>
    {
        ["408"] = USState.California,
        ["701"] = USState.NorthDakota,
        ...
    };
اولین تفاوتی که این دو روش با هم دارند این است که در حالت استفاده‌ی از Index Initializer پس از کامپایل، ()IEnumerable.Add فراخوانی نمی‌شود. این تفاوت بسیار مهم است و کار اضافه کردن مقادیر اولیه را با استفاده از کلید (Key) ویژه انجام می‌دهد.
شبه کد مثال بالا به صورت زیر می‌شود:

Collection Initializer
create a Dictionary<string, USState>
add to new Dictionary the following items: 
     "408", USState.California
     "701", USState.NorthDakota
Index Initializer
create a Dictionary<string, USState> then
using AreaCodeUSState's default Indexed property
    set the Value of Key "408" to USState.California
    set the Value of Key "701" to USState.NorthDakota
حال به مثال زیر توجه کنید:

Collection Initializer 
enum USState {...}
var AreaCodeUSState = new Dictionary<string, USState>
        {
            { "408", USState.Confusion},
            { "701", USState.NorthDakota },
            { "408", USState.California},
            ...
        };
Console.WriteLine( AreaCodeUSState.Where(x => x.Key == "408").FirstOrDefault().Value );
Index Initializer
enum USState {...}
var AreaCodeUSState = new Dictionary<string, USState>
    {
        ["408"] = USState.Confusion,
        ["701"] = USState.NorthDakota,
        ["408"] = USState.California,
        ...
    };
Console.WriteLine( AreaCodeUSState2.Where(x => x.Key == "408").FirstOrDefault().Value );  // output = California
هر دو کد بالا با موفقیت کامپایل و اجرا می‌شود، اما در زمان اجرای Collection Initializer هنگامیکه می‌خواهد مقدار دوم "408" را اضافه کند با استثناء ArgumentException متوقف می‌شود چرا که کلید "408" از قبل وجود دارد.
اما در زمان اجرا، Index Initializer به صورت کامل و بدون خطا این کار را انجام می‌دهد و در کلید "408" مقدار USState.Confusion قرار می‌گیرد. سپس "701" مقدار USState.NorthDakota و بعد از استفاده‌ی مجدد از کلید "408" مقدار USState.California جایگزین مقدار قبلی می‌شود.

var fibonaccis = new List<int>
    {
        [0] = 1,
        [1] = 2,
        [3] = 5,
        [5] = 13
    }
این کد هم معتبر است و هم کامپایل می‌شود. البته معتبر است، ولی صحیح نیست. <List<T اجازه‌ی تخصیص اندیسی فراتر از اندازه‌ی فعلی را نمی‌دهد.
تلاش برای تخصیص مقدار 1 با کلید 0 به <List<int، سبب بروز استثناء ArguementOutOfRangeException می شود. وقتی (List<T>.Add(item فراخوانی می‌شود اندازه‌ی لیست یک واحد افزایش می‌یابد. بنابراین باید دقت داشت که Index Initializer از ()Add. استفاده نمی‌کند؛ در عوض با استفاده از خصوصیت اندیس پیش فرض، مقداری را برای یک کلید تعیین می‌کند.
برای چنین حالتی بهتر است از همان روش قدیمی Collection Initializer استفاده کنیم:
var fibonaccis = new List<int>()
    {
        1,
        3,
        5,
        13
    };
مطالب
نحوه نمایش تمام آیکون‌های تعریف شده در یک قلم در WPF
سال نو مبارک! به امید روزهایی شاد، سلامت و پر برکت.

پیرو مطلب قلم‌هایی حاوی آیکون که خصوصا در برنامه‌های مترو بیشتر مرسوم شده‌اند، شاید بد نباشد کار برنامه Character Map ویندوز را با WPF شبیه سازی کنیم.
ابتدا Model و ViewModel  این برنامه را درنظر بگیرید:
namespace CrMap.Models
{
    public class Symbol
    {
        public char Character { set; get; }
        public string CharacterCode { set; get; }
    }
}

using System;
using System.Collections.Generic;
using System.Windows.Media;
using CrMap.Models;

namespace CrMap.ViewModels
{
    public class CrMapViewModel
    {
        public IList<Symbol> Symbols { set; get; }
        public int GridRows { set; get; }
        public int GridCols { set; get; }

        public CrMapViewModel()
        {
            fillDataSource();
        }

        private void fillDataSource()
        {
            Symbols = new List<Symbol>();
            GridCols = 15;

            var fontFamily = new FontFamily(new Uri("pack://application:,,,/"), "/Fonts/#whhglyphs");

            GlyphTypeface glyph = null;
            foreach (var typeface in fontFamily.GetTypefaces())
            {
                if (typeface.TryGetGlyphTypeface(out glyph) && (glyph != null))
                    break;
            }

            if (glyph == null)
                throw new InvalidOperationException("Couldn't find a GlyphTypeface.");

            GridRows = (glyph.CharacterToGlyphMap.Count / GridCols) + 1;

            foreach (var item in glyph.CharacterToGlyphMap)
            {
                var index = item.Key;
                Symbols.Add(new Symbol
                {
                    Character = Convert.ToChar(index),
                    CharacterCode = string.Format("&#x{0:X}", index)
                });
            }
        }
    }
}
توضیحات:

یک سری قابلیت جالب در WPF برای استخراج اطلاعات قلم‌ها وجود دارند که در فضای نام System.Windows.Media اسمبلی PresentationCore.dll قرار گرفته‌اند. برای نمونه پس از وهله سازی FontFamily بر اساس یک قلم مدفون شده در برنامه، امکان استخراج تعداد گلیف‌های موجود در این قلم وجود دارد که نحوه انجام آن‌را در متد fillDataSource ملاحظه می‌کنید.
این اطلاعات استخراج شده و لیست Symbols برنامه را تشکیل می‌دهند. در نهایت برای نمایش این اطلاعات، از ترکیب ItemsControl و UniformGrid استفاده خواهیم کرد:
<Window x:Class="CrMap.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="clr-namespace:CrMap.ViewModels"
        Title="MainWindow" WindowStartupLocation="CenterScreen" WindowState="Maximized"
        Height="350" Width="525">
    <Window.Resources>
        <vm:CrMapViewModel x:Key="vmCrMapViewModel" />
    </Window.Resources>
    <ScrollViewer VerticalScrollBarVisibility="Visible">
        <ItemsControl
                      DataContext="{StaticResource vmCrMapViewModel}"  
                      ItemsSource="{Binding Symbols}"
                      Name="MainItemsControl"
                      VerticalAlignment="Top"     
                      HorizontalAlignment="Center"
                      Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="4">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <UniformGrid
                        HorizontalAlignment="Center"
                        VerticalAlignment="Center"
                        Columns="{Binding GridCols}" 
                        Rows="{Binding GridRows}">
                    </UniformGrid>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <ContentControl>
                        <Border BorderBrush="SlateGray"
                            HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                            BorderThickness="1" CornerRadius="3" Margin="3">
                            <StackPanel Margin="3" Orientation="Vertical">
                                <TextBlock                                         
                                    HorizontalAlignment="Center" 
                                    VerticalAlignment="Center" 
                                    FontFamily="Fonts/#whhglyphs"
                                    Foreground="DarkRed"
                                    FontSize="17"
                                    Text="{Binding Character}" />
                                <TextBlock                                         
                                    HorizontalAlignment="Center" 
                                    VerticalAlignment="Center" 
                                    Text="{Binding CharacterCode}" />
                            </StackPanel>
                        </Border>
                    </ContentControl>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </ScrollViewer>
</Window>


دریافت مثال این مطلب
CrMap.zip
 
اشتراک‌ها
سایت Material UI Colors

Google's material ui color palette comprises primary and accent colors that can be used for illustration or to develop your brand colors. 

سایت Material UI Colors
مطالب
فرم‌های مبتنی بر قالب‌ها در Angular - قسمت دوم - ایجاد اولین فرم
در قسمت قبل، مروری داشتیم بر تفاوت‌های دو نوع مختلف فناوری‌های ایجاد و مدیریت فرم‌ها در Angular و هچنین ساختار ابتدایی برنامه‌ی این سری را ایجاد کردیم. در ادامه، اولین فرم مبتنی بر قالب‌ها را ایجاد خواهیم کرد.


ایجاد اولین فرم مبتنی بر قالب‌ها

پس از ایجاد کامپوننت employee-register، فایل قالب آن یا src\app\employee\employee-register\employee-register.component.html را گشوده و به نحو ذیل تکمیل می‌کنیم:
<h3>Angular Forms</h3>
<form #form="ngForm">
    <input type="text" placeholder="Name">
    <button type="submit">Ok</button>
</form>

form.pristine: {{ form.pristine }}
زمانیکه المان فرم به صفحه اضافه می‌شود، Angular به صورت خودکار دایرکتیو مرتبطی را به فرم اضافه می‌کند. برای دسترسی به این دایرکتیو نیاز است یک template reference variable را تعریف کرد. برای مثال "form="ngForm#  به معنای تعریف متغیر form است که به دایرکتیو توکار ngForm متصل شده‌است و اکنون حاوی وهله‌ای از این دایرکتیو می‌باشد. به همین جهت است که امکان دسترسی به اطلاعات این وهله توسط درج form.pristine در همان قالب وجود دارد.
خاصیت pristine مشخص می‌کند که آیا فرم توسط کاربر تغییر یافته‌است یا خیر؟


مقدار خاصیت pristine در ابتدای کار true است؛ به این معنا که هنوز تغییری در آن اعمال نشده‌است.

یک نکته: ممکن است در حین توسعه‌ی برنامه، خطای ذیل را در کنسول developer tools مرورگرها مشاهده کنید:
 There is no directive with "exportAs" set to "ngForm"
این دایرکتیو تنها زمانی قابل دسترسی است که در قسمت imports ماژول جاری که با آن کار می‌کنید، تعریف FormsModule را به همان نحوی که در انتهای قسمت قبل بررسی کردیم (قسمت «افزودن ماژول فرم‌ها به برنامه»)، افزوده باشید.

در ادامه، در همین فرمی که تعاریف آن‌را در بالا مشاهده می‌کنید، اطلاعاتی را وارد نمائید. هنوز هم مقدار خاصیت pristine مساوی true است. علت اینجا است که هنوز به Angular اعلام نکرده‌ایم که کدام فیلد یا فیلدهای فرم را باید تحت نظر قرار دهد. برای این منظور ابتدا به المان تعریف شده نامی را انتساب داده و سپس دایرکتیو ngModel را نیز به انتهای تعاریف آن اضافه می‌کنیم:
<h3>Angular Forms</h3>
<form #form="ngForm">
    <input type="text" placeholder="Name" name="name" ngModel>
    <button type="submit">Ok</button>
</form>

form.pristine: {{ form.pristine }}

اکنون اگر مقدار فرم را تغییر دهیم، مشاهده خواهیم کرد که مقدار خاصیتpristine  به false تغییر می‌کند:


یک نکته: زمانیکه دایرکتیو ngModel ذکر می‌شود، تعریف name المان متناظر با آن، الزامی است؛ در غیراینصورت خطای ذیل را در کنسول developer tools مرورگرها مشاهده خواهید کرد:
 Error: If ngModel is used within a form tag, either the name attribute must be set or the form
control must be defined as 'standalone' in ngModelOptions.


خاموش کردن اعتبارسنجی توکار مرورگرها

یکی از کارهایی را که نیاز است در حین کار با فرم‌ها انجام داد، خاموش کردن اعتبارسنجی توکار مرورگرها است. فرض کنید ویژگی معتبر و استاندارد required را به یکی از المان‌های ورودی اضافه کرده‌اید:
<input type="text" required placeholder="Name" name="name" ngModel>
در این حالت اگر برنامه را اجرا کنید و بدون تکمیل این فیلد بر روی دکمه‌ی ارسال فرم کلیک نمائید، به ازای مرورگرهای مختلف، پیام انگلیسی «لطفا این فیلد را تکمیل کنید» ظاهر خواهد شد و هر کدام شکل متفاوتی را دارند که جزئیات آن‌ها را نمی‌توان تغییر داد و یا سفارشی سازی کرد. به این مورد، browser validation می‌گویند. به همین جهت برای خاموش کردن این اعتبارسنجی توکار مرورگرها و ارائه‌ی تجربه‌ی کاربری یکنواخت و یکدستی در تمام مرورگرها، نیاز است ویژگی novalidate را به تگ فرم اضافه کرد:
<form #form="ngForm" novalidate>
هر دوی ویژگی‌های novalidate و یاrequired ، جزو استاندارد HTML هستند و ارتباطی به Angular ندارند.


بهبود ظاهر فرم توسط اعمال شیوه‌نامه‌های بوت استرپ

در قسمت قبل، در ابتدای کار تدارک ساختار مثال این سری، بوت استرپ را نیز نصب و تنظیم کردیم. در ادامه می‌خواهیم اندکی ظاهر این فرم را بر اساس شیوه‌نامه‌های بوت استرپ بهبود ببخشیم:
<div class="container">
    <h3>Angular Forms</h3>
    <form #form="ngForm" novalidate>
        <div class="form-group">
            <label>First Name</label>
            <input type="text" class="form-control" required name="firstName" ngModel>
        </div>
        <div class="form-group">
            <label>Last Name</label>
            <input type="text" class="form-control" required name="lastName" ngModel>
        </div>
        <button class="btn btn-primary" type="submit">Ok</button>
    </form>
</div>

form.pristine: {{ form.pristine }}


- برای افزودن بوت استرپ نیازی نیست تا شیوه‌نامه‌ی آن‌را به صورت دستی به Index.html برنامه اضافه کرد. همینقدر که ارجاعی از آن در فایل angular-cli.json. در قسمت شیوه‌نامه‌های آن وجود داشته باشد، به صورت خودکار در bundle نهایی تولید شده‌ی توسط سیستم ساخت برنامه‌ی Angular CLI ظاهر خواهد شد.
- در اینجا ابتدا فرم خود را در داخل یک container قرار داده‌ایم. این مورد سبب می‌شود تا محتوای آن به میانه‌ی صفحه منتقل شود.
- سپس شیوه‌نامه‌ی btn به دکمه‌ی ارسال فرم اضافه شده‌است تا شکل دکمه‌های بوت استرپ را پیدا کند.
- سپس هر فیلد ورودی داخل یک div با کلاس form-group محصور می‌شود و هر کنترل، کلاس form-control را خواهد یافت.


افزودن سایر المان‌های ورودی به فرم

تا اینجا دو text box را به فرم اضافه کرده‌ایم. در ادامه می‌خواهیم المان‌های دیگری را نیز تعریف کنیم:

افزودن Check boxes

        <div class="checkbox">
            <label>
                <input type="checkbox" name="is-full-time" ngModel> Full Time Employee
            </label>
        </div>
چون با بوت استرپ کار می‌کنیم، نیاز است المان ورودی از نوع checkbox را داخل یک div با کلاس checkbox محصور کنیم. سپس یک label را تعریف کرده و Input را داخل آن قرار دهیم. در اینجا نیز همانند سایر المان‌ها نیاز است نامی را به آن انتساب داده و سپس دایرکتیو ngModel را قید نمود تا Angular این کنترل را تحت نظر قرار دهد.


افزودن Radio buttons

        <label>Payment Type</label>
        <div class="radio">
            <label>
                <input type="radio" name="pay-type" value="FullTime" checked>
                Full Time
            </label>
        </div>
        <div class="radio">
            <label>
                <input type="radio" name="pay-type" value="PartTime">
                Part Time
            </label>
        </div>
Radio buttons نیز شبیه به Check boxها تعریف می‌شوند. در اینجا نیز یک div با کلاس radio و سپس label ایی که المان ورودی از نوع radio داخل آن قرار می‌گیرد، افزوده خواهد شد. فقط در اینجا باید دقت داشت که گروه بندی این المان‌ها بر اساس نام آن‌ها انجام می‌شود. به همین جهت است که نام این دو المان یکی وارد شده‌است. همچنین باید value آن‌را نیز تنظیم کرد. این مقداری است که در نهایت به سرور ارسال خواهد شد.


افزودن Drop downs

        <div class="form-group">
            <label>Primary Language</label>
            <select class="form-control">
                <option *ngFor="let lang of languages">
                    {{ lang }}
                </option>
            </select>
        </div>
در اینجا از المان select برای تشکیل یک drop down استفاده می‌کنیم و نحوه‌ی تعریف آن بسیار شبیه است به تعریف text boxهایی که داخل form-group محصور شده و همچنین کلاس form-control را پیدا می‌کنند.
اما قسمت مهم آن، اطلاعاتی است که قرار است در این drop down نمایش داده شوند. این اطلاعات را می‌توان از آرایه‌ی languages گرفت و سپس توسط یک ngFor به المان select اضافه کرد. بنابراین باید به فایل employee-register.component.ts مراجعه کرده و آرایه‌ی languages را به آن افزود:
export class EmployeeRegisterComponent implements OnInit {
  languages = ["Persian", "English", "Spanish", "Other"];
کاری که در اینجا انجام می‌شود، تکرار المان option توسط ngFor است. برای مثال در اینجا 4 بار المان option توسط عناصر آرایه‌ی زبان‌ها در داخل المان select تکرار خواهد شد. به عبارتی select نهایی رندر شده‌ی در صفحه، چنین شکلی را پیدا می‌کند:
<select class="form-control">
  <option>Persian</option>
  <option>English</option>
  <option>Spanish</option>
  <option>Other</option>
</select>

تا اینجا فرم تشکیل شده‌ی ما چنین نمایی را پیدا می‌کند:


در قسمت بعد این فرم را توسط مباحث data binding و بررسی نحوه‌ی دسترسی به اطلاعات آن در کامپوننت مرتبط، تکمیل خواهیم کرد.



کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: angular-template-driven-forms-lab-02.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کرده‌اید. سپس از طریق خط فرمان به ریشه‌ی پروژه وارد شده و دستور npm install را صادر کنید تا وابستگی‌های آن دریافت و نصب شوند. در آخر با اجرای دستور ng serve -o برنامه ساخته شده و در مرورگر پیش فرض سیستم نمایش داده خواهد شد.
مطالب
ارتباط بین Controller و Directive در AngularJs
در پست قبلی با کلیات مفاهیم دیرکتیو‌ها آشنا شدید. در این پست قصد داریم برخی توابع  کنترلرهای تعریف شده در Angular را به وسیله دیرکتیو‌های تعریف شده در ماژول فراخوانی نماییم. در ادامه این موضوع را طی یک مثال بررسی خواهیم کرد.
ابتدا View مورد نظر را به صور زیر ایجاد می‌کنیم:
<script type="text/javascript" src="~/scripts/Modules/module4.js"></script>

<div ng-app="myApp">
    <div ng-controller="myCtrl">
        <span enter>Load More Books</span>
    </div>
</div>
برنامه به این صورت است که با ورود نشانگر ماوس بر روی تگ span (فراخوانی رویداد mouseenter برای تگ هایی که دارای دیرکتیو enter باشند) یک تابع به نام loadMoreBook در کنترلر myCtrl فراخوانی می‌شود.
بک فایل جاوااسکریپتی به نام myModule بسازید و ماژول مورد نظر را ایجاد نمایید:
var app = angular.module('myApp', []);
کنترلر مورد نظر به همراه تابع loadMoreBook را به صورت زیر ایجاد می‌کنیم(البته در اینجا به جای لود واقعی داده از یک alert استفاده کردم):
app.controller('myCtrl', function ($scope) {
    $scope.loadMoreBook = function () {
        alert('Loading Books...');
    }
});
حال نوبت به دیرکتیو مورد بحث می‌رسد که به صورت زیر ایجاد می‌شود:
app.directive('enter', function () {
    return function (scope, element) {
        element.bind('mouseenter', function () {
            scope.loadMoreBook();
        })        
    }
});
اولین نکته این است که به در تابع سازنده دیرکتیو به جای برگشت آبجک مورد نظر یک تابع برگشت داه می‌شود. برای اینکه بتوان به توابع کنترلر محصور کننده دیرکتیو دسترسی داشت آرگومان اول تابع معادل scope مورد استفاده در کنترلر خواهد بود. آرگومان دوم معادل المانی است که  دارای دیرکتیو enter است. در این تابع ابتدا برای رویداد mouseenter رویدادگردان آن پیاده سازی شده است که در آن تابع loadMoreBook کنترلر مورد نظر فراخوانی می‌شود.
خروجی


حال فرض بر این است که در کنترلر بالا تابع دیگری به نام loadMoreAuthor برای فراخوانی نویسندگان نیز وجود دارد. به صورت زیر:
app.controller('myCtrl', function ($scope) {
    $scope.loadMoreBook = function () {
        alert('Loading Books...');
    }

    $scope.loadMoreAuthor = function () {
        alert('Loading Authors...');
    }
});
اما برای انعطاف پذیری بیشتر برنامه، قصد داریم دیرکتیو بالا را به گونه ای تغییر دهیم که نام تابع مورد نظر در کنترلر را به عنوان مقدار یک ویژگی دریافت کند. به صورت زیر:

<script type="text/javascript" src="~/scripts/Modules/module4.js"></script>

<div ng-app="myApp">
    <div ng-controller="myCtrl">
        <span enter="loadMoreBook()">Load More Book</span>         
        <hr>
       <span enter="loadMoreAuthor()">Load More Author</span>
    </div>
</div>
برای به دست آوردن مقدار دیرکتیوی که به عنوان ویژگی در المان تعیین شده، باید از آرگومان سوم در تابع سازنده دیرکتیو به صورت زیر استفاده کرد.
app.directive('enter', function () {
    return function (scope, element , attrs) {
        element.bind('mouseenter', function () {
            scope.$apply(attrs.enter);
        })        
    }   
});
در  کد‌های بالا، برای اینکه بتوان بر اساس نام یک تابع آن را فراخوانی کرد، از سرویس apply$ که به صورت توکار در angular تعبیه شده است استفاده کردم. برای به دست آوردن نام تابع، باید از آرگومان سوم تابع (attrs) به همراه نام دیرکتیو استفاده کرد. به دلیل اینکه نام دیرکتیو enter است باید پارامتر سرویس apply$ به صورت attrs.enter باشد. خروجی نیز مانند حالت قبل خواهد بود.
مطالب
پیدا کردن آیتم‌های تکراری در یک لیست به کمک LINQ

گاهی از اوقات نیاز می‌شود تا در یک لیست، آیتم‌های تکراری موجود را مشخص کرد. به صورت پیش فرض متد Distinct برای حذف مقادیر تکراری در یک لیست با استفاده از LINQ موجود است که البته آن‌هم اما و اگرهایی دارد که در ادامه به آن پرداخته خواهد شد، اما باز هم این مورد پاسخ سؤال اصلی نیست (نمی‌خواهیم موارد تکراری را حذف کنیم).

برای حذف آیتم‌های تکراری از یک لیست جنریک می‌توان متد زیر را نوشت:
public static List<T> RemoveDuplicates<T>(List<T> items)
{
return (from s in items select s).Distinct().ToList();
}
برای مثال:
public static void TestRemoveDuplicates()
{
List<string> sampleList =
new List<string>() { "A1", "A2", "A3", "A1", "A2", "A3" };
sampleList = RemoveDuplicates(sampleList);
foreach (var item in sampleList)
Console.WriteLine(item);
}
این متد بر روی لیست‌هایی با نوع‌های اولیه مانند string‌ و int و امثال آن درست کار می‌کند. اما اکنون مثال زیر را در نظر بگیرید:
public class Employee
{
public int ID { get; set; }
public string FName { get; set; }
public int Age { get; set; }
}

public static void TestRemoveDuplicates()
{
List<Employee> lstEmp = new List<Employee>()
{
new Employee(){ ID=1, Age=20, FName="F1"},
new Employee(){ ID=2, Age=21, FName="F2"},
new Employee(){ ID=1, Age=20, FName="F1"},
};

lstEmp = RemoveDuplicates<Employee>(lstEmp);

foreach (var item in lstEmp)
Console.WriteLine(item.FName);
}
اگر متد TestRemoveDuplicates را اجرا نمائید، رکورد تکراری این لیست جنریک حذف نخواهد شد؛ زیرا متد distinct بکارگرفته شده نمی‌داند اشیایی از نوع کلاس سفارشی Employee را چگونه باید با هم مقایسه نماید تا بتواند موارد تکراری آن‌ها را حذف کند.
برای رفع این مشکل باید از آرگومان دوم متد distinct جهت معرفی وهله‌ای از کلاسی که اینترفیس IEqualityComparer را پیاده سازی می‌کند، کمک گرفت.
public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer);
که نمونه‌ای از پیاده سازی آن به شرح زیر می‌تواند باشد:

public class EmployeeComparer : IEqualityComparer<Employee>
{
public bool Equals(Employee x, Employee y)
{
//آیا دقیقا یک وهله هستند؟
if (Object.ReferenceEquals(x, y)) return true;

//آیا یکی از وهله‌ها نال است؟
if (Object.ReferenceEquals(x, null) ||
Object.ReferenceEquals(y, null))
return false;

return x.Age == y.Age && x.FName == y.FName && x.ID == y.ID;
}

public int GetHashCode(Employee obj)
{
if (Object.ReferenceEquals(obj, null)) return 0;
int hashTextual = obj.FName == null ? 0 : obj.FName.GetHashCode();
int hashDigital = obj.Age.GetHashCode();
return hashTextual ^ hashDigital;
}
}
اکنون اگر یک overload برای متد RemoveDuplicates با درنظر گرفتن IEqualityComparerتهیه کنیم، به شکل زیر خواهد بود:
public static List<T> RemoveDuplicates<T>(List<T> items, IEqualityComparer<T> comparer)
{
return (from s in items select s).Distinct(comparer).ToList();
}
به این صورت متد آزمایشی ما به شکل زیر (که وهله‌ای از کلاس EmployeeComparer‌ به آن ارسال شده) تغییر خواهد کرد:
public static void TestRemoveDuplicates()
{
List<Employee> lstEmp = new List<Employee>()
{
new Employee(){ ID=1, Age=20, FName="F1"},
new Employee(){ ID=2, Age=21, FName="F2"},
new Employee(){ ID=1, Age=20, FName="F1"},
};

lstEmp = RemoveDuplicates(lstEmp, new EmployeeComparer());

foreach (var item in lstEmp)
Console.WriteLine(item.FName);
}
پس از این تغییر، حاصل این متد تنها دو رکورد غیرتکراری می‌باشد.

سؤال: برای یافتن آیتم‌های تکراری یک لیست چه باید کرد؟
احتمالا مقاله "روش‌هایی برای حذف رکوردهای تکراری" را به خاطر دارید. اینجا هم می‌توان کوئری LINQ ایی را نوشت که رکوردها را بر اساس سن، گروه بندی کرده و سپس گروه‌هایی را که بیش از یک رکورد دارند، انتخاب نماید.
public static void FindDuplicates()
{
List<Employee> lstEmp = new List<Employee>()
{
new Employee(){ ID=1, Age=20, FName="F1"},
new Employee(){ ID=2, Age=21, FName="F2"},
new Employee(){ ID=1, Age=20, FName="F1"},
};

var query = from c in lstEmp
group c by c.Age into g
where g.Count() > 1
select new { Age = g.Key, Count = g.Count() };

foreach (var item in query)
{
Console.WriteLine("Age {0} has {1} records", item.Age, item.Count);
}
}


Vote on iDevCenter