کتابخانه twitter-bootstrap-wizard
demo
البته Angular به همراه دایرکتیو ویژهای به نام ngModel است که two-way data binding را با import ماژول ویژهی فرمها میسر میکند:
که آن نیز در اصل از جمع Property Binding و Event Binding تشکیل شدهاست:
<input [ngModel]="username" (ngModelChange)="username = $event">
<input [(ngModel)]='username' />
انقیاد به خواص یا Property binding
فرض کنید دو کامپوننت والد و فرزند را ایجاد کردهایم:
در کامپوننت والد، مقداری را توسط متد deposit هربار 100 آیتم افزایش میدهیم:
import { Component, OnInit } from "@angular/core"; @Component({ selector: "app-parent", templateUrl: "./parent.component.html", styleUrls: ["./parent.component.css"] }) export class ParentComponent implements OnInit { amount = 500; constructor() { } ngOnInit() { } deposit() { this.amount += 100; } }
<h2>Custom two way data binding</h2> <div class="panel panel-primary"> <div class="panel-heading"> <h2 class="panel-title">Parnet Component</h2> </div> <div class="panel-body"> <label>Available amount:</label> {{amount}} <button (click)="deposit()" class="btn btn-success">Deposit 100</button> <div> <app-child [amount]="amount"> </app-child> </div> </div> </div>
کامپوننت فرزند به صورت ذیل تعریف میشود:
import { Component, OnInit, Input } from "@angular/core"; @Component({ selector: "app-child", templateUrl: "./child.component.html", styleUrls: ["./child.component.css"] }) export class ChildComponent implements OnInit { @Input() amount: number; constructor() { } ngOnInit() { } withdraw() { this.amount -= 100; } }
با این قالب:
<div class="panel panel-default"> <div class="panel-heading"> <h2 class="panel-title">Child Component</h2> </div> <div class="panel-body"> <label>Amount available: </label> {{amount}} <button (click)="withdraw()" class="btn btn-danger">Withdraw 100</button> </div> </div>
در اینجا زمانیکه data binding را به صورت ذیل تعریف میکنیم:
<app-child [amount]="amount"> </app-child>
این ارتباط نیز یک طرفهاست. برای مثال اگر بر روی دکمهی Deposit والد کلیک کنیم:
مقدار افزایش یافتهی در والد، به فرزند نیز منتقل میشود و نمایش داده خواهد شد. اما اگر بر روی دکمهی withdraw فرزند کلیک کنیم:
تغییر صورت گرفته، به والد انعکاس پیدا نمیکند. برای اطلاع رسانی به والد، به انقیاد به رخدادها نیاز داریم.
انقیاد به رخدادها یا Event binding
یک کامپوننت میتواند به رخدادهای صادر شدهی توسط کامپوننتی دیگر گوش فرا دهد:
import { Component, OnInit, Input, Output, EventEmitter } from "@angular/core"; @Component({ selector: "app-child", templateUrl: "./child.component.html", styleUrls: ["./child.component.css"] }) export class ChildComponent implements OnInit { @Input() amount: number; @Output() amountChange = new EventEmitter(); constructor() { } ngOnInit() { } withdraw() { this.amount -= 100; this.amountChange.emit(this.amount); } }
اکنون در قالب کامپوننت والد، این رخداد را درون یک () معرفی خواهیم کرد:
<app-child [amount]="amount" (amountChange)="this.amount= $event"> </app-child>
اکنون اگر برنامه را آزمایش کنیم، با کلیک بر روی دکمهی withdraw فرزند، مقدار کاهش یافته به والد نیز منعکس میشود:
پیاده سازی syntax ویژهی Banana in a box
تا اینجا پیاده سازی two way data-binding سفارشی به پایان میرسد. اما تعریف طولانی:
<app-child [amount]="amount" (amountChange)="this.amount= $event"> </app-child>
<app-child [(amount)]="amount"> </app-child>
نکتهی ویژهی آن، وجود پسوند Change در نام رخداد تعریف شدهاست:
@Input() amount: number; @Output() amountChange = new EventEmitter();
بنابراین به صورت خلاصه جهت تعریف یک انقیاد دو طرفه سفارشی:
- ابتدا باید انقیاد به یک خاصیت ورودی x را تعریف کرد.
- سپس نیاز است انقیاد به یک رخداد خروجی همنام، که نام آن، پسوند Change را اضافهتر دارد، یعنی xChange را تعریف کرد.
- اکنون میتوان two-way data binding syntax ویژهای را به نام banana in a box بر روی ایندو تعریف کرد[(x)].
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید.
A set of files that comprise a standalone app, a library, or a set of end-to-end (e2e) tests.
ng --version
ng new angular-apps --create-application=false
angular-apps/README.md angular-apps/angular.json angular-apps/package.json angular-apps/tsconfig.json angular-apps/tslint.json angular-apps/.editorconfig angular-apps/.gitignore
cd angular-apps
ng generate application admin
ng generate library ngx-qrcode
ng generate service auth --project=admin
projects/admin/src/app/auth.service.spec.ts projects/admin/src/app/auth.service.ts
ng serve admin
ng new
What name would you like to use for the new workspace and initial project?
WebSocket is real-time communication protocol we can use to make UI in browser to send and receive messages from server over real-time communication channel. WebSocket is also supported by ASP.NET Core. This blog post demonstrates how to build simple browser based chat room on ASP.NET Core and WebSocket.
@import '~@angular/material/theming'; @include mat-core(); $green-primary: mat-palette($mat-green,A700); $green-accent: mat-palette($mat-green, A700, A200, A700); $green-warn: mat-palette($mat-green,A700); $green-app-theme: mat-light-theme($green-primary, $green-accent, $green-warn); .green{ @include angular-material-theme($green-app-theme); } $yellow-primary: mat-palette($mat-yellow,A200); $yellow-accent: mat-palette($mat-green, A700, A200, A700); // The warn palette is optional (defaults to red). $yellow-app-warn: mat-palette($mat-green,A700); // Create the theme object (a Sass map containing all of the palettes). $yellow-app-theme: mat-light-theme($yellow-primary, $yellow-accent, $yellow-app-warn); .yellow{ @include angular-material-theme($yellow-app-theme); }
<div [ngClass]="themeClass" color="primary"> <mat-toolbar #toolbar color="primary"> <p>تست قالب پویا</p> <a routerLink='' mat-button>خانه</a> <button mat-button [matMenuTriggerFor]="menu">قالب</button> <mat-menu #menu="matMenu"> <button mat-menu-item (click)="changeTheme('')">پیشفرض</button> <button mat-menu-item (click)="changeTheme('green')">سبز</button> <button mat-menu-item (click)="changeTheme('yellow')">زرد</button> </mat-menu> </mat-toolbar> </div>
import { Component } from '@angular/core'; import { Http } from '@angular/http'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { title = 'app'; themeClass = localStorage.getItem('nameOfThemeClass'); constructor() { } ngOnInit() { } changeTheme(theme) { this.themeClass = theme; localStorage.setItem('nameOfThemeClass', this.themeClass); } }
شروع کار با MVC ویژه مبتدی ها
تفاوت مجوز LGPL با GPL در چیست؟
A component licensed under LGPL can be used by closed source, proprietary software, both internally used and distributed, for free, with no effects on the software using the component. LGPL is not “contagious” in the same way as GPL, so it only affects the component under LGPL. As long as you’re only using official distributions of the component, it is free to use and free to redistribute. The only requirement is that you include a notice in your “about” page or similar that the component is used.
This web app allows you to create a visual representation of your BenchmarkDotNet console results. You can conveniently copy the generated chart to your clipboard, save it as a PNG image, or share it through a URL.
در اینجا برای بررسی مقدماتی کامپوننتها، یک پروژهی جدید React را ایجاد میکنیم.
> create-react-app sample-04 > cd sample-04 > npm start
> npm install --save bootstrap
پس از اجرای این دستور، ممکن است پیامهای اخطاری مانند «requires a peer of jquery@1.9.1 - 3 but none is installed» را نیز مشاهده کنید که مهم نیستند. چون در اینجا صرفا از امکانات CSS ای بوت استرپ استفاده خواهیم کرد و کاری با jQuery نداریم. محل نصب آن نیز پوشهی node_modules\bootstrap برنامه است.
سپس برای افزودن فایل bootstrap.css به پروژهی React خود، ابتدای فایل index.js را به نحو زیر ویرایش خواهیم کرد:
import "bootstrap/dist/css/bootstrap.css";
ایجاد اولین کامپوننت React
در پوشهی src برنامه، پوشهی جدیدی را به نام components ایجاد میکنیم و تمام کامپوننتهای خود را در آن قرار خواهیم داد. سپس داخل این پوشه، یک فایل جدید و خالی را به نام counter.jsx ایجاد میکنیم. پسوند این فایل jsx است و نام فایلهای کامپوننتها را نیز camel case وارد میکنیم؛ یعنی اولین حرف اولین واژهی وارد شده، با حروف کوچک و تمام واژههای پس از آن با حروف بزرگ شروع خواهند شد مانند coolApp. مزیت استفادهی از پسوند jsx نسبت به js، فراهم شدن امکانات مخصوص React در VSCode است.
در ابتدای فایل counter.jsx، نیاز است وابستگیهای React را import کنیم. اگر از قسمت اول بخاطر داشته باشید، «simple react snippets» را نیز در VSCode نصب کردیم. به کمک آن میتواند این نوع importها را سادهتر وارد کرد. برای این منظور imrc را تایپ کرده و سپس دکمهی tab را فشار دهید. به این ترتیب یک سطر زیر به صورت خودکار تولید میشود:
import React, { Component } from 'react';
import React, { Component } from "react"; class Counter extends Component { render() { return <h1>Hello world!</h1>; } } export default Counter;
خروجی متد render در اینجا، یک رشتهی معمولی نیست؛ بلکه یک عبارت jsx است که در قسمت اول معرفی شد. این عبارت در نهایت توسط کامپایلر Babel به معادل React.createElement ترجمه میشود. به همین جهت نیاز است تا import React را در ابتدای این ماژول درج کرد؛ هرچند به ظاهر به صورت مستقیم از آن استفاده نمیکنیم.
تا اینجا این کامپوننت در UI برنامه نمایش داده نمیشود. به همین جهت به فایل index.js مراجعه کرده و آنرا به صورت زیر تغییر میدهیم:
- ابتدا نیاز است تا شیء Counter را در اینجا import کنیم و چون خروجی پیشفرض است، نیازی به ذکر {} برای معرفی آن نیست:
import Counter from "./components/counter";
ReactDOM.render(<Counter />, document.getElementById("root"));
درج چند عنصر در عبارات JSX
میخواهیم در کامپوننت Counter، یک دکمه را نیز نمایش دهیم. برای انجام اینکار، به نحو زیر عمل میکنیم:
render() { return <h1>Hello world!</h1><button>Increment</button>; }
عبارات JSX در نهایت باید تبدیل به متد React.createElement شوند. اولین پارامتر این متد، نوع المانی است که قرار است ایجاد شود که در اینجا h1 است. اما در اینجا دو المان را داریم. در این حالت Babel نمیداند که چگونه باید یک چنین عبارتی را به React.createElement ترجمه کند. یک راه حل این است که کل این عبارت را داخل یک div قرار داد:
render() { return ( <div> <h1>Hello world!</h1> <button>Increment</button> </div> );
return ; <div></div>
return ( <div></div> );
return ( <React.Fragment> <h1>Hello world!</h1> <button>Increment</button> </React.Fragment> );
نکته 2: در VSCode برای ویرایش همزمان ابتدا و انتهای یک تگ (برای مثال ویرایش همزمان عبارت div در اینجا و تبدیل آن به React.Fragment در دو قسمت)، عبارت آن تگ را انتخاب کرده و سپس دکمههای ctrl+d را فشار دهید تا بتوانید همزمان هر دو عبارت انتخاب شده را با هم ویرایش کنید. به اینکار multi-cursor editing میگویند.
نمایش پویای اطلاعات در عبارات JSX
در ادامه بجای نمایش عبارت ثابت «Hello world»، میخواهیم آنرا به صورت پویا تنظیم کنیم. برای این منظور یک خاصیت جدید را در کلاس جاری، به نام state تعریف کرده و آنرا با یک شیء، مقدار دهی میکنیم. state، یک خاصیت ویژه در کامپوننتهای React است و بیانگر دادههایی است که آن کامپوننت نیاز دارد. این دادهها میتوانند یک key/value ساده باشند و یا حتی value تعریف شده نیز میتواند یک شیء پیچیده باشد.
import React, { Component } from "react"; class Counter extends Component { state = { count: 0 }; render() { return ( <React.Fragment> <span>{this.state.count}</span> <button>Increment</button> </React.Fragment> ); } } export default Counter;
همانطور که عنوان شد در بین {}ها میتوان هر نوع عبارت مجاز جاوا اسکریپتی را ذکر کرد و عبارت چیزی است که مقداری را بازگشت میدهد. بنابراین عبارتی مانند {2+2} را نیز میتوان در اینجا بکار برد و یا حتی در اینجا میتوان متدی را فراخوانی کرد که مقداری را بازگشت میدهد:
import React, { Component } from "react"; class Counter extends Component { state = { count: 0 }; render() { return ( <React.Fragment> <span>{this.formatCount()}</span> <button>Increment</button> </React.Fragment> ); } formatCount() { const { count } = this.state; // Object Destructuring return count === 0 ? "Zero" : count; } } export default Counter;
در متد formatCount حتی میتوان عبارات JSX را نیز بجای یک رشتهی ساده، بازگشت داد:
formatCount() { const { count } = this.state; // Object Destructuring return count === 0 ? <h1>Zero</h1> : count; }
مقدار دهی ویژگیهای عناصر در عبارات JSX
فرض کنید یک المان img را به عبارت JSX کلاس Counter اضافه کردهایم. اکنون میخواهیم ویژگی src آنرا مقدار دهی کنیم. در اینجا هر چیزی که بین "" قرار گیرد، به صورت یک رشتهی ثابت پردازش میشود. برای تنظیم آن به یک متغیر، ابتدا خاصیت state را به صورت زیر جهت درج imageUrl، ویرایش میکنیم:
state = { count: 0, imageUrl: "/logo192.png" };
render() { return ( <React.Fragment> <img src={this.state.imageUrl} alt="" /> <span>{this.formatCount()}</span> <button>Increment</button> </React.Fragment> ); }
return ( <React.Fragment> <img src={this.state.imageUrl} alt="" /> <span className="badge badge-primary m-2">{this.formatCount()}</span> <button className="btn btn-secondary btn-sm">Increment</button> </React.Fragment> );
تا اینجا اگر فایل کامپوننت Counter را ذخیره کنید، خروجی ذیل در مرورگر ظاهر خواهد شد:
روش مقدار دهی ویژگی style نیز متفاوت است. در اینجا React انتظار دارد تا شیءای را که به صورت زیر تشکیل میشود:
styles = { fontSize: 50, fontWeight: "bold" };
return ( <React.Fragment> <img src={this.state.imageUrl} alt="" /> <span style={this.styles} className="badge badge-primary m-2"> {this.formatCount()} </span> <button className="btn btn-secondary btn-sm">Increment</button> </React.Fragment> );
اعمال این styles نمونه، یک چنین خروجی را به همراه خواهد داشت:
مزیت تعریف شیء styles به صورت یک خاصیت در کلاس، امکان استفادهی مجدد از آن در سایر المانها است. اگر چنین چیزی مدنظر شما نیست، میتوان این شیء را به صورت inline هم تعریف کرد:
<button style={{ fontSize: 30 }} className="btn btn-secondary btn-sm">
مقدار دهی پویای ویژگی className عناصر در عبارات JSX
در ادامه میخواهیم اگر مقدار count مساوی صفر بود، span ای که هم اکنون با یک badge آبی (با کلاس badge-primary) نمایش داده میشود، زرد رنگ (با کلاس badge-warning) شود و در غیراینصورت آبی رنگ. بنابراین میخواهیم بر اساس مقدر count، مقدار کلاسهای انتسابی به className را به صورت پویا تغییر دهیم و این الگویی است که در برنامههای واقعی بسیار استفاده میشود:
render() { let classes = "badge m-2 badge-"; classes += this.state.count === 0 ? "warning" : "primary"; return ( <React.Fragment> <img src={this.state.imageUrl} alt="" /> <span style={this.styles} className={classes}> {this.formatCount()} </span> <button style={{ fontSize: 30 }} className="btn btn-secondary btn-sm"> Increment </button> </React.Fragment> );
البته باید دقت داشت که میتوان منطق تشکیل متغیر classes را به یک متد، جهت خلوت سازی متد render نیز منتقل کرد. برای این کار، دو سطر مرتبط با متغیر classes را در VSCode انتخاب کنید. سپس یک آیکن لامپ مانند ظاهر میشود که با کلیک بر روی آن، منوی extract to method نیز قابل انتخاب است:
render() { let classes = this.getBadgeClasses(); // ... } getBadgeClasses() { let classes = "badge m-2 badge-"; classes += this.state.count === 0 ? "warning" : "primary"; return classes; }
<span style={this.styles} className={this.getBadgeClasses()}>