نظرات مطالب
احراز هویت و اعتبارسنجی کاربران در برنامه‌های Angular - قسمت ششم - کار با منابع محافظت شده‌ی سمت سرور
Http Module منسوخ شده‌است (و زمانیکه ماژولی منسوخ شده اعلام می‌شود، یعنی در چند نگارش بعد، از کل مجموعه حذف خواهد شد). این سری از Http Client استفاده می‌کند که مطلب روش «ارتقاء به HTTP Client در Angular 4.3» را در این مورد می‌توانید مطالعه کنید؛ به همراه مطلب «ارتقاء به Angular 6: بررسی تغییرات RxJS». مباحث Interceptors توکار مورد استفاده‌ی در اینجا، برای Http Client آن وجود خارجی دارد. برای Http Module منسوخ شده‌، روش «راه‌اندازی Http Interceptor در Angular» وجود داشت.
نظرات مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت چهارم - data binding
اگر در استفاده از Two way databinding با پیغام خطای "Can't bind to 'ngModel' since it isn't a known property of 'input'. " روبرو شدید، حتما در ماژول برنامه، ماژول فرمها را ایمپورت کنید.
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
نظرات مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت نهم - مسیریابی
توضیح «... البته باید دقت داشت که فایل systemjs.config.js هم کمی نیاز است جهت بارگذاری این مسیریاب جدید اصلاح شود ...»:
علت اینجا است که فعلا پوشه‌ی router، فاقد پوشه‌ی bundles است. تمام اجزای نگارش 2 دارای پوشه‌های bundles هستند (مانند node_modules\@angular\common\bundles)، منهای پوشه‌ی router. همچنین در فایل systemjs.config.js  یک چنین تعریفی هست:
   function packUmd(pkgName) {
        packages['@angular/' + pkgName] = { main: '/bundles/' + pkgName + '.umd.js', defaultExtension: 'js' };
};
که مسیرهای bundles را بارگذاری می‌کند. فعلا برای رفع آن از این روش استفاده کنید:
var setPackageConfig = /*System.packageWithIndex ? packIndex : packUmd;*/packIndex;

// **OR** method 2
ngPackageNames.forEach(setPackageConfig);
packages['@angular/router'] = { main: 'index.js', defaultExtension: 'js' };
نظرات مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت سوم - غنی سازی کامپوننت‌ها
با سلام
من همه موارد ذکر شده رو انجام دادم و وقتی که پروژه رو اجرا میکنم خطاهای زیر رو دریافت می‌کنم:
Template parse warnings:
"#" inside of expressions is deprecated. Use "let" instead! ("
                </thead>
                <tbody>
                    <tr [ERROR ->]*ngfor="#product of products">
                        <td>
                            <img [src]="): ProductListComponent@33:24

lang.js (line 374)

EXCEPTION: Template parse errors:
Can't bind to 'ngforOf' since it isn't a known native property ("
                </thead>
                <tbody>
                    <tr [ERROR ->]*ngfor="#product of products">
                        <td>
                            <img [src]="): ProductListComponent@33:24
Property binding ngforOf not used by any directive on an embedded template ("
                </thead>
                <tbody>
                    [ERROR ->]<tr *ngfor="#product of products">
                        <td>
                            <img [s"): ProductListComponent@33:20
Can't bind to 'ng-if' since it isn't a known native property ("
        </div>
        <div class="table-responsive">
            <table class="table" [ERROR ->]*ng-if="products && products.length">
                <thead>
                    <tr>
"): ProductListComponent@17:33
Property binding ng-if not used by any directive on an embedded template ("
        </div>
        <div class="table-responsive">
            [ERROR ->]<table class="table" *ng-if="products && products.length">
                <thead>
                "): ProductListComponent@17:12
و وقتی هم که گالپ را اجرا می‌کنم خطاهای زیر را می‌دهد:
D:/Projects/TestAngular2/node_modules/@angular/core/src/application_ref.d.ts(39,88): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/application_ref.d.ts(99,42): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/application_ref.d.ts(174,33): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/change_detection/differs/default_keyvalue_differ.d.ts(24,15): error TS2304: Cannot find name 'Map'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/change_detection/differs/default_keyvalue_differ.d.ts(26,16): error TS2304: Cannot find name 'Map'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/di/reflective_provider.d.ts(105,123): error TS2304: Cannot find name 'Map'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/di/reflective_provider.d.ts(105,165): error TS2304: Cannot find name 'Map'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/async.d.ts(27,33): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/async.d.ts(28,45): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/collection.d.ts(1,25): error TS2304: Cannot find name 'MapConstructor'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/collection.d.ts(2,25): error TS2304: Cannot find name 'SetConstructor'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/collection.d.ts(4,27): error TS2304: Cannot find name 'Map'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/collection.d.ts(4,39): error TS2304: Cannot find name 'Map'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/collection.d.ts(7,9): error TS2304: Cannot find name 'Map'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/collection.d.ts(8,30): error TS2304: Cannot find name 'Map'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/collection.d.ts(11,43): error TS2304: Cannot find name 'Map'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/collection.d.ts(12,27): error TS2304: Cannot find name 'Map'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/collection.d.ts(14,23): error TS2304: Cannot find name 'Map'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/collection.d.ts(15,25): error TS2304: Cannot find name 'Map'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/collection.d.ts(100,41): error TS2304: Cannot find name 'Set'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/collection.d.ts(101,22): error TS2304: Cannot find name 'Set'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/collection.d.ts(102,25): error TS2304: Cannot find name 'Set'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/lang.d.ts(4,17): error TS2304: Cannot find name 'Map'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/lang.d.ts(5,17): error TS2304: Cannot find name 'Set'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/lang.d.ts(70,59): error TS2304: Cannot find name 'Map'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/promise.d.ts(2,14): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/promise.d.ts(8,32): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/promise.d.ts(9,38): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/promise.d.ts(10,35): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/promise.d.ts(10,93): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/promise.d.ts(11,34): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/promise.d.ts(11,50): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/promise.d.ts(12,32): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/promise.d.ts(12,149): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/facade/promise.d.ts(13,43): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/linker/component_resolver.d.ts(8,53): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/linker/component_resolver.d.ts(12,44): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/linker/dynamic_component_loader.d.ts(62,148): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/linker/dynamic_component_loader.d.ts(103,144): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/linker/dynamic_component_loader.d.ts(108,139): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/linker/dynamic_component_loader.d.ts(109,135): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/reflection/reflector.d.ts(28,22): error TS2304: Cannot find name 'Map'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/reflection/reflector.d.ts(30,15): error TS2304: Cannot find name 'Map'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/reflection/reflector.d.ts(32,15): error TS2304: Cannot find name 'Map'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/reflection/reflector.d.ts(34,15): error TS2304: Cannot find name 'Map'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/reflection/reflector.d.ts(36,16): error TS2304: Cannot find name 'Set'.
D:/Projects/TestAngular2/node_modules/@angular/core/src/testability/testability.d.ts(40,20): error TS2304: Cannot find name 'Map'.
D:/Projects/TestAngular2/node_modules/@angular/platform-browser-dynamic/platform_browser_dynamic.d.ts(75,90): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/rxjs/Observable.d.ts(10,66): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/rxjs/Observable.d.ts(66,60): error TS2304: Cannot find name 'Promise'.
D:/Projects/TestAngular2/node_modules/rxjs/Observable.d.ts(66,70): error TS2304: Cannot find name 'Promise'.
در حالی که کلیه پکیج‌ها نصب شده است.
با تشکر
مطالب
مراحل ارتقاء پروژه‌های Angular از نسخه‌ی 6.0 به 7.0
مراحل ارتقاء پروژه‌های Angular از نگارش 6 به 7 آن به شرح زیر هستند:
1- به روز رسانی Angular CLI
ابتدا نیاز است نگارش قبلی را حذف و سپس نگارش جدید را نصب کنید:
npm uninstall -g @angular/cli
npm cache verify
# if npm version is < 5 then use `npm cache clean` 
npm install -g @angular/cli@latest
البته Angular 7 پشتیبانی از  Node 10 را اضافه کرده است (بیشتر؛ دانلود Node). بنابراین پیش از اجرای دستورات فوق بهتر است NodeJS خود را نیز به روز کنید:
npm i -g npm

2- به روز رسانی RxJS (اگر در نگارش 6 آن‌را تکمیل نکرده‌اید)
1-تعویض کردن HttpModule با HttpClientModule و سرویس Http با سرویس HttpClient
2-حذف کردن ویژگی‌های منسوخ شده از RxJS 6 با اجرای دستور زیر:
npm install -g rxjs-tslint
rxjs-5-to-6-migrate -p src/tsconfig.app.json
3-حذف rxjs-compat بعد از بروزرسانی RxJS 6
 
3- به روز رسانی TypeScript
Angular 7 از تایپ اسکریپت 3.1 استفاده می‌کند (بیشتر). به همین جهت نیاز است وابستگی‌های سراسری سیستم خود را مانند TypeScript، پس از نصب CLI جدید، به نحو زیر به روز کنید:
npm update -g

4- به روز رسانی وابستگی‌های اصلی پروژه
برای به‌‌روز رسانی به نسخه 7 (core framework و CLI)، دستورات زیر را اجرا کنید:
ng update @angular/cli
ng update @angular/core
ng update rxjs
و اگر از Angular Material نیز استفاده می‌کنید، نیاز به اجرای دستور زیر را نیز خواهید داشت:
ng update @angular/material
اگر در اینجا خطای  peer dependency را مشاهده کردید، از سوئیچ force-- در انتهای دستورات، استفاده کنید.
و یا به صورت خلاصه دستور زیر تمام مراحل فوق را به صورت یکجا انجام می‌دهد:
 ng update --all --force
 
5- به روز رسانی Service worker 

اگر شما از Service worker  مربوط به Angular استفاده می‌کنید، بجای versionedFiles از files استفاده کنید. رفتار همان است و تغییر نکرده‌است.
 
6- به روز رسانی وابستگی‌های ثالث پروژه
برای به روز رسانی سایر وابستگی‌های پروژه‌، می‌توان از بسته‌ی npm-check-updates استفاده کرد:
npm install npm-check-updates -g
ncu -u
npm install
دستور دوم تمام شماره نگارش‌های بسته‌های موجود در فایل package.json را به صورت خودکار به آخرین نگارش آن‌ها روز رسانی می‌کند و دستور سوم این بسته‌های جدید را دریافت و نصب خواهد کرد.  
مطالب
React 16x - قسمت 22 - ارتباط با سرور - بخش 1 - برپایی تنظیمات
هر برنامه‌ی وب، دارای یک frontend و یک backend است. تا اینجا، تمام تمرکز این سری، بر روی پیاده سازی frontend بود و هیچکدام از برنامه‌هایی را که تکمیل کردیم، تبادل اطلاعاتی را با وب سرویس‌های backend نداشتند؛ اما به عنوان یک توسعه دهنده‌ی React، نیاز است با نحوه‌ی ارتباط با سرور آشنایی داشت که در طی چند قسمت به آن می‌پردازیم.


ایجاد برنامه‌ی backend ارائه دهنده‌ی REST API

در اینجا یک برنامه‌ی ساده‌ی ASP.NET Core Web API را جهت تدارک سرویس‌های backend، مورد استفاده قرار می‌دهیم. هرچند این مورد الزامی نبوده و اگر علاقمند بودید که مستقل از آن کار کنید، حتی می‌توانید از سرویس آنلاین JSONPlaceholder نیز برای این منظور استفاده کنید که یک Fake Online REST API است. کار آن ارائه‌ی یک سری endpoint است که به صورت عمومی از طریق وب قابل دسترسی هستند. می‌توان به این endpintها درخواست‌های HTTP خود را مانند GET/POST/DELETE/UPDATE ارسال کرد و از آن اطلاعاتی را دریافت نمود و یا تغییر داد. به هر کدام از این endpointها یک API گفته می‌شود که جهت آزمایش برنامه‌ها بسیار مناسب هستند. برای نمونه در قسمت resources آن اگر به آدرس https://jsonplaceholder.typicode.com/posts مراجعه کنید، می‌توان لیستی از مطالب را با فرمت JSON مشاهده کرد. کار آن ارائه‌ی آرایه‌ای از اشیاء جاوا اسکریپتی قابل استفاده‌ی در برنامه‌های frontend است. بنابراین زمانیکه یک HTTP GET را به این endpoint ارسال می‌کنیم، آرایه‌ای از اشیاء مطالب را دریافت خواهیم کرد. همین endpoint، امکان تغییر این اطلاعات را توسط برای مثال HTTP Delete نیز میسر کرده‌است.

اگر علاقمندید بودید می‌توانید از JSONPlaceholder استفاده کنید و یا در ادامه دقیقا ساختار همین endpoint ارائه‌ی مطالب آن‌را با ASP.NET Core Web API نیز پیاده سازی می‌کنیم (برای مطالعه‌ی قسمت «ارتباط با سرور» اختیاری است و از هر REST API مشابهی که توسط nodejs یا PHP و غیره تولید شده باشد نیز می‌توان استفاده کرد):

مدل مطالب
namespace sample_22_backend.Models
{
    public class Post
    {
        public int Id { set; get; }
        public string Title { set; get; }
        public string Body { set; get; }

        public int UserId { set; get; }
    }
}
ساختار این مدل، با ساختار مدل مطالب JSONPlaceholder یکی درنظر گرفته شده‌است، تا مطلب قابلیت پیگیری بیشتری را پیدا کند.


منبع داده‌ی فرضی مطالب

برای ارائه‌ی ساده‌تر برنامه، یک منبع داده‌ی درون حافظه‌ای را به همراه یک سرویس، در اختیار کنترلر مطالب، قرار می‌دهیم:
using System;
using System.Collections.Generic;
using System.Linq;
using sample_22_backend.Models;

namespace sample_22_backend.Services
{
    public interface IPostsDataSource
    {
        List<Post> GetAllPosts();
        bool DeletePost(int id);
        Post AddPost(Post post);
        bool UpdatePost(int id, Post post);
        Post GetPost(int id);
    }

    /// <summary>
    /// هدف صرفا تهیه یک منبع داده آزمایشی ساده تشکیل شده در حافظه است
    /// </summary>
    public class PostsDataSource : IPostsDataSource
    {
        private readonly List<Post> _allPosts;

        public PostsDataSource()
        {
            _allPosts = createDataSource();
        }

        public List<Post> GetAllPosts()
        {
            return _allPosts;
        }

        public Post GetPost(int id)
        {
            return _allPosts.Find(x => x.Id == id);
        }

        public bool DeletePost(int id)
        {
            var item = _allPosts.Find(x => x.Id == id);
            if (item == null)
            {
                return false;
            }

            _allPosts.Remove(item);
            return true;
        }

        public Post AddPost(Post post)
        {
            var id = 1;
            var lastItem = _allPosts.LastOrDefault();
            if (lastItem != null)
            {
                id = lastItem.Id + 1;
            }

            post.Id = id;
            _allPosts.Add(post);
            return post;
        }

        public bool UpdatePost(int id, Post post)
        {
            var item = _allPosts
                .Select((pst, index) => new { Item = pst, Index = index })
                .FirstOrDefault(x => x.Item.Id == id);
            if (item == null || id != post.Id)
            {
                return false;
            }

            _allPosts[item.Index] = post;
            return true;
        }

        private static List<Post> createDataSource()
        {
            var list = new List<Post>();
            var rnd = new Random();
            for (var i = 1; i < 10; i++)
            {
                list.Add(new Post { Id = i, UserId = rnd.Next(1, 1000), Title = $"Title {i} ...", Body = $"Body {i} ..." });
            }
            return list;
        }
    }
}
در این سرویس، نیازمندی‌های کنترلر مطالب مانند ارائه لیست تمام مطالب، نمایش اطلاعات یک مطلب، به روز رسانی، ایجاد و حذف یک مطلب، تدارک دیده شده‌اند. سپس از این سرویس در کنترلر زیر استفاده می‌کنیم:


کنترلر Web API برنامه‌ی backend

using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using sample_22_backend.Models;
using sample_22_backend.Services;

namespace sample_22_backend.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class PostsController : ControllerBase
    {
        private readonly IPostsDataSource _postsDataSource;

        public PostsController(IPostsDataSource postsDataSource)
        {
            _postsDataSource = postsDataSource;
        }

        [HttpGet]
        public ActionResult<List<Post>> GetPosts()
        {
            return _postsDataSource.GetAllPosts();
        }

        [HttpGet("{id}")]
        public ActionResult<Post> GetPost(int id)
        {
            var post = _postsDataSource.GetPost(id);
            if (post == null)
            {
                return NotFound();
            }
            return Ok(post);
        }

        [HttpDelete("{id}")]
        public ActionResult DeletePost(int id)
        {
            var deleted = _postsDataSource.DeletePost(id);
            if (deleted)
            {
                return Ok();
            }
            return NotFound();
        }

        [HttpPost]
        public ActionResult<Post> CreatePost([FromBody]Post post)
        {
            post = _postsDataSource.AddPost(post);
            return CreatedAtRoute(nameof(GetPost), new { post.Id }, post);
        }

        [HttpPut("{id}")]
        public ActionResult<Post> UpdatePost(int id, [FromBody]Post post)
        {
            var updated = _postsDataSource.UpdatePost(id, post);
            if (updated)
            {
                return Ok(post);
            }
            return NotFound();
        }
    }
}
این کنترلر که در مسیر شروع شده‌ی با https://localhost:5001/api قرار می‌گیرد، جهت پشتیبانی از افعال مختلف HTTP مانند Get/Post/Delete/Update طراحی شده‌است که در ادامه، در برنامه‌ی React خود از آن‌ها استفاده خواهیم کرد. پس از ایجاد این پروژه‌ی web api، یک نمونه خروجی آن در مسیر https://localhost:5001/api/posts، به صورت زیر خواهد بود:


البته نمایش فرمت شده‌ی JSON در مرورگر کروم، نیاز به نصب این افزونه را دارد.


ایجاد ساختار ابتدایی برنامه‌ی ارتباط با سرور

در اینجا برای بررسی کار با سرور، یک پروژه‌ی جدید React را ایجاد می‌کنیم:
> create-react-app sample-22-frontend
> cd sample-22-frontend
> npm start
در ادامه توئیتر بوت استرپ 4 را نیز نصب می‌کنیم. برای این منظور پس از باز کردن پوشه‌ی اصلی برنامه توسط VSCode، دکمه‌های ctrl+` را فشرده (ctrl+back-tick) و دستور زیر را در ترمینال ظاهر شده وارد کنید:
> npm install --save bootstrap
سپس برای افزودن فایل bootstrap.css به پروژه‌ی React خود، ابتدای فایل index.js را به نحو زیر ویرایش خواهیم کرد:
import "bootstrap/dist/css/bootstrap.css";
این import به صورت خودکار توسط webpack ای که در پشت صحنه کار bundling & minification برنامه را انجام می‌دهد، مورد استفاده قرار می‌گیرد.

سپس فایل app.js را به شکل زیر تکمیل می‌کنیم:
import "./App.css";

import React, { Component } from "react";

class App extends Component {
  state = {
    posts: []
  };

  handleAdd = () => {
    console.log("Add");
  };

  handleUpdate = post => {
    console.log("Update", post);
  };

  handleDelete = post => {
    console.log("Delete", post);
  };

  render() {
    return (
      <React.Fragment>
        <button className="btn btn-primary mt-1 mb-1" onClick={this.handleAdd}>
          Add
        </button>
        <table className="table">
          <thead>
            <tr>
              <th>Title</th>
              <th>Update</th>
              <th>Delete</th>
            </tr>
          </thead>
          <tbody>
            {this.state.posts.map(post => (
              <tr key={post.id}>
                <td>{post.title}</td>
                <td>
                  <button
                    className="btn btn-info btn-sm"
                    onClick={() => this.handleUpdate(post)}
                  >
                    Update
                  </button>
                </td>
                <td>
                  <button
                    className="btn btn-danger btn-sm"
                    onClick={() => this.handleDelete(post)}
                  >
                    Delete
                  </button>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </React.Fragment>
    );
  }
}

export default App;
که حاصل آن، یک دکمه، برای افزودن مطلبی جدید، به همراه جدولی است از مطالب که قصد داریم در ادامه، اطلاعات آن‌را از سرور دریافت کرده و حذف و یا به روز رسانی کنیم:



نگاهی به انواع و اقسام HTTP Client‌های مهیا

در ادامه نیاز خواهیم داشت تا از طریق برنامه‌های React خود، درخواست‌های HTTP را به سمت سرور (یا همان برنامه‌ی backend) ارسال کنیم، تا بتوان اطلاعاتی را از آن دریافت کرد و یا تغییری را در اطلاعات موجود، ایجاد نمود. همانطور که پیشتر نیز در این سری عنوان شد، React برای این مورد نیز راه‌حل توکاری را به همراه ندارد و تنها کار آن، رندر کردن View و مدیریت DOM است. البته شاید این مورد یکی از مزایای کار با React نیز باشد! چون در این حالت می‌توانید از کتابخانه‌هایی که خودتان ترجیح می‌دهید، نسبت به کتابخانه‌هایی که به شما ارائه/تحمیل (!) می‌شوند (مانند برنامه‌های Angular) آزادی انتخاب کاملی را داشته باشید. برای مثال هرچند Angular به همراه یک HTTP Module توکار است، اما تاکنون چندین بار بازنویسی از ابتدا شده‌است! ابتدا با یک کتابخانه‌ی HTTP مقدماتی شروع کردند. بعدی آن‌را منسوخ شده اعلام و با یک ماژول جدید جایگزین کردند. بعد در نگارشی دیگر، چون این کتابخانه وابسته‌است به RxJS و خود RxJS نیز بازنویسی کامل شد، روش کار کردن با این HTTP Module نیز مجددا تغییر پیدا کرد! بنابراین اگر با Angular کار می‌کنید، باید کارها را آنگونه که Angular می‌پسندد، انجام دهید؛ اما در اینجا خیر و آزادی انتخاب کاملی برقرار است.
بنابراین اکنون این سؤال مطرح می‌شود که در React، برای برقراری ارتباط با سرور، چه باید کرد؟ در اینجا آزاد هستید برای مثال از Fetch API جدید مرورگرها و یا روش Ajax ای مبتنی بر XML قدیمی‌تر آن‌ها، استفاده کنید (اطلاعات بیشتر) و یا حتی اگر علاقمند باشید می‌توانید از محصور کننده‌های آن مانند jQuery Ajax استفاده کنید. بنابراین اگر با jQuery Ajax راحت هستید، به سادگی می‌توانید از آن در برنامه‌های React نیز استفاده کنید. اما ... ما در اینجا از یک کتابخانه‌ی بسیار محبوب و قدرتمند HTTP Client، به نام Axios (اکسیوس/ یک واژه‌ی یونانی به معنای «سودمند») استفاده خواهیم کرد که فقط تعداد بار دانلود هفتگی آن، 6 میلیون بار است!


نصب Axios در برنامه‌ی React این قسمت

برای نصب کتابخانه‌ی Axios، در ریشه‌ی پروژه‌ی React این قسمت، دستور زیر را در خط فرمان صادر کنید:
> npm install --save axios
پس از برپایی این مقدمات، ادامه‌ی مطلب «ارتباط با سرور» را در قسمت بعدی پیگیری می‌کنیم.


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: sample-22-frontend-part-01.zip و sample-22-backend-part-01.zip
مطالب
مشاهده سایت شیرپوینت به کمک IP Address
در صورتی که نیاز به مشاهده یک سایت شیرپوینت خارج از یک دامین باشد باید تنظیمات مسیر یابی آن فعال باشد تا بتوان آن را بدون نمایش خطا مشاهده کرد . در این پست کوتاه تنظیمات مرتبط به Alternate Access Mappings یا AAM بیان می‌شوند .
در صورتی که هنگام ایجاد application مسیر دامنه را برای آن مشخص کرده باشید و نیاز داشته باشید از خارج از دامنه نیز آن را مشاهده کنید ممکن است در مواردی به مشکل برخورد کنید . مثلا با مشاهده لیست به خطای زیر برخورد کنید : 

برای رفع این مشکل باید مسیر نامعتبر را برای Application به عنوان یک مسیر جایگزین تعریف کرد .

یک راه تعریف AAM برای Application می‌باشد . برای این منظور روی Configure alternate access mappings در بخش مدیریت مرکزی شیرپوینت (در قسمت‌های مشخص شده در تصویر زیر ) بروید : 

سپس روی Add Internal Urls کلیک کنید و در فیلد Url مسیر آدرس را با IP و پورت آن وارد کنید (در صورت وجود ) و zone آن را روی Interanet قرار دهید . (باید SiteCollection را نیز در همین بخش مشخص کنید ) 

البته این تنظیم از بخش Edit Public Urls نیزبا کمی تغییر قابل انجام می‌باشد . 

پس از اعمال تنظیمات مشکل عدم نمایش محتویات رفع خواهد شد .

راه حل دیگر اعمال یک Extend برای Application جاری است . 

در صورتی که از روش دوم یعنی Extend این کار را انجام دهید برای شما یک Application در زیر مجموعه سایت‌های IIS ایجاد می‌شود که ممکن است باز هم همان مشکل رخ دهد . برای رفع ان می‌توان Binding آن را ویرایش کرد و آی پی مورد نظر را یه آن Assign کرد  

موفق باشید 

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

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

<script>


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

</script>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

export default {

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

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

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

Vue.use(VueRouter)

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

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


نکته:  برای اجرای برنامه و دریافت پکیج‌های مورد استفاده در مثال جاری، نیاز است دستور زیر را اجرا کنید: 
npm install
مطالب
ذخیره سازی اطلاعات در مرورگر توسط برنامه‌های Angular
تمام برنامه‌های وب، از داده‌ها استفاده می‌کنند و امکان ذخیره سازی، به اشتراک گذاری و بازیابی آن‌ها حتی زمانیکه اتصال به شبکه برقرار نیست، بسیار حائز اهمیت است. به همین جهت مرورگرهای امروزی نیز به همراه قابلیت‌هایی هستند تا این امر را ساده‌تر کنند. این محل ذخیره سازی، درون مرورگر کاربر بوده و دسترسی به آن نیز بسیار سریع است. همچنین امکان دسترسی به آن در حالت آفلاین و بدون اتصال به شبکه نیز میسر است. البته باید دقت داشت که بسته به نوع ذخیره سازی اطلاعات محلی انتخاب شده، حداکثر 10 مگابایت بیشتر در اختیار برنامه قرار نمی‌گیرد. همچنین دسترسی این اطلاعات وابسته‌است به ماشین و وسیله‌ی مورد استفاده. برای مثال اگر کاربر از طریق سیستم و ماشین دیگری برنامه را مرور کند، دیگر دسترسی به اطلاعات محلی قبلی خود نخواهد داشت و یا اگر کاربر کش مرورگر را خالی کند، این اطلاعات نیز حذف می‌شوند.


حالت‌های مختلف ذخیره سازی اطلاعات در مرورگر کاربر

Web Storage و یا Client-side storage در دو حالت کلی session storage و local storage قابل دسترسی است:
الف) session storage
در این حالت اطلاعات ذخیره شده‌ی در session storage، پس از بسته شدن مرورگر، به صورت خودکار حذف خواهند شد.

ب) local storage
اطلاعات ذخیره شده‌ی در local storage پس از بسته شدن مرورگر نیز باقی مانده و قابل دسترسی و بازیابی مجدد هستند. تاریخ انقضای آن‌ها صرفا بر اساس خالی شدن دستی کش مرورگر توسط کاربر و یا حذف دستی اطلاعات آن توسط کدهای برنامه تعیین می‌شود.

هر دو حالت فوق به صورت ایزوله ارائه می‌شوند؛ با محدودیت حجم 10 مگابایت (جمع حجم نهایی هر دو حالت با هم، محدود به 10 مگابایت است). به این معنا که برنامه‌های هر دومین، تنها به محل ذخیره سازی خاص همان دومین دسترسی خواهند داشت.
همچنین API دسترسی به آن‌ها synchronous است و کار کردن با آن‌ها ساده‌است.

البته Client-side storage به دو مورد فوق خلاصه نمی‌شود و شامل File Storage ،WebSQL ،IndexedDB و کوکی‌های مرورگر نیز هست.
- File Storage هنوز مراحل آزمایشی خودش را طی می‌کند و مناسب برنامه‌های دنیای واقعی نیست.
- WebSQL قرار بود بر اساس بانک اطلاعاتی معروف SQLite ارائه شود؛ اما W3C در سال 2010 این استاندارد را منسوخ شده اعلام کرد و با IndexedDB جایگزین شد. دسترسی به آن async است و می‌تواند موضوع بحثی مجزا باشد.
- کوکی‌های مرورگرها نیز یکی دیگر از روش‌های ذخیره سازی اطلاعات در مرورگرها هستند و تنها به ذخیره سازی حداکثر 4096 بایت اطلاعات محدود هستند. کوکی‌ها نیز همانند local storage پس از بسته شدن مرورگر باقی می‌مانند؛ اما برخلاف آن، دارای تاریخ انقضاء و همچنین قابلیت ارسال بین دومین‌ها را نیز دارا می‌باشند. اگر تاریخ انقضای یک کوکی تعیین نشود، همانند session storage، در پایان کار مرورگر و بسته شدن آن، حذف خواهد شد.


تهیه یک سرویس Angular برای کار با Web Storage

جهت کپسوله سازی نحوه‌ی کار با session storage و local storage می‌توان سرویسی را برای این‌کار تهیه کرد:
import { Injectable } from "@angular/core";

@Injectable()
export class BrowserStorageService {

  getSession(key: string): any {
    const data = window.sessionStorage.getItem(key);
    return JSON.parse(data);
  }

  setSession(key: string, value: any): void {
    const data = value === undefined ? null : JSON.stringify(value);
    window.sessionStorage.setItem(key, data);
  }

  removeSession(key: string): void {
    window.sessionStorage.removeItem(key);
  }

  removeAllSessions(): void {
    for (const key in window.sessionStorage) {
      if (window.sessionStorage.hasOwnProperty(key)) {
        this.removeSession(key);
      }
    }
  }

  getLocal(key: string): any {
    const data = window.localStorage.getItem(key);
    return JSON.parse(data);
  }

  setLocal(key: string, value: any): void {
    const data = value === undefined ? null : JSON.stringify(value);
    window.localStorage.setItem(key, data);
  }

  removeLocal(key: string): void {
    window.localStorage.removeItem(key);
  }

  removeAllLocals(): void {
    for (const key in window.localStorage) {
      if (window.localStorage.hasOwnProperty(key)) {
        this.removeLocal(key);
      }
    }
  }
}
دسترسی به local storage از طریق شیء window.localStorage انجام می‌شود و کار با آن در برنامه‌های Angular، نیاز به وابستگی خاص دیگری ندارد. این مورد برای کار با session storage از طریق شیء window.sessionStorage صورت می‌گیرد. هر دو حالت، دارای متدهای setItem برای ذخیره سازی اطلاعات، getItem برای دریافت اطلاعات، بر اساس کلیدی مشخص و removeItem برای حذف اطلاعات کلیدی معلوم، هستند.
در حالت setItem اطلاعاتی را که مرورگرها ذخیره می‌کنند باید رشته‌ای باشد. به همین جهت توسط متد JSON.stringify می‌توان یک شیء را تبدیل به رشته کرد و ذخیره نمود و در حالت getItem توسط متد JSON.parse، می‌توان این رشته را مجددا به همان شیء پیشین خود تبدیل کرد و بازگشت داد.


محل صحیح تعریف BrowserStorageService

همانطور که در مطلب «سازماندهی برنامه‌های Angular توسط ماژول‌ها» بررسی شد، محل صحیح تعریف این سرویس سراسری مشترک در بین کامپوننت‌ها و ماژول‌های برنامه، در CoreModule و پوشه‌ی src\app\core\browser-storage.service.ts است:
import { BrowserStorageService } from "./browser-storage.service";
import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
import { RouterModule } from "@angular/router";

@NgModule({
  imports: [CommonModule, RouterModule],
  exports: [], // components that are used in app.component.ts will be listed here.
  declarations: [], // components that are used in app.component.ts will be listed here.
  providers: [BrowserStorageService] // singleton services of the whole app will be listed here.
})
export class CoreModule { };

و CoreModule نیز به AppModule اضافه می‌شود:
import { CoreModule } from "./core/core.module";

@NgModule({
  imports:      [
//...
    CoreModule,
//...
    RouterModule.forRoot(appRoutes)
  ],
//...
})
export class AppModule { }

بنابراین یکی دیگر از روش‌های به اشتراک گذاری اطلاعات در بین قسمت‌های مختلف برنامه، ذخیره سازی آن‌ها در session/local storage و سپس بازیابی آن‌ها بر اساس کلیدهای مشخص آن‌ها است.


مثالی از نحوه‌ی کاربرد BrowserStorageService

برای آزمایش سرویس تهیه شده، از کامپوننت و قالب ذیل استفاده خواهیم کرد. در اینجا سرویس BrowserStorageService به سازنده‌ی کلاس تزریق شده‌است و سپس دو حالت session storage و local storage مورد بررسی قرار گرفته‌اند:
import { BrowserStorageService } from "./../../core/browser-storage.service";
import { Component, OnInit } from "@angular/core";

@Component({
  selector: "app-browser-storage-sample-test",
  templateUrl: "./browser-storage-sample-test.component.html",
  styleUrls: ["./browser-storage-sample-test.component.css"]
})
export class BrowserStorageSampleTestComponent implements OnInit {

  fromSessionStorage = "";
  fromLocalStorage = ""

  sessionStorageKey = "sessionStorageKey1";
  localStorageKey = "localStorageKey1"

  constructor(private browserStorage: BrowserStorageService) { }

  ngOnInit() {
  }

  sessionStorageSetItem() {
    this.browserStorage.setSession(this.sessionStorageKey, "Val1");
  }

  sessionStorageGetItem() {
    this.fromSessionStorage = this.browserStorage.getSession(this.sessionStorageKey);
  }

  localStorageSetItem() {
    this.browserStorage.setLocal(this.localStorageKey, { key1: "val1", key2: 2 });
  }

  localStorageGetItem() {
    this.fromLocalStorage = JSON.stringify(this.browserStorage.getLocal(this.localStorageKey));
  }
}
به همراه قالب:
<h1>Browser storage sample</h1>
<div class="panel">
  <button class="btn btn-primary" (click)="sessionStorageSetItem()" type="button">sessionStorage -> Set Item</button>
  <button class="btn btn-success" (click)="sessionStorageGetItem()" type="button">sessionStorage -> Get Item</button>
  <div class="alert alert-info" *ngIf="fromSessionStorage">
    {{fromSessionStorage}}
  </div>
</div>

<div class="panel">
  <button class="btn btn-warning" (click)="localStorageSetItem()" type="button">localStorage -> Set Item</button>
  <button class="btn btn-success" (click)="localStorageGetItem()" type="button">localStorage -> Get Item</button>
  <div class="alert alert-info" *ngIf="fromLocalStorage">
    {{fromLocalStorage}}
  </div>
</div>

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


و اگر به برگه‌ی Application کنسول ابزارهای توسعه دهنده‌های مرورگرها نیز مراجعه کنیم، این مقادیر ثبت شده را در دو حالت استفاده‌ی از session storage و local storage، می‌توان مشاهده کرد:



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