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 { }
علت اینجا است که فعلا پوشهی 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' }; };
var setPackageConfig = /*System.packageWithIndex ? packIndex : packUmd;*/packIndex; // **OR** method 2 ngPackageNames.forEach(setPackageConfig); packages['@angular/router'] = { main: 'index.js', defaultExtension: 'js' };
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'.
npm uninstall -g @angular/cli npm cache verify # if npm version is < 5 then use `npm cache clean` npm install -g @angular/cli@latest
npm i -g npm
2-حذف کردن ویژگیهای منسوخ شده از RxJS 6 با اجرای دستور زیر:
npm install -g rxjs-tslint rxjs-5-to-6-migrate -p src/tsconfig.app.json
npm update -g
برای بهروز رسانی به نسخه 7 (core framework و CLI)، دستورات زیر را اجرا کنید:
ng update @angular/cli ng update @angular/core ng update rxjs
ng update @angular/material
ng update --all --force
npm install npm-check-updates -g ncu -u npm install
ایجاد برنامهی 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; } } }
منبع دادهی فرضی مطالب
برای ارائهی سادهتر برنامه، یک منبع دادهی درون حافظهای را به همراه یک سرویس، در اختیار کنترلر مطالب، قرار میدهیم:
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(); } } }
البته نمایش فرمت شدهی JSON در مرورگر کروم، نیاز به نصب این افزونه را دارد.
ایجاد ساختار ابتدایی برنامهی ارتباط با سرور
در اینجا برای بررسی کار با سرور، یک پروژهی جدید React را ایجاد میکنیم:
> create-react-app sample-22-frontend > cd sample-22-frontend > npm start
> npm install --save bootstrap
import "bootstrap/dist/css/bootstrap.css";
سپس فایل 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
برای رفع این مشکل باید مسیر نامعتبر را برای 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 کرد
ng generate component [name] یا ng g c [name]
<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>
<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>
<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>
<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>
import axios from 'axios'
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 }) } }
axios.defaults.baseURL = 'http://www.omdbapi.com/?apikey=b76b385c&page=1&type=movie&Content-Type=application/json'
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' })
npm install
حالتهای مختلف ذخیره سازی اطلاعات در مرورگر کاربر
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); } } } }
در حالت 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، میتوان مشاهده کرد:
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید.