UI-Router ابزاری برای مسیریابی در AngularJS است که این امکان را برایتان فراهم میکند تا بخشهای برنامه رابط کاربریتان را به شکل یک ماشین حالت ساماندهی کنید. برخلاف سرویس route$ که بر اساس مسیریابی URLها ساماندهی شده و کار میکند، UI-Router بر اساس حالتها کار میکند، که این حالتها میتوانند در صورت لزوم مسیریابی هم داشته باشند.
UI-Router یکی از افزونههای مجموعه Angular-ui، و پاراگراف بالا معرفی آن در صفحه خانگیش است (تقریبا!). این افزونه جزئیات مفصلی دارد و در این مطلب تنها به معرفی آن خواهم پرداخت (بر اساس مطالب صفحه خانگیش). پیش از ادامه پیشنهاد میکنم اگر مطالب زیر را نخواندهاید ابتدا آنها را مرور کنید:
برای استفاده از UI-Router باید:
- فایل جاوا اسکریپت آن را دانلود کنید (released یا minified).
- در صفحه اصلی برنامهتان پس از include کردن فایل اصلی AngularJS فایل angular-ui-router.js (یا angular-ui-router.min.js) را include کنید.
- 'ui.router' را به لیست وابستگیهای ماژول اصلی اضافه کنید.
<!doctype html> <html ng-app="myApp"> <head> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js"></script> <script src="js/angular-ui-router.min.js"></script> <script> var myApp = angular.module('myApp', ['ui.router']); // For Component users, it should look like this: // var myApp = angular.module('myApp', [require('angular-ui-router')]); </script> ... </head> <body> ... </body> </html>
حالتها و viewهای تو در تو
قابلیت اصلی UI-Router امکان تعریف حالتها و vieweهای تو در تو است. در مطلب مسیریابی در AngularJs #بخش اول دایرکتیو ng-view معرفی شده است. هنگام استفاده از سرویس route$ با این دایرکتیو میتوان محل مورد نظر برای بارگذاری محتویات مربوط به مسیرها را مشخص کرد. دایرکتیو ui-view در UI-Router همین نقش را دارد. فرض کنید این کد فایل index.html باشد:<!-- index.html --> <body> <div ui-view></div> <!-- We'll also add some navigation: --> <a ui-sref="state1">State 1</a> <a ui-sref="state2">State 2</a> </body>
در ادامه برای هر کدام از حالتها یک template اضافه میکنیم:
فایل state1.html:
<!-- partials/state1.html --> <h1>State 1</h1> <hr/> <a ui-sref="state1.list">Show List</a> <div ui-view></div>
<!-- partials/state2.html --> <h1>State 2</h1> <hr /> <a ui-sref="state2.list">Show List</a> <div ui-view></div>
دو نکته قابل توجه در این templateها وجود دارد. اول اینکه همانطور که میبینید templateها خود شامل تگی با دایرکتیو ui-view هستند. و دوم مقدار دایرکتیو ui-sref است که به صورت state1.list و state2.list آمده است. این جدا سازی با نقطه نشان دهنده سلسله مراتب حالتهاست. یعنی حالتهای state1 و state2 هرکدام حالت فرزندی به نام list دارند. در ادامه وقتی حالتها و مسیریابی را در ()app.config تعریف کنیم این مسائل از هالهای از ابهام که در آن هستند خارج میشوند! فعلا بیاید با راهنمای UI-Router پیش برویم و فایلهای template حالتهای فرزند را تعریف کنیم. templateهایی که قرار است در ui-view پدرانشان بارگذاری شوند:
<!-- partials/state1.list.html --> <h3>List of State 1 Items</h3> <ul> <li ng-repeat="item in items">{{ item }}</li> </ul>
<!-- partials/state2.list.html --> <h3>List of State 2 Things</h3> <ul> <li ng-repeat="thing in things">{{ thing }}</li> </ul>
myApp.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) { // // For any unmatched url, redirect to /state1 $urlRouterProvider.otherwise("/state1"); // // Now set up the states $stateProvider .state('state1', { url: "/state1", templateUrl: "partials/state1.html" }) .state('state1.list', { url: "/list", templateUrl: "partials/state1.list.html", controller: function($scope) { $scope.items = ["A", "List", "Of", "Items"]; } }) .state('state2', { url: "/state2", templateUrl: "partials/state2.html" }) .state('state2.list', { url: "/list", templateUrl: "partials/state2.list.html", controller: function($scope) { $scope.things = ["A", "Set", "Of", "Things"]; } }) }]);
خصوصیت url مشخص کننده مسیر حالت است. این خصوصیت همان مقداریست که به عنوان پارامتر اول به ()routeProvider.when$ پاس میشد. در این پارامتر میشود متغیرهای url را هم به همان ترتیب تعریف کرد. مثلا اگر حالت state1 در آدرسش یک پارامتر id داشته باشد میشود آن را به این ترتیب تعریف کرد:
.state('state1', { url: "/state1/:id", templateUrl: "partials/state1.html" })
$stateParams.id
.state('list', { parent: "state1", url: "/list", templateUrl: "partials/state1.list.html", controller: function($scope) { $scope.items = ["A", "List", "Of", "Items"]; } }) .state('list', { parent: "state2", url: "/list", templateUrl: "partials/state2.list.html", controller: function($scope) { $scope.items = ["A", "List", "Of", "Items"]; } })
- وابستگیهای فراهم شده در حالت پدر به وسیله "resolve"
- دادههای سفارشی مشخص شده در خصوصیت data حالت پدر
.state('state1', { url: "/state1", templateUrl: "partials/state1.html", data:{ foodata: 'addorder' } })
$state.current.data.foodata
Viewهای نامگذاری شده و چندگانه
<!-- index.html --> <body> <div ui-view="viewA"></div> <div ui-view="viewB"></div> <!-- Also a way to navigate --> <a ui-sref="route1">Route 1</a> <a ui-sref="route2">Route 2</a> </body>
myApp.config(function ($stateProvider) { $stateProvider .state('index', { url: "", views: { "viewA": { template: "index.viewA" }, "viewB": { template: "index.viewB" } } }) .state('route1', { url: "/route1", views: { "viewA": { template: "route1.viewA" }, "viewB": { template: "route1.viewB" } } }) .state('route2', { url: "/route2", views: { "viewA": { template: "route2.viewA" }, "viewB": { template: "route2.viewB" } } }) });
چند نکته
.state('list', { parent: "state1", url: "/list", templateUrl: "partials/state1.list.html", controller: "state1ListController as listCtrl1" } }) .state('list', { parent: "state2", url: "/list", templateUrl: "partials/state2.list.html", controller: "state2ListController as listCtrl2" } })
حالتهای انتزاعی
حساسیت به حروف بزرگ و کوچک
- در فایل "angular-ui-router.js" عبارت "new RegExp(compiled)" را پیدا کرده و آن را به "RegExp(compiled, 'i')" تبدیل کنید. و یا در "angular-ui-router.min.js" (هرکدام از فایلها که استفاده میکنید) عبارت new RegExp(o) را پیدا کرده و آن را به new RegExp(o, "i") تبدیل کنید. همین؛ صدایش را هم در نیاورید!
Last week, at ng-conf, the Angular team at Google provided the web developer world with an update on the state of Angular 2. They were joined on stage by a member of the TypeScript team, Jonathan Turner, to also announce that Angular 2 will be built using TypeScript. Jonathan then demoed a preview of the upcoming TypeScript 1.5 release via an Angular 2 sample application.
At the time of writing this post, default ASP.NET Core SPA templates allow you to create Angular 5 based app with Visual Studio without installing any third-party extensions or templates. Angular 6 is out now and you can also upgrade the Angular 5 app to Angular 6 . What if you want to create Angular 6 app with VS 2017? This post talks about how to create an Angular 6 App with Visual Studio 2017 and how to extend it with a simple example.
کدهای سمت سرور دریافت تصویر
در اینجا کدهای سمت سرور برنامه، نکتهی خاصی را به همراه نداشته و صرفا یک تصویر ساده (محتوای باینری) را بازگشت میدهد:
using Microsoft.AspNetCore.Mvc; namespace AngularTemplateDrivenFormsLab.Controllers { [Route("api/[controller]")] public class ShowImagesController : Controller { [HttpGet("[action]")] public IActionResult GetImage() { return File("~/assets/resume.png", "image/png"); } } }
سرویس دریافت محتوای باینری در برنامههای Angular
برای اینکه HttpClient برنامههای Angular بتواند محتوای باینری را بجای محتوای JSON پیشفرض آن دریافت کند، نیاز است نوع خروجی سمت سرور آنرا به blob تنظیم کرد:
import { Injectable } from "@angular/core"; import { Observable } from "rxjs/Observable"; import { HttpClient } from "@angular/common/http"; @Injectable() export class DownloadBinaryDataService { constructor(private httpClient: HttpClient) { } public getImage(): Observable<Blob> { return this.httpClient.get("/api/ShowImages/GetImage", { responseType: "blob" }); } }
ساخت URL برای دسترسی به اطلاعات باینری
تمام مرورگرهای جدید از ایجاد URL برای اشیاء Blob دریافتی از سمت سرور، توسط متد توکار URL.createObjectURL پشتیبانی میکنند. این متد، شیء URL را از شیء window جاری دریافت میکند و سپس اطلاعات باینری را دریافت کرده و آدرسی را جهت دسترسی موقت به آن تولید میکند.
بنابراین ابتدا سرویس جدیدی را در مسیر src\app\core\window.service.ts جهت دسترسی به این شیء پنجره ایجاد میکنیم:
import { Injectable } from "@angular/core"; function getWindow(): any { return window; } @Injectable() export class WindowRefService { get nativeWindow(): any { return getWindow(); } }
چون این سرویس، یک سرویس سراسری است، آنرا در قسمت providers مربوط به CoreModule ثبت خواهیم کرد تا در تمام برنامه قابل دسترسی شود:
import { WindowRefService } from "./window.service"; @NgModule({ providers: [ WindowRefService ] }) export class CoreModule {}
const urlCreator = this.nativeWindow.URL; imageBlobUrl = urlCreator.createObjectURL(imageDataBlob);
اصلاح Content Security Policy سمت سرور جهت نمایش تصاویر blob
زمانیکه از متد createObjectURL استفاده میشود، یک نمونه آدرس تولیدی آن چنین شکلی را پیدا میکند:
<img src="blob:http://localhost:5000/9d4bae44-00bc-4ed8-9f27-cac2de5ecd5d">
img-src 'self' data: blob:
دریافت تصویر از سرور و نمایش آن در برنامهی Angular
پس از این مقدمات، کامپوننتی که یک تصویر را از سمت سرور دریافت کرده و نمایش میدهد، چنین کدی را خواهد داشت:
import { WindowRefService } from "./../../core/window.service"; import { DownloadBinaryDataService } from "app/angular-http-client-blob/download-binary-data.service"; import { Component, OnInit, ViewChild, ElementRef } from "@angular/core"; import { DomSanitizer, SafeUrl } from "@angular/platform-browser"; @Component({ templateUrl: "./download-blobs.component.html", styleUrls: ["./download-blobs.component.css"] }) export class DownloadBlobsComponent implements OnInit { @ViewChild("sampleImage1") sampleImage1: ElementRef; private nativeWindow: Window; imageBlobUrl: string; sanitizedImageBlobUrl: SafeUrl; constructor(private downloadService: DownloadBinaryDataService, private windowRefService: WindowRefService, private sanitizer: DomSanitizer) { } ngOnInit() { this.nativeWindow = this.windowRefService.nativeWindow; this.downloadService.getImage().subscribe(imageDataBlob => { const urlCreator = this.nativeWindow.URL; this.imageBlobUrl = urlCreator.createObjectURL(imageDataBlob); // doesn't work -> <img [src]="imageBlobUrl"> this.sampleImage1.nativeElement.src = this.imageBlobUrl; // works -> <img #sampleImage1> this.sanitizedImageBlobUrl = this.sanitizer.bypassSecurityTrustUrl(this.imageBlobUrl); // works -> <img [src]="sanitizedImageBlobUrl"> }); } }
<h1>Angular HttpClient Blob</h1> <h4>#sampleImage1</h4> <img #sampleImage1> <h4>imageBlobUrl</h4> <img [src]="imageBlobUrl"> <h4>sanitizedImageBlobUrl</h4> <img [src]="sanitizedImageBlobUrl">
سپس مشترک متد getImage دریافت تصویر شده و اطلاعات نهایی آنرا به صورت imageDataBlob دریافت میکنیم.
این اطلاعات باینری را به متد createObjectURL ارسال کرده و آدرس موقتی این تصویر را در مرورگر بدست میآوریم.
در ادامه سه روش کار با این URL نهایی را بررسی کردهایم:
- دسترسی مستقیم به imageBlobUrl
this.imageBlobUrl = urlCreator.createObjectURL(imageDataBlob); // doesn't work -> <img [src]="imageBlobUrl">
<h4>imageBlobUrl</h4> <img [src]="imageBlobUrl">
<img _ngcontent-c1="" src="unsafe:blob:http://localhost:5000/a4505339-5da2-4303-949c-8e6a7cfff2fc">
Refused to load the image 'unsafe:blob:http://localhost:5000/a4505339-5da2-4303-949c-8e6a7cfff2fc' because it violates the following Content Security Policy directive: "img-src 'self' data: blob:".
- برای رفع این مشکل، میتوان از سرویس DomSanitizer آن که به سازندهی کلاس تزریق شدهاست استفاده کرد:
this.sanitizedImageBlobUrl = this.sanitizer.bypassSecurityTrustUrl(this.imageBlobUrl); // works -> <img [src]="sanitizedImageBlobUrl">
<img [src]="sanitizedImageBlobUrl">
<img _ngcontent-c1="" src="blob:http://localhost:5000/a4505339-5da2-4303-949c-8e6a7cfff2fc">
- روش دیگر نمایش این تصویر، انتساب این آدرس به شیء بومی این تصویر است. برای اینکار در قالب این کامپوننت، یک template reference variable را به img نسبت میدهیم:
<img #sampleImage1>
@ViewChild("sampleImage1") sampleImage1: ElementRef;
this.sampleImage1.nativeElement.src = this.imageBlobUrl; // works -> <img #sampleImage1>
در نهایت Angular یک چنین خروجی را برای نمایش اینگونه تصاویر، بر اساس کدهای فوق رندر میکند:
<ng-component _nghost-c1=""><h1 _ngcontent-c1="">Angular HttpClient Blob</h1> <h4 _ngcontent-c1="">#sampleImage1</h4> <img _ngcontent-c1="" src="blob:http://localhost:5000/a4505339-5da2-4303-949c-8e6a7cfff2fc"> <h4 _ngcontent-c1="">imageBlobUrl</h4> <img _ngcontent-c1="" src="unsafe:blob:http://localhost:5000/a4505339-5da2-4303-949c-8e6a7cfff2fc"> <h4 _ngcontent-c1="">sanitizedImageBlobUrl</h4> <img _ngcontent-c1="" src="blob:http://localhost:5000/a4505339-5da2-4303-949c-8e6a7cfff2fc"> </ng-component>
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید.
error RZ9992: Script tags should not be placed inside components because they cannot be updated dynamically. To fix this, move the script tag to the 'index.html' file or another static location. For more information see https://go.microsoft.com/fwlink/?linkid=872131
<script suppress-error="BL9992" />
در صورتی که از وب سرویس asmx یا web API یا wcf service در پروژه وبی جهت انجام عملیات اسکن استفاده کنم با خطای زیر مواجه میشوم :
Creating an instance of the COM component with CLSID {850D1D11-70F3-4BE5-9A11-77AA6B2BB201} from the IClassFactory failed due to the following error: 80070005 Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)).
سطح دسترسی application pool وب سایت را نیز تغییر داده ام, اما مشکل حل نشده است .
ظاهرا اگر عملیات دسترسی به کتابخانه WIA از طریق وب سایت انجام شود این مشکل به وجود میآید .
یعنی حتما باید این کار در یک پروژه مجزای WCF انجام شود و هاست شود و سپس در وب اپلیکیشن فراخوانی شود؟
مروری بر ASP.NET Core View Component
Partial Views and Child Actions are one the most used features of ASP.NET MVC. Partial Views provides us a way to create a reusable component that can be used in multiple Views. There are Actions which can be marked as Child Actions and these cannot be invoked via URL but inside views or partial views. Child Actions are no more available with ASP.NET Core. View Components are new way to implement this feature in ASP.NET Core.
Response Caching در ASP.NET Core 1.1
With the ASP.NET Core 1.1 release, many new features were introduced. One of them was enabling gZip compression and today we will take a look at another new feature which is Response Caching Middleware.
مهاجرت به AngularJS 2.0
"Our goal with Angular 2 is to make the best possible set of tools for
building web apps not constrained by maintaining backwards compatibility
with existing APIs." —Angular blog