- 参考
- 環境構築
- ファイル構成
- Angularの仕組み
- main.ts
- app.component.tsの中身を見てみる
- 新規コンポーネントの作り方
- html内で変数を扱う
- イベントリスナーの設定方法
- Stateの更新(値の更新)
- @Input @Output(親子コンポーネントで値受け渡し)
- for文の書き方(html内)
- if else文の書き方(html内)
参考
Udemyの講座。
英語だけど日本語字幕あり。
環境構築
・Node.jsのインストール
Node.js — Run JavaScript Everywhere
・AngularCLIのインストール
npm install -g @angular/cli
・プロジェクトの新規作成
ng new プロジェクト名
・プロジェクトの起動
npm start
開発用サーバーが起動する。

http://localhost:4200/ にアクセスで画面開く↓

Ctrl+Cでサーバー停止。
既存プロジェクトのインポート
Git等で既存プロジェクトをclone
↓
コマンドプロンプトでプロジェクトフォルダに移動
↓
npm installで依存関係をインストール
↓
npm start で起動
ファイル構成

tsconfig~はtypeScriptをJavaScriptにコンパイルする用の設定ファイル。
package~は依存関係の設定用ファイル。
angular.jsonはAngular提供ツール全般の追加設定。
.editorconfigはコードの書式設定。(文字コードやインデント設定など)
root>srcにあるstyle.cssはアプリ全体に適用するCSS。
root>srcにあるindex.htmlは最初に訪問するページ。
root>srcにあるmain.tsはリクエストが来たときに最初に呼び出される処理。
root>src>app以下のディレクトリにソースコードのファイルを置いていくよ。
Angularの仕組み
index.htmlの中身はほとんど空なのにどうやって画面表示してるの?
index.html
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Essentials</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> </head> <body> <app-root></app-root> </body> </html>
body要素にこれしかない↓
<app-root></app-root>
しかし、実際に画面を開いて検証モードを開くと↓

body要素に色々追加されている。
例えばscriptタグの中で、main.tsが呼ばれているのがわかる。
こんな感じでAngularが良い感じにhtmlを書き換えて、ブラウザが解析可能な形にしているらしい。
main.ts
import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from './app/app.component'; bootstrapApplication(AppComponent).catch((err) => console.error(err));
bootstrapApplication関数は「AppComponent」を引数に取る。
AppComponentとは何なのか?
app.componentをimportしたもののようで↓
import { AppComponent } from './app/app.component';
app.component.tsの中身を見てみる
AppComponentをエクスポートしている。これがmain.tsのbootstrapApplication関数に渡されている。
@Componentはデコレーターと呼ばれるもので、TypeScriptの機能でメタデータを追加するもの。
デコレーターとは?
既存のクラスやメソッドにデコレーションする(=追加機能を入れる)イメージ。
import { Component } from '@angular/core'; @Component({ selector: 'app-root', standalone: true, imports: [], templateUrl: './app.component.html', styleUrl: './app.component.css', }) export class AppComponent {}
@Componentの中身
selector: 'app-root',
これは、html内のどこにレンダリングされるかを示す。
この場合はindex.html内のここ↓

下記の2行は、Componetに適用するhtmlとCSSの場所。
templateUrl: './app.component.html'
styleUrl: './app.component.css'

新規コンポーネントの作り方
AngularCLI使うと簡単。
ng generate component コンポーネント名 ng g c コンポーネント名 --省略形 ng g c users/user --usersコンポーネント以下にuserコンポーネントを作成する場合
VsCodeのターミナルだとエラー出たので、コマンドプロンプトを管理者モードで実行したらいけた。

userコンポーネントを自動で作ってくれる↓

↓
VsCodeならimportも自動で追記してくれる
Quick fixをクリックする↓

tsファイルにUserComponentのimport文が追記された↓

html内で変数を扱う
プロパティバインディング構文。
{{ 変数名 }}
user.component.ts
import { Component } from '@angular/core'; import { DUMMY_USERS } from '../dummy-users'; const randomIndex = Math.floor(Math.random() * DUMMY_USERS.length) @Component({ selector: 'app-user', standalone: true, imports: [], templateUrl: './user.component.html', styleUrl: './user.component.css' }) export class UserComponent { selectedUser = DUMMY_USERS[randomIndex]; //ここ }
user.component.html
<div> <button> <img /> <span>{{ selectedUser.name }}</span> <!-- ここ --> </button> </div>
htmlの属性に対して変数を使う時は
[ 属性名 ] = "変数名"
<button>
<img [alt]="selectedUser.name" [src]="'assets/users/' + selectedUser.avatar" />
</button>
</div>
属性とは、id class alt などHTMLの要素に何かしらの設定をするもの。
属性名=”属性値”
HTMLの属性について | HTML 入門 | レッスン | CreatorQuest
定数の扱い(URLやパスなどhtmlに直書きしたくないもの)
これを書き換えたい。
users.component.html(修正前)
<button>
<img [src]="'assets/users/' + selectedUser.avatar" />
</button>
</div>
user.component.ts
//省略 export class UserComponent { selectedUser = DUMMY_USERS[randomIndex]; // いわゆるgetter。これをhtml側で呼び出す。 get imagePath() { return 'assets/users/' + this.selectedUser.avatar; } }
users.component.html(修正後)
<button>
<img [src]="imagePath" />
</button>
</div>
イベントリスナーの設定方法
1.tsファイルに関数を定義。
onSelectUser() { console.log('Clicked!') }
2.htmlで呼び出し
(イベント) = "関数名()"
<div> <button (click)="onSelectUser()"> </button> </div>
Stateの更新(値の更新)
Stateはコンポーネント内の変数のこと。
つまり、コンポーネント内の変数の更新方法。
ReactだとuseState()→setState()が必要だったところ。
Stateの更新方法1(値の代入)
特別な処理は必要なく値を代入するだけ。
export class UserComponent { selectedUser = DUMMY_USERS[randomIndex]; onSelectUser() { const randomIndex = Math.floor(Math.random() * DUMMY_USERS.length) this.selectedUser = DUMMY_USERS[randomIndex];// 普通に代入するだけ } }
State更新と画面更新の仕組み
Stateが更新されると、アプリ内の全てのコンポーネントをチェックして、画面に変化があるかチェックする。
画面変化がある場合は、画面を更新する。
けっこう重い処理なのだろうか?
Stateの更新方法2(Signal)
ReactのState更新に似た方法。
こちらは変更したStateからAngularに対して、ここのState変更しましたよーとSignalを投げる。
そのため、全てのコンポーネントの変更チェックは不要で処理が軽い。
useer.component.ts
export class UserComponent { // selectedUser = DUMMY_USERS[randomIndex]; //signal関数に置き換え。(ReactのuseStateと同じ) //signal関数は関数を返すので、selectedUserには関数が代入される。 //signalの引数には初期値を渡す selectedUser = signal(DUMMY_USERS[randomIndex]); //signalではgetは使わない。 // いわゆるgetter。これをhtml側で呼び出す。 // get imagePath() { // return 'assets/users/' + this.selectedUser().avatar; // } //getからcomputed関数に置き換え(戻り値は関数) //signalを使う場合、getではなくcomputed関数を使う //computed関数にはsignalが仕込まれており、selectedUserが更新される度に、signalが変更を検知してimagePathも更新される imagePath = computed(() => 'assets/users/' + this.selectedUser().avatar); onSelectUser() { const randomIndex = Math.floor(Math.random() * DUMMY_USERS.length) // this.selectedUser = DUMMY_USERS[randomIndex]; this.selectedUser.set(DUMMY_USERS[randomIndex]);//set関数に置き換え。 } }
user.component.html
selectedUser関数とimagePath関数を呼び出す。
<div> <button (click)="onSelectUser()"> <img [alt]="selectedUser().name" [src]="imagePath()" /> <span>{{ selectedUser().name }}</span> </button> </div>
@Input @Output(親子コンポーネントで値受け渡し)
@Input()は 親コンポーネントから子コンポーネントに値を引き渡す 役割を持ちます。
@Output()は 子コンポーネントから親コンポーネントにイベント(値)を引き渡す。
Angularの@Input(), @Output()を理解する。 #Angular - Qiita
いまいち理解できていない。
@Input例
1,app.component.html(親コンポーネント)から子に[avatar]と[name]の値を渡す。
<app-header /> <main> <ul id="users"> <li> <app-user [avatar]="users[0].avatar" [name]="users[0].name" /> </li> <li> <app-user [avatar]="users[1].avatar" [name]="users[1].name" /> </li> <li> <app-user [avatar]="users[2].avatar" [name]="users[2].name" /> </li> <li> <app-user [avatar]="users[3].avatar" [name]="users[3].name" /> </li> </ul> </main>
2,user.component.ts(子コンポーネントで[avatar]と[name]の値を受け取る
export class UserComponent { //avatar受取。 //avatar!の!はこの変数はundefinedやnullになることはありません、とTypeScriptに教える記述。 //{required: true}は親から子への値渡しを必須にする。渡さないとエラー出るようになる便利機能。 @Input({required: true}) avatar!: string; @Input({required: true}) name!: string; //name受取 get imagePath() { return 'assets/users/' + this.avatar; //avatarの値をimagePathに代入 } onSelectUser() {} }
3,user.component.htmlで表示
<div> <button (click)="onSelectUser()"> <img [src]="imagePath" [alt]="name" /> <span>{{ name }}</span> </button> </div>
画面はこんな感じ↓

@InputをSignal版に書き換えよう
user.component.ts
import { Component, computed, Input, input } from '@angular/core'; export class UserComponent { // @Input({required: true}) avatar!: string; // @Input({required: true}) name!: string; //@Inputを置き換え avatar = input.required<string>(); name = input.required<string>(); // get imagePath() { // return 'assets/users/' + this.avatar; // } //getを置き換え imagePath = computed(() => { return 'assets/users/' + this.avatar(); }) onSelectUser() {} }
Signalを使ったほうが処理が軽いらしい。
@Output例
ちょっとわかりにくいので、こちらのリンクを見るほうが良い。
Angularの@Input(), @Output()を理解する。 #Angular - Qiita
■要点
- @Outputで定義した変数は親コンポーネントでイベントとして使用できる。ここではapp.component.htmlの
(select)のこと。 - イベントが発火すると、まず子コンポーネントのイベントが発火。ここではuser.component.htmlの
(click)="onSelectUserChild()" - 次に子コンポーネントのonClickChild関数で、
this.select.emit(this.id)により親にidを渡す。 - idは親コンポーネントの
$eventに渡される。 - 次に親コンポーネントで
(select)="onSelectUser($event)"が発火。コンソールにidが表示。
ボタンを押すと、コンソールにidを表示する処理↓

import { Component, EventEmitter, Input, Output } from '@angular/core'; export class UserComponent { @Input({required: true}) id!: string; @Input({required: true}) avatar!: string; @Input({required: true}) name!: string; @Output() select: EventEmitter<string> = new EventEmitter(); //@Output()は宣言時、 EventEmitterをインスタンス化 します。 get imagePath() { return 'assets/users/' + this.avatar; } onSelectUserChild() { return this.select.emit(this.id);//emitは放出という意味。親にidを渡す。 } }
user.component.html(子コンポーネント)
<div> <button (click)="onSelectUserChild()"> //最初にこっちが発火 <img [src]="imagePath" [alt]="name" /> <span>{{ name }}</span> </button> </div>
app.component.ts(親コンポーネント)
export class AppComponent { users = DUMMY_USERS; onSelectUser(id: string) { console.log('Selected user with id ' + id); } }
app.component.html(親コンポーネント)
<app-header /> <main> <ul id="users"> <li> <app-user [id]="users[0].id" [avatar]="users[0].avatar" [name]="users[0].name" (select)="onSelectUser($event)"//子の後にこっちが発火 /> </li> <li> <app-user [id]="users[1].id" [avatar]="users[1].avatar" [name]="users[1].name" (select)="onSelectUser($event)" /> </li> <li> <app-user [id]="users[2].id" [avatar]="users[2].avatar" [name]="users[2].name" (select)="onSelectUser($event)" /> </li> <li> <app-user [id]="users[3].id" [avatar]="users[3].avatar" [name]="users[3].name" (select)="onSelectUser($event)" /> </li> </ul> </main>
for文の書き方(html内)
usersリストを表示する。
<ul id="users"> @for (user of users; track user.id) { <li> <app-user [user]="user" (select)="onSelectUser($event)" /> </li> } </ul>
■trackとは?
一意のキー(ユニークキー)をtrackに指定する必要がある。
レンダリング効率化のための機能。
リストが変更される度にリスト全体を再作成するのではなく、既存のリストを再利用するためにtrackを指定する。
Reactでも同じ機能があった。
ngFor(昔の書き方)
htmlだけでなく、tsファイルでimportも必要で手間が多い。
app.component.html
<li *ngFor="let user of users"> <app-user [user]="user" (select)="onSelectUser($event)" /> </li>
htmlだけに追加すると下記のメッセージが出る。templateではngFor使ってるけど、Moduleでimportしてないよ的なの↓

app.component.ts(必要部分だけ抜粋)
importを追加。
import { NgFor } from '@angular/common'; //ここ @Component({ selector: 'app-root', standalone: true, imports: [HeaderComponent, UserComponent, TasksComponent, NgFor], //ここ templateUrl: './app.component.html', styleUrl: './app.component.css', })
if else文の書き方(html内)
JavaScriptでは「0, null, undefined」はfalseになる。
@if (selectedUser) {
<app-tasks [name]="selectedUser.name"></app-tasks>
} @else {
<p id="fallback">Select a user to see their tasks!</p>
}
ngIf(昔の書き方)
参考↓
【Angular】ngIfディレクティブとは?「使い方」や「プログラム例」などを解説! - IT Information
*ngIf="selectedUserがtrueなら<app-tasks>~</app-tasks>"が表示される。
*ngIf="selectedUserがfalseなら<ng-template #noSelectUser>~</ng-template>"が表示される。
app.component.html
<app-tasks *ngIf="selectedUser; else noSelectUser" [name]="selectedUser.name"></app-tasks> <ng-template #noSelectUser> <p id="fallback">Select a user to see their tasks!</p> </ng-template>
app.component.ts(必要部分だけ抜粋)
importを追加。
import { NgFor, NgIf } from '@angular/common';//ここ @Component({ selector: 'app-root', standalone: true, imports: [HeaderComponent, UserComponent, TasksComponent, NgFor, NgIf],//ここ templateUrl: './app.component.html', styleUrl: './app.component.css', })