Angularで画面を作ってみる

まだコンポーネント化もできていない状態。
ある程度の形ができたら、要素を解説しつつまとめる予定。

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { FlexLayoutModule } from '@angular/flex-layout';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatMenuModule } from '@angular/material/menu';
import { MatSelectModule } from '@angular/material/select';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatIconModule } from '@angular/material/icon';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatListModule } from '@angular/material/list';
import { MatTreeModule } from '@angular/material/tree';

import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    FlexLayoutModule,
    MatButtonModule,
    MatFormFieldModule,
    MatInputModule,
    MatMenuModule,
    MatSelectModule,
    MatSidenavModule,
    MatIconModule,
    MatToolbarModule,
    MatListModule,
    MatTreeModule,
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.html

<mat-toolbar color="primary">
  <mat-toolbar-row>
    <button mat-icon-button>
      <mat-icon (click)="drawer.toggle()">menu</mat-icon>
    </button>
    <h1>SITE</h1>
    <span class="menu-spacer"></span>
    <div>
    </div>
  </mat-toolbar-row>
</mat-toolbar>

<mat-drawer-container class="sidenav-container">
  <mat-drawer #drawer class="sidenav" fixedInViewport="true"
    [attr.role]="'dialog'"
    [mode]="'over'"
    [opened]="!(isHandset$ | async)"
  >
    <mat-toolbar>Menu</mat-toolbar>
    <mat-nav-list>
      <mat-tree [dataSource]="dataSource" [treeControl]="treeControl" class="example-tree">
        <!-- This is the tree node template for leaf nodes -->
        <mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle>
          <li class="mat-tree-node">
            <!-- use a disabled button to provide padding for tree leaf -->
            <button mat-icon-button disabled></button>
            {{node.name}}
          </li>
        </mat-tree-node>
        <!-- This is the tree node template for expandable nodes -->
        <mat-nested-tree-node *matTreeNodeDef="let node; when: hasChild">
          <li>
            <div class="mat-tree-node">
              <button mat-icon-button matTreeNodeToggle
                      [attr.aria-label]="'Toggle ' + node.name">
                <mat-icon class="mat-icon-rtl-mirror">
                  {{treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right'}}
                </mat-icon>
              </button>
              {{node.name}}
            </div>
            <ul [class.example-tree-invisible]="!treeControl.isExpanded(node)">
              <ng-container matTreeNodeOutlet></ng-container>
            </ul>
          </li>
        </mat-nested-tree-node>
      </mat-tree>
    </mat-nav-list>
  </mat-drawer>
  <mat-drawer-content>
    <div fxLayout="column" style="height:calc(100vh - 128px)">
      <div fxFlex="48px" class="v-center" style="background-color: royalblue">
        <ng-container *ngFor="let link of links">
          <a href="#"><span>{{link}}</span></a>
        </ng-container>
      </div>
      <div fxFlex class="v base">
        <ng-container *ngTemplateOutlet="pack; context:args"></ng-container>
      </div>
    </div>
  </mat-drawer-content>
</mat-drawer-container>

<ng-template #pack let-class="class" let-params="list">
  <div [class]="class">
    <ng-container *ngFor="let param of params">
      <ng-container [ngSwitch]="param">
        <ng-container *ngSwitchCase="'i'"><ng-container *ngTemplateOutlet="input"></ng-container></ng-container>
        <ng-container *ngSwitchCase="'b'"><ng-container *ngTemplateOutlet="button"></ng-container></ng-container>
        <ng-container *ngSwitchDefault><ng-container *ngTemplateOutlet="pack; context:param"></ng-container></ng-container>
      </ng-container>
    </ng-container>
  </div>
</ng-template>

<ng-template #input>
  <div class="b">
    <span class="l"><label for="cheese">ラベル</label></span>
    <span class="i"><input name="cheese" value="インプットエリア"></span>
  </div>
</ng-template>
<ng-template #button>
  <div class="b">
    <button mat-button color="primary">確定</button>
  </div>
</ng-template>

app.component.css


a {
    display: flex;
    justify-content: left;
    align-items: center;
    border-radius:0px;
    width: 100px;
    height:100%;
    background: linear-gradient(to right, royalblue, royalblue 10%, skyblue);
}

a::before{
    content: "";
    width:18px;
}

a::after{
    content: "";
    width:24px;
    height:48px;
    position: relative;
    left:20px;
    background:linear-gradient(to bottom left, royalblue 50%, transparent 50%) top left/ 100% 50% no-repeat,
    linear-gradient(to top left, royalblue 50%, transparent 50%) bottom left / 100% 50% no-repeat;
    z-index: 1;
}

div.base {
    background-color:lightslategray;
    padding:5px;
}

div.area {
    background-color:lightgray;
    padding:5px;
}

div.v {
    display: flex;
    flex-direction: column;
    width:100%;
}

div.h {
    display: flex;
    flex-direction: row;
    width:auto;
}

div.b {
    display: flex;
    border-color:transparent;
    border-width:0.5px;
    border-style:solid;
    height:40px;
    width:100%;
}

div.v div.h div.b+div.b{
    margin-left: 5px;
}

div.h div.v div.b+div.b{
    margin-left: 0px;
}

div.v+div.v{
    margin-left: 5px;
}

.area div.v+div.v{
    margin-left: 15px;
}

div.h+div.h{
    margin-top: 5px;
}

.v-center {
    display: flex;
    align-items: center;
    padding-left:25px;
}

span.l {
    display: flex;
    justify-content: center;
    align-items: center;
    background-color:royalblue;
    padding:1px;
    height:38px;
    width:100px;
    min-width:100px;
}

span.i {
    display: flex;
    justify-content: center;
    align-items: center;
    background-color:aliceblue;
    padding:1px 3px;
    height:38px;
    width:100%;
}

input {
    border-width:1px;
    border-color:white;
    border-style:inset;
    height:35px;
    width:100%;
}

label {
    color:white;
}

.sep {
    height:10px;
    width:10px;
}

@media screen and (max-width: 640px) {
    div.h {
        flex-wrap: wrap;
    }
    div.v+div.v {
        margin-left: 0px;
    }
    div.v div.h div.b+div.b{
        margin-left: 0px;
    }
    .area div.v+div.v{
        margin-left: 0px;
    }
}

.example-tree-invisible {
    display: none;
}

.example-tree ul,
.example-tree li {
    margin-top: 0;
    margin-bottom: 0;
    list-style-type: none;
}

app.component.ts

import { Component } from '@angular/core';
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { NestedTreeControl } from '@angular/cdk/tree';
import { MatTreeNestedDataSource } from '@angular/material/tree';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

interface FoodNode {
  name: string;
  children?: FoodNode[];
}

const TREE_DATA: FoodNode[] = [
  {
    name: 'Fruit',
    children: [
      {name: 'Apple'},
      {name: 'Banana'},
      {name: 'Fruit loops'},
    ]
  }, {
    name: 'Vegetables',
    children: [
      {
        name: 'Green',
        children: [
          {name: 'Broccoli'},
          {name: 'Brussels sprouts'},
        ]
      }, {
        name: 'Orange',
        children: [
          {name: 'Pumpkins'},
          {name: 'Carrots'},
        ]
      },
    ]
  },
];

interface Container {
  layout: string;
  children?: Container[];
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent {
  title = 'ng-test';
  links = ["LINK1", "LINK2", "LINK3"]

  v(...params:any[]):any {
    return {class:"v", list:params}  
  }

  h(...params:any[]):any {
    return {class:"h", list:params}  
  }

  va(...params:any[]):any {
    return {class:"v area", list:params}  
  }

  ha(...params:any[]):any {
    return {class:"h area", list:params}  
  }

  args = 
  this.v(
    this.h(
      this.va("i","i","i","i"),
      this.va("i","i","i"),
    ),
    this.ha(
      this.v(
        "i","i","i"
      ),
      this.v(
        "i","i","i","i"
      ),
    ),
    this.ha(
      "i","i","i"
    ),
    this.h(
      this.va(
        this.h("i","b"),
        "i","i"
      )
    )
  )

  isHandset$: Observable<boolean> = this.breakpointObserver.observe(Breakpoints.Handset)
  .pipe(
    map(result => result.matches)
  );

  constructor(private breakpointObserver: BreakpointObserver) {
    this.dataSource.data = TREE_DATA;
  }

  treeControl = new NestedTreeControl<FoodNode>(node => node.children);
  dataSource = new MatTreeNestedDataSource<FoodNode>();
  hasChild = (_: number, node: FoodNode) => !!node.children && node.children.length > 0;
}

サーバにあるテキストをマスタとした連想配列を提供するサービス

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable({ 
  providedIn: 'root'
})
export class MapService {
  nameMap:Observable<{[name: string]: string}>

  constructor(private http: HttpClient) {
    let url = "../assets/sample.txt"
    this.nameMap = this.http.get(url, {responseType:'text'}).pipe(map(data => {
      return data.split('\n')
      .map(x => x.split("="))
      .reduce((map, arr) => ({
        ...map,
        [arr[0]]: arr[1]
      }), {});
    }));
  }

  getValue(name) {
    return this.nameMap.pipe(map(x => x[name]))
  }
}
タイトルとURLをコピーしました