vaguely

和歌山に戻りました。ふらふらと色々なものに手を出す毎日。

【TypeScript】type の使いどころが知りたい話

はじめに

※2020/04/12
お前の仕事はいつまで続くんだという話ですが、ようやく書き終わりました。。。

この記事は TypeScript Advent Calendar 2019 の 6 日目の記事です。

ふとコードを書いていて気になったのが、例えばサーバー側から受け取った値を JSON に変換したいとき、 class を使うべき? interface を使うべき? それとも type ?ということでした。

C# であれば class を選択することになります。

では TypeScript の場合は?

というか、 C# にも存在する class と interface はともかく、 type って何よ?どう使うの?となったので調べてみることにしました。

type について

まず type (正確には Type Aliases) とは何か、という話から。

alias (別名)の名前通り、型に対する別名をつけるためのものです。

使い方はこんな感じです。

type SampleType = string;
let sample: SampleType = "hello";

type SampleType2 = {
    id: number,
    name: string,
}
let sample2: SampleType2 = {
    id: 0,
    name: "world",
}

interface と type aliases

共通点

よく比較される interface と type aliases ですが、実際使い方によっては全く区別がつかない場合もあります。

interface SampleInterface {
    id: number;
    name: string;
}
let sample3: SampleInterface = {
    id: 1,
    name: "!!!",
}

どちらも JavaScript への変換後は消えてしまうためあくまで TypeScript 内での振る舞いに留まる点や、同じプロパティを持っていればその interface / type aliases として扱われる点など。

よく言われる(?)のが、(type aliases は継承ができないため)継承が必要なら interface を使う、という内容。

ver.3.8.3 現在、Classが実装する(implements)場合はどちらも可能です。

interface ISample{
    name: string;
}
type TSample = {
    message: string
};
/** OK */
class IClassSample implements ISample {
    public name: string = 'Hello'
}
/** OK */
class TClassSample implements TSample {
    public message: string = 'World'
}

interface / type aliases で違いはあるのか?その使い分けは?というのが気になったので調べてみることにしました。
(口調がいかがでしたかブログっぽいな)

違い

declaration merging

interface の大きな特徴(だと思う)は "declaration merging" です。
同じ namespace 、同じファイル、同じモジュール内に同名の interface がある場合、ひとまとまりの interface として扱われます。

interface ISample{
    name: string;
}
interface ISample{
    message: string;
}
function main() {
    // 1つ目、2つ目の ISample が統合される.
    const iSample: ISample = {
        name: 'Hello',
        message: 'World'
    };
}

同様のことは type alias で Cross 型を使って再現することはできますが、明示的に書く必要があります。


interface ISample{
    name: string;
}
type TSample = {
    message: string
};
type TCrossSample = ISample & TSample
function main() {
    const tSample: TCrossSample = {
        name: 'Hello',
        message: 'World'
    };
}

extends

これも interface のみの特徴ですが、 interface は interface や type aliases を拡張 (extends) できます。


interface ISample{
    name: string;
}
type TSample = {
    message: string
};
interface IExtendSample extends ISample, TSample {
    id: number;
}
function main() {
    const iSample: IExtendSample = {
        id: 0,
        name: 'Hello',
        message: 'World'
    };
}

これも同じく type alias で Cross 型を使って再現できます。

定義できる型

interface で定義できるのは下記のような形式のみです。
(propertyName はなくてもOK)


interface InterfaceName {
    propertyName: Type
};

type aliases はあくまで型に別名をつけるだけ、ということもあってか、色々な種類の型を定義できます。


interface ISample{
    name: string;
}
type TSample = {
    message: string
};
/** Union type */
type TUnionSample = ISample|TSample;
/** Cross type */
type TCrossSample = ISample & TSample & { id: number; }
/** Tuple */
type TTupleSample = [string, number];
/** Function */
type TFunctionSample = (message: string) => void;
/** Object */
type TObjectSample = {
    message: string
};
/** Mapped type */
type TBase = {
    id: number,
    name: string
};
type TMappedSample = { [P in keyof TBase]: string; };
/** Conditional type */
type TConditionalSample = T extends string? 'string': 'other';
function main() {
    const crossSample: TCrossSample = {
        id: 0,
        name: 'Hello',
        message: 'World'
    };
    const tupleSample: TTupleSample = ['hello', 0];
    const mappedSample: TMappedSample = {
        id: '0',
        name: 'Hello'
    };
    const conditionalSample: TConditionalSample = 'string';
}

Union 型、 Tuple は type aliases のみで定義できるため、 interface は使えません。

いつ type aliases を使うべきか?

interface と type aliases の違いはわかった気がしますが、 ではいつ type aliases を使うか。
(どちらも使用可能な場合)

Effective TypeScript によると、プロジェクトのスタイルに合わせる・または "declaration merging" が必要かどうかで判断するべき、と。

個人的には C# に慣れていることもあり、振る舞い(メソッド)を定義して Class で実装する場合は interface 、Data Transfer Object の用に、データを格納するためのオブジェクトは type aliases を使いたい気がします。

参照

ASP.NET Core + Entity Framework Core のプロジェクトを 2.2 から 3.0 にアップグレードした話

はじめに

この記事は C# Advent Calendar 2019 の二日目の記事です。

冷静に考えるとこれを C# の話として書いていいのか?と今更ながら思ったりもするのですが、プロジェクトは C# で書いてるし、 C# に関連する話もないではないから許してください(..)_

環境

  • .NET Core : ver.2.2.402 (from), ver.3.0.100 (to)
  • Microsoft.EntityFrameworkCore : ver.2.2.6 (from), ver.3.0.0 (to)
  • Npgsql.EntityFrameworkCore.PostgreSQL : ver.2.2.4 (from), ver.3.0.1 (to)
  • Windows10 : ver.1903
  • Rider : ver.2019.2.3
  • PostgreSQL : ver.12.0

元のプロジェクト

まずはアップグレードする前のプロジェクトを用意します。

UpgradeSample.csproj

<Project Sdk="Microsoft.NET.Sdk.Web">
    <PropertyGroup>
        <TargetFramework>netcoreapp2.2</TargetFramework>
        <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
        <RuntimeIdentifier>win-x86</RuntimeIdentifier>
    </PropertyGroup>
    <ItemGroup>
        <PackageReference Include="Microsoft.AspNetCore.App" />
        <PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
        <PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.6" />
        <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.2.4" />
    </ItemGroup>
</Project>
  • RuntimeIdentifier は Self-contained app (実行環境に .NET Core が入っていなくても動作するよう出力されたアプリケーション) のために追加しています。
  • Program.cs も存在しますが、特にプロジェクト作成時から変更なく、 3.0 にアップグレード後も触らないため省略しています。

Startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json.Serialization;
using UpgradeSample.Models;
using UpgradeSample.Products;

namespace UpgradeSample
{
    public class Startup
    {
        private IConfigurationRoot Configuration { get; }
        public Startup(IHostingEnvironment env)
        {
            // config ファイル読み込み.
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", false, true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", false, true)
                .AddEnvironmentVariables();
            Configuration = builder.Build();
        }
        public void ConfigureServices(IServiceCollection services)
        {
            // DB Connect
            services.AddDbContext<UpgradeSampleContext>(options =>
                options.UseNpgsql(Configuration["DbConnect"]));

            // 生成される JSON のプロパティ名を大文字始まりで出力する.
            services.AddMvc()
                .AddJsonOptions(options =>
                {
                    options.SerializerSettings.ContractResolver = new DefaultContractResolver();
                });
            // DI
            services.AddScoped<IProductService, ProductService>();
        }
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseStaticFiles();
            app.UseMvc();
        }
    }
}

ApiController.cs

using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using UpgradeSample.Models;
using UpgradeSample.Products;

namespace UpgradeSample.Controllers
{
    public class ApiController: Controller
    {
        private readonly IProductService _productService;
        public ApiController(IProductService productService)
        {
            _productService = productService;
        }
        [Route("/")]
        [Route("/Home")]
        public string Index()
        {
            return "hello";
        }
        [HttpGet]
        [Route("/products")]
        public async Task<List<Product>> GetProducts([FromQuery] string[] names)
        {
            return await _productService.GetProductsAsync(names);
        }
    }
}

Product.cs

using System.ComponentModel.DataAnnotations.Schema;
using Newtonsoft.Json;

namespace UpgradeSample.Models
{
    [Table("Product")]
    public class Product
    {
        [Column("ProductId")]
        [JsonProperty("ProductId")]
        public int? ProductId { get; set; }

        [Column("ProductName")]
        [JsonProperty("ProductName")]
        public string ProductName { get; set; }
    }
}
  • JsonProperty は今回あまり役に立ってはいませんが、 ASP.NET Core プロジェクト内と JSON で名前が異なるときなどに活躍します。

UpgradeSampleContext.cs

using Microsoft.EntityFrameworkCore;

namespace UpgradeSample.Models
{
    public class UpgradeSampleContext: DbContext
    {
        public UpgradeSampleContext(DbContextOptions<UpgradeSampleContext> options)
            :base(options)
        {
        }
        public DbSet<Product> Products { get; set; }
    }
}

IProductService.cs

using System.Collections.Generic;
using System.Threading.Tasks;
using UpgradeSample.Models;

namespace UpgradeSample.Products
{
    public interface IProductService
    {
        Task<List<Product>> GetProductsAsync(string[] names);
    }
}

ProductService.cs

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using UpgradeSample.Models;

namespace UpgradeSample.Products
{
    public class ProductService: IProductService
    {
        private readonly UpgradeSampleContext _context;
        public ProductService(UpgradeSampleContext context)
        {
            _context = context;
        }
        public async Task<List<Product>> GetProductsAsync(string[] names)
        {
            return await _context.Products
              .Where(p => names.Length <= 0 ||
                  names.Any(n => p.ProductName == n))
              .ToListAsync();
        }        
    }
}

いかん。。。元のコードを並べるだけで結構スペースを取ってしまった。。。

3.0 にアップグレードする

気を取り直して早速 3.0 にアップグレードしてみますよ。

プロジェクトのバージョンを更新するには、 1 つまたは 2 つのファイルを変更する必要があります。

global.json

{
  "sdk": {
    "version": "3.0.100"
  }
}

UpgradeSample.csproj

<Project Sdk="Microsoft.NET.Sdk.Web">
    <PropertyGroup>
        <TargetFramework>netcoreapp3.0</TargetFramework>
        <RuntimeIdentifier>win-x86</RuntimeIdentifier>
    </PropertyGroup>
    <ItemGroup>
        <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.0.0" />
        <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.0.1" />
    </ItemGroup>
</Project>
  • global.json は Rider でプロジェクトを作った場合は出力されていましたが、 Powershelldotnet new empty を実行して作った場合はありませんでした。
  • Rider ではもう一つ、 Run/Debug configuration のバージョンも上げる必要があるかもしれません。
  • これらのファイルを変更後、自動で実行されない場合は dotnet restore を実行します。

エラーを修正する

メジャーバージョンアップということで、主に下記に関連してエラーや警告が出ました。

  1. Endpoint Routing への変更
  2. Newtonsoft.Json から System.Text.Json への変更
  3. Entity Framework Core で、 Linq のクエリをクライアントで実行しない
  4. RuntimeIdentifier がロードできない

  5. Migrate from ASP.NET Core 2.2 to 3.0 - Microsoft Docs

  6. What's new in ASP.NET Core 3.0 - Microsoft Docs
  7. Comparing Startup.cs between the ASP.NET Core 3.0 templates: Exploring ASP.NET Core 3.0 - Part 2 - Andrew Lock | .NET Escapades
  8. My First Look at ASP.NET Core 3.0 - Shawn Wildermuth

1. MVC から Endpoint Routing への変更

2.2 ではルーティングを使うのに AddMvc, UseMvc を使っていましたが、 3.0 からは AddControllers, UseRouting, UseEndpoints に変わります。

Startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Identity;
using Models;
using UpgradeSample.Models;
using UpgradeSample.Products;
using UpgradeSample.Users;

namespace UpgradeSample
{
    public class Startup
    {
        ...
        public void ConfigureServices(IServiceCollection services)
        {
            ...            
            // for setting JSON parameter names pascal case.
            services.AddControllers()
                .AddJsonOptions(options =>
                    options.JsonSerializerOptions.PropertyNamingPolicy = null);
            // DI
            ...
        }
        public void Configure(IApplicationBuilder app, IHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseStaticFiles();
            
            app.UseRouting();
            app.UseEndpoints(endpoints =>
            {
                // [Routing("")] を使ってルーティングする場合
                endpoints.MapControllers();
            });
        }
    }
}

また今回は使用していませんが、認証などを行う場合、 UseRouting の後、 UseEndpoints の前に UseAuthentication, と UseAuthorization を記載順に実行する必要があります(順序が違ったり UseAuthentication が実行されていないと実行時に例外が発生します)。

操作しようとした人が誰かを確認した後、その人がその操作を実行できるか?を確認する、ということで、考えれば順序がわかるのですが、実行時に例外が発生するという辺りはちょっとややこしいですね。

2. Newtonsoft.Json から System.Text.Json への変更

3.0 にアップグレードしたときにコンパイルエラーが発生したのは、この Json 関連だけでした。

今回のサンプルで対象となるのは 2 箇所です。

Startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Identity;
using Models;
using UpgradeSample.Models;
using UpgradeSample.Products;
using UpgradeSample.Users;

namespace UpgradeSample
{
    public class Startup
    {
        ...
        public void ConfigureServices(IServiceCollection services)
        {
            ...            
            // for setting JSON parameter names pascal case.
            services.AddControllers()
                .AddJsonOptions(options =>
                    options.JsonSerializerOptions.PropertyNamingPolicy = null);
            // DI
            ...
        }
        ...

Product.cs

using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;

namespace UpgradeSample.Models
{
    [Table("Product")]
    public class Product
    {
        [Column("ProductId")]
        public int? ProductId { get; set; }
        [Column("ProductName")]
        [JsonPropertyName("ProductName")]
        public string ProductName { get; set; }
    }
}

基本的な使い方は変わらず、書き方が微妙に違っている、という感じです。

3. Entity Framework Core で、 Linq のクエリをクライアントで実行しない

個人的には一番影響が大きいのでは?と思っているところです。

Entity Framework Core で、 Linq を使ったコード( Where )が SQL に変換できない場合、例外を発生するようになりました。

これによってパフォーマンスの悪化が防げる、というメリットと、 List を Linq を使って検索するつもりで雑にアクセスしていると実行時例外を発生させまくるというデメリットがあります。

ProductService.cs

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using UpgradeSample.Models;

namespace UpgradeSample.Products
{
    public class ProductService: IProductService
    {
        ...
        public async Task<List<Product>> GetProductsAsync(string[] names)
        {
            // 実行時に例外発生
            return await _context.Products
              .Where(p => names.Length <= 0 ||
                  names.Any(n => p.ProductName == n))
              .ToListAsync();
        }        
    }
}

対策としては、 Any など SQL に変換できない条件で検索したい場合、先に AsEnumerable を実行する、というのが考えられます。

ProductService.cs

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using UpgradeSample.Models;

namespace UpgradeSample.Products
{
    public class ProductService: IProductService
    {
        ...
        public async Task<List<Product>> GetProductsAsync(string[] names)
        {
            return await Task.Run(() => 
            {
              return _context.Products
                .AsEnumerable()
                .Where(p => names.Length <= 0 ||
                  names.Any(n => p.ProductName == n))
                .ToList();
            });
        }        
    }
}

ややこしいのが、例えば Where の中身が例えば Where(p => p.ProductId == id) のような内容なら( id は引数となどで渡されているとして)わざわざ AsEnumerable を含む必要がない、というところ。

全部に AsEnumerable を含む、というのはおかしな気がするので、できるだけ Any などを使わず、 SQL に変換できるように書くのが良い。。。のかしら?

BeginTransaction

今回使用していませんが、 C# 8 で using 句に await が使えるようになった影響で、トランザクション開始時に実行する _context.Database.BeginTransaction() が await できるようになりました。

await using (var transaction = _context.Database.BeginTransaction())

public async Task SetUserNameAsync(ApplicationUser user, string userName, CancellationToken cancellationToken)
{
    await using var transaction = _context.Database.BeginTransaction();
    try
    {
      user.UserName = userName;
      _context.Users.Update(user);
      _context.SaveChanges();
      transaction.Commit();
    }
    catch (Exception e)
    {
        transaction.Rollback();
    }
}

Rider 先生曰く await using(var transaction = _context.Database.BeginTransaction()){ } より上記のように {} を使わない書き方を推奨、ということで、なぜかしら?と思っていたのですが、どうも単にネストを浅くしようとしているだけのようです。

4. RuntimeIdentifier がロードできない

3.0 にアップグレード後、 win-x86 のパッケージ?がロードできない旨の警告の後、全 C# クラスがエラーになる問題が発生してビビったという話です。

あれこれ試してみた結果、リストア -> 再ビルド で解決しました。

Single-file executable

また RuntimeIdentifier に関連して? Single-file executable というものが追加されています。

これまで Self-contained app で出力すると大量にファイルが出力されていたのですが、この設定をしておくとファイル数がぐっと少なくなります。

UpgradeSample.csproj

<Project Sdk="Microsoft.NET.Sdk.Web">
    <PropertyGroup>
        <TargetFramework>netcoreapp3.0</TargetFramework>
        <RuntimeIdentifier>win-x86</RuntimeIdentifier>
        <PublishSingleFile>true</PublishSingleFile>
    </PropertyGroup>
    <ItemGroup>
        <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.0.0" />
        <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.0.0" />
        <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.0.1" />
    </ItemGroup>
</Project>

これで通常通り dotnet publish -c Release -r win-x86 を実行すると下記ファイルが出力されます。

おわりに

まだおっかなびっくり試しているのと、慌ててやるのはダメゼッタイというところではありますが、壊滅的な状況になることもなく 3.0 に上げることができました。

サポート期間的なこともありますが、パフォーマンスなど多くの改善点が含まれているだけに、早めに更新していきたいところです。

C# Advent Calendar, 明日は @takayoshitanaka さんです。

よろしくお願いいたします(..)_

Angular(ver.8.2) のページを IE11 に対応させたい話

はじめに

この記事は Angular #2 Advent Calendar 2019 一日目の記事です。

以前からちょろちょろ触っていた Angular ですが、そろそろ試しに使ってみたい、ということで、実践を意識して(どうにも逃れられない) IE11 の対応に挑戦してみたいと思います。

Environment

  • npm : ver.6.13.1
  • Angular : ver.8.2.14
  • Angular-cli : ver.8.3.19
  • VSCode : ver.1.41.0-insider

IE11 でページを表示する

デフォルトでは真っ白のページしか表示されないので、まずは表示されるようにしてみます。

といってもやったことは下記に従っただけです。

一応変更した内容を挙げておきます。

  1. プロジェクトに tsconfig-es5.app.json というファイルを追加
  2. angular.json で 1.が読み込まれるよう変更
  3. ng serve --configuration es5 で実行( package.json の Scripts に追加しても良い)

2.について、サンプルを丸コピーしたらプロジェクトが見つからないとエラーになりました。

angular.json

"serve": {
  "builder": "@angular-devkit/build-angular:dev-server",
  "options": {
      ...
  },
  "configurations": {
    "production": {
     ...
    },
    "es5": {
      "browserTarget": "app:build:es5" ← ここでエラー。 app をプロジェクト名にする必要がある。
    }
  }
},

Grid layout を有効にする

Angular には 以前 PostCSS で利用していた Autoprefixer が組み込まれており、 Flexbox などは IE でもそのまま使用できます。

ただ、 Grid Layout は対応されていない(多分)ため、個別に対応が必要です。

PostCSS の場合は postcss.config.js に書いていたわけですが、 Angular の場合は CSS に下記を追加します。

/* autoprefixer grid: autoplace */ 
#grid-frame{
    height: 30%;
    width: 90%;
    display: grid;
    grid-template-columns: 10% 10%;
    grid-template-rows: 15% 85%;
}
...

これで IE11 でも Grid Layout が有効になります。

ただし、この状態だと grid-column と grid-row が聞かず、 要素を横に grid-template-columns の数だけ並べ、超過すると改行、という動作となります。

autoplace の部分を変更すれば良さげですが。。。

背景色に Alpha のある色を設定する

まぁ Angular 独自の話ではないのですが。。。

000000AA のように、 background-color で RGBA で色指定をしてしまうと、 IE では色が表示されません。

rgba(r, g, b, a) を使って指定する必要があります。

#frame-background{
  background-color: rgba(17, 17, 17, 0.67);
}

PostCSS を使っていたときは自動で変換してくれていたので、つい見逃していました orz

Pikaday を使う

ChromeFirefox などのブラウザでは input type="date" とすれば Date Picker が使えるわけですが、 IE ではそうはいかない、ということで、以前使用した Pikaday を使えるようにしたいと思います。

本当は Angular 用に用意された Date Picker もあるようなので、そちらにした方が便利なのかもしれませんが、仕事でも既に Pikaday を使っているため、まずはそちらを試してみます。

Install

npm install --save moment pikaday @types/pikaday

正直なところ、基本的な使い方はほぼ一緒です。

CSS(pikaday.css) は各画面共通ということで styles.css でインポートすることにしました。

styles.css

@import '../node_modules/pikaday/css/pikaday.css';

あとは Pikaday に関連付けるだけです。

今回は top-page というコンポーネントで使うことにしました。

top-page.component.ts

import { Component, OnInit } from '@angular/core';
import * as Pikaday from 'pikaday';

~省略~

export class TopPageComponent implements OnInit {

~省略~

  ngOnInit() {
    let picker = new Pikaday({ 
      field: document.getElementById('datepicker') 
    });
  }
}

top-page.component.html

<input type="text" id="datepicker">

Data bindings

さて、せっかくの Angular なので、値の受け取りは Two-way data binding を使いたいところ。

が、何も考えずに [(ngModel)]="dateText" のように書いてしまうと、 Pikaday で値が入力された場合にイベントが発火せず、 dateText の中身は空のままです。

この原因は上記で設定されているイベントが (input) であるためで、これを (change) にしてやる必要があります。

top-page.component.html

<input type="text" id="datepicker"
  [value]="dateText" (change)="dateText=$event.target.value">

おわりに

基本的には自動で良い感じにしてくれる、ということで大変ありがたいのですが、 Angular に任せておきさえすればすべてうまくいく、というわけでもない辺りがちょっと辛いですね。
(主に IE の特に CSS 周りの対応)

まぁあれこれ地雷を踏み抜きながら覚えていくしかないか。。。

個人的には JS Framework 無しから Angular に切り替えて、一番嬉しいのは HTML の部分を TypeScript(JavaScript) ではなく HTML として書ける、ということかなと思っています。

コードで生成しても良いのですが、規模が大きくなってくるとやっぱり分かりづらい。。。

React や Vue だとまた違っていると思うので、そちらも試してみたいなとは思っています。

さて Advent Calender ですが、明日は ringtail003 さんです。

よろしくお願いいたします(..)_

JSConf China 2019 in 上海参加記録( 2 日目)

はじめに

こちらも途中ですが、 JSConf China 2019 2日目の内容をメモっておきます。

※ 2019/11/09 会場でメモった内容をほぼそのまま書いています。
意味不明な部分や聞き間違い、解釈違いなど多数あると思います。

もうすぐ資料や動画が配信されるはずなので、その後更新する予定です。

会場

  • 9:55開始
  • 開場時点での参加者数は昨日より少ない。
    • チケットのやりとりで Keynote に間に合わないのを恐れたため?(つまり自分と一緒)
    • 開始時点でも昨日より少なかった(昨日飲みすぎ?)
  • PC自体は共通のMacを使ってるっぽい
  • 中国語セッションは 3/8

0. Opening

  • 開発者同士の交流、ということを再度強調

Keynote. Learn as I write the Docs

資料

昨日のSessionについて

  • 各Sessionの良いところをかんたんな概要に触れながら。

フロントエンド、React を学んできた4年間

  • 新しい技術にふれるとき、たくさんのDocsを読むことになる

4 years as Front End

  • 最初は何もわからなくてもとにかく楽しかった。
  • 3年目、より多くを得るために、Conferenceに参加
  • Redux
  • React Redux

自分自身について。

  • 十分なキャリアがあるか?
  • 英語の解釈問題
  • 書くことの難しさ

Richard Feynman

  • Names don't constitude knowlege

  • tool や library は開発者を助けてくれる

  • いつ、どこで学ぶか?

  • 誰かに教えることは、学ぶための最も良い方法

    • Docsを書くことはこのことに繋がる
    • どう書けば相手(ユーザー)に伝わるか、を考えなければならないから

tackling the challenge

  • 英語で書くこと。
    • 細かい Typo やミスなど気にしない
    • 完璧じゃなくても良い
    • Tipsの共有による練習
    • thesaurusのようなツールを使う
    • proof reading
    • 他の人に説明する
    • 1-1 sharing
    • 習慣にすること
  • メンテナと直接やり取りできる素晴らしさ
  • ライブラリのアーリーアダプターになる
  • チームにおけるドメインエキスパートになること

Docs

2. Serverless is your BFF

start

  • かんたんなアバターを作るサービス モバイル、PCそれぞれからBFFに接続-> 各Serviceに

Webの歴史

  • 1.0はとてもシンプル
  • 2.0はたくさんのサービス・機能が生まれた。
    • Front end/backend に別れた
    • Frontendはツールやフレームワークの登場によりかんたんに。
    • Fullstack

Full stack?

  • UI -> Product
    • 仕様ぎめ、開発、Ops
  • Node.jsによりFrontend、Backendで再利用できるコードも増えた
    • ただしFrontend、Backendのやり取りは依然困難。
  • Backend はとても複雑
  • Scalable, Security, Debuggable...
  • Prototype -> Production architectureに移るに当たり、気にするものがいっぺんに増える
  • Autoscale

Cloud computing

  • すべて自前のマシンで用意するOn-premissから、FunctionsのみのFaaSへ。

    • ビジネスにフォーカスする
  • Node.js

    • Faas
      • SCF
  • tencent cloud でどのようにアップロードしているかデモ

    • アップロードした写真に帽子をつける
  • Ergonomic Upload code and go

Serverless is economic

  • reduce team size
  • 使っただけ払う

VMだと必要なときもそうでないときも同額かかる

Serverless is reliable

summary

  • Serverless の紹介。Webの歴史、良いところを取り上げ、Tencent cloudを使うデモも。
    • Live coding, Live debugging

3. Entropic package registry

What is package manager?

  • Registory <--> client
  • Do install work?
  • publish permission

What is package?

  • Softwareをひとまとめにしたもの
  • NPMなどであつかう
  • Npm registry -> npm -> disk
  • disk -> webpack -> bunndle ひとまとめに

Node.js

  • Node.jsも一つの file manager
    • disk -> Node.js -> commonjs
    • 動的に package manager を動かす?

yarn

  • cache -> yarn -> Pnt resolution

Plugin

  • loader of Node.js

Tink

  • Disk にキャッシュする

Recap

基本的に

  • 全部 Package managerを使う。

Entropic

  • Entropic は Package を一箇所にまとめるのではなく、個別に分かれている
  • それらが連携しあって動く。

4. Redux Based Modular Design

Slide

Motivation

  • build large app
  • Modular -> 模块化
  • サイズが大きくなってくると、
    • Combine reducers,Middleware, connection
    • 機能ごとにクラス分け

modularity

困難

Pros

  • CDのしやすさ Divide, conquer
  • テストの完全自動化

Motivation & Goal

Redux Cons

  • Immutable data
    • 実現が難しい?
    • verbose update operations
  • Boilerplate Code が増える
  • Modularity
    • Loose, difficult
    • ロジックが分割しづらい(UIに入り込んでるとか)

Solution

usm-redux

  • Reducer
    • @state decorateを使って状態管理、
    • @action で処理を定義。関数につく。
      • 状態を変える
      • Router 的な動きが実現できるので、switchなどを取り除ける
      • 引数にstateを追加

        Action type

        dispatch

  • イベント処理の分割

Module Dependency

  • Module inject
  • Customizable Iocのようなものをつかう

Immutable data update

immer
  • 今の値をベースにImmutable stateを作る

benefit

  • 便利
  • かんたんにカプセル化できる
  • IoCなどは自分で好きなものを選べる
  • Reduxの原則や動きを引き継ぐ

5. Diving into TypeScript design metadata with NestJS

Slide

memo

Design-time metadata

  • Reflection

Parameter type metadata

  • extract type
  • DI, Type -> Nest.js feature

DIの説明

  • @Injectable() をつけることで、複数のDependencyを解決できる

Compiler metadata

decorate( [metadata('design:parametypes', [HttpService, Logger])], CatsService,

Reflection API

Reflect.getMetadata('design:parametypes',CatsService)

Pipes

  • castなども使われる
  • PipeTransform{ transform(

    ) }

    Reflection API challenges

    interfaces

  • 依存interfaceの場合、JSにしたときはObjectとなる

Potential solution

  • abstruct solution

    Generics

  • Genericsの場合にどう解決する?
    • JSにするとクラス情報はなくなってしまう *

      Circular dependency

  • 依存クラスがJS変換時にundefinedに
    • @Inject( =>

Type metadata

export class User{ email: string; name: string; } ↓ export class User{ @Property email: string; @Property name: string; }

複数プロパティがあるとき Reflect.getMetadata('design: type, new User(), 'email')だと正しくmetadata がundefined になってしまい、Typeがとれない

@Propertyを使う

What's the point

AST

TypeScript Plugins

Return type metadata

  • OpenAPI gRPC, GraphQL mutations

戻り地がPromise< string>のとき、Reflect.getMetadata が Promiseになる。stringは?

Recap

  • design-time metada

  • TODO: news letter 登録

6. Writing code to refactor code

Slide

概要

はじめに

  • Zenhubでは CommonJS-> ES6に変更した。
  • 1752files
  • 5 years old

Large scale refactoring のながれ(失敗)

  1. Find and Replace
  2. どうにもならん\(^o^)/

ASTの紹介

  • astexplore.net (AST Explorer)で見ることができる
  • JSON形式になる

codemod

  • find & replace inspect the code

    • Prettier
    • ESLint autofix
  • webpack, babel transpilationはcodemodではない

  • Codemod in action
  • Transforming requires to import
    • CommonJSの var a = require("doge.js").wow => import { wow as amused } from "doge.js"
  • iTerm2
  • npx codemod としてるから、npm でインストールするツールか?
  • from が同じものがある場合、combine?

  • ASTはリファクタリングのときにも使える

  • Codemodだけでなく、StaticTypesなどいろんなツールがある

7. JavaScript 的执行 & 性能

Slide

はじめに

  • a.b = 123 と a[' b'].b のパフォーマンス差?
  • esparse
  • V8と? の比較
    • d8でどう解釈されているか
  • BinaryExpression

bytecode

  • staGlobal
  • jerry --show-opcodes add.js
  • V8での解釈・実行の流れや発生したパフォーマンス問題など

WebAssembly

JavaScript の歴史

V8

  • V8によって、JavaScriptがWebフロントエンドでの立場を不動のものにした?

8. 去除迷雾, 漫谈 WebAssembly

  • C/C++, Go などの複数のから WAコードを生成
  • ブラウザ上では実行できない?

WAはJavaScriptにとって代わるか?

  • 補うもの
  • JSで直接WebAssemblyを書くことはできない

実行速度はあまり変わらない?

  • V8の場合、JS -> AST -> ignition -> turofan -> local machine code
  • WA(.wasm) -> Liftoff -> TurboFan -> local machine code

パフォーマンスは変わらない?

  • WAはDownload, Decodeを同時にするのでパフォーマンスが上がる
  • 最適化

WA moduleの構成

  • function signature
  • run time

おすすめの言語

  • Rust
    • WA を推し進めているMozillaが作っている言語

Demo

  • 書くときは普通にRustをかく。実行時はバイナリコードに。
  • バイナリコードに変換する?ツールあり。

Panel discussion

翻訳するときに、ネイティブのような表現ができない

  • Googleなどで表現を調べる、ドキュメントを見てみる
  • 英語から中国語に直すときも苦労する

問題のあるパッケージが混ざった場合にどう対応するか

  • npmなどと同じ?

Nest.jsでInversifyJSを使わない理由?

  • InversifyJS自体は使っていないから実際のところはわからないが、 使い方が違う?
  • 少なくともInversifyJSなど他のものを使うことは進めない

ASTによるリファクタリングが向いてないとき?

CommonJS -> ES6には使えるが、JS -> TSではASTによるリファクタリングが有効か?

  • おそらく

Jerry.js

WAのセキュリティ上の問題など?将来的な問題が発生する恐れはある?

  • すでに対応はしている

ドキュメントが人に伝わらないとき?

  • 言い方を変えてみる、細かく説明してみる
  • 時間をあけてもう一度自分で読んでみる。
    • 理解できる?

Middleware of NestJS?

  • Expressのものと同じ?
  • HTTPアプリケーションを作るときには依存する

Nest.js は GraphQL に対応している?

  • Yes.
  • GraphQL のデータの作り方は2種類あり、Code firstとData first?がある

WAはマルチスレッドなどの対応している?

  • している?が使ったことはない
  • 基本的にもとの言語に依存する
  • ブラウザの機能を使う?

CherryScript Quick.js?

Entrophic Production ready?

  • No
  • いつ? -> まだわからなそう

JSConf China 2019 in 上海参加記録( 1 日目)

はじめに

間が空いてしまいましたが、 JSConf China 2019 1日目の内容をメモっておきます。

※ 2019/11/06 会場でメモった内容をほぼそのまま書いています。
意味不明な部分や聞き間違い、解釈違いなど多数あると思います。

もうすぐ資料や動画が配信されるはずなので、その後更新する予定です。

会場

まずは会場の様子から。

f:id:mslGt:20191109063304p:plain

f:id:mslGt:20191109063325p:plain

ステージ

  • 中央にステージと大きなスクリーンがあり、左右に少し小さめのスクリーンがありました。
  • 英語セッションのときは中央のスクリーンの両端に、中国語訳がつけられていました。

椅子など

  • パイプ椅子が並んでおり、机は最前列のみ設置されていました。
    (長時間座っているとお尻が痛い(´・ω・`))
  • 端の方には休憩スペースがあり、休憩時間にはコーヒーを飲んだり転がったり。。
  • 休憩時間中、席に PC を置いて行く方がちらほら。信頼のあらわれ?面倒だっただけ?
  • みんな Mac を使っていたのでちょっと欲しくなるなど。

参加者について

  • 男女比は 9:1 くらい。参加者はざっと 300 人ぐらい。
  • 欧米系の方もちらほら見られた。スタッフをしてくれている方も。

各セッションの言語

  • 8 割くらい英語で行われていた。国際的なカンファレンスだから?海外ゲストにもわかるように?
  • ただし、観客の方は(自分の周囲だけかもだが)最後の方はしんどそうだった。わかる。わかるよ。
  • パネルディスカッションのときは司会の方が通訳したりもしていたが、間に合わず端折るところも。ですよね~。

Opening & Opening Note

  • 今年で 10 年目 & 7 回目(毎年開催しているわけではない)
  • 10年前には React も GraphQL もなかった。
  • 中国内の各地で開催しており、上海の他北京、深センなどでも。
  • カンファレンスの理念は、開発者同士でコミュニケーションを取ること。
    • Web をめぐる開発環境はどんどん進化して便利になっているが、コミュニケーションについては十分とは言えない
  • Opening にて Wechat でグループを作り、皆で参加する。
    • 人数は 120 人くらいに
    • 質問、休憩時間などでの連絡、終了後の連絡などで使われている

Keynote. Continuous learning & leadership evolution

Video

※このカンファレンスのものではありませんが、同じタイトルで別のカンファレンスで話ししている動画があったため載せておきます。

Optimized for cost

  • deliver technology
  • stranger pattern
  • deliver value team
  • move up team
  • 将来的に何を選ぶ
  • 客やこれまでの経緯など、とりまく環境の共有

チームをどうまとめていくか

  • チームごとに担う仕事はバラバラである。特に QA チームは完全に開発チームから離れている
  • これらをどうまとめていくか

  • product model

  • A3 problem solving
  • Speed to value
  • DevOps
  • 計測しないと解決できない
  • focus just speed
  • 新しい環境に移ったら、90 日で慣れる?
  • 実際のグラフ、カンバン?の様子なども見せてくれていた。
  • Roleも大切
  • global technology
  • 他のチームに入っててこの役割
  • data with context drives clear
  • sharing success
  • 以前は失敗とはネガティブなものだった
  • who made change
  • 組織ごとに異なる
    • それに合わせる
  • leadership need evolve

2. The beauty of TypeScript

Slide

最も良い言語は何か?

  • 状況による。例えば 128kb RAM の環境なら C が選ばれるだろう
  • フロントエンドなら JavaScript/TypeScript
  • 高开发なら GolangJava (C#Python の人気は今ひとつのよう)

どういう状況だと TypeScript が一番輝くか?

  • 開発者が多い
  • 開発する項目数が多い
  • TypeScript ならどんな JavaScript も TypeScript として扱うことができる

TypeScript の使用状況

  • 使ったことのある人は八割くらい。
  • 殆どの業務で TypeScript を使う人は四割くらい

JavaScript の辛いところ

JavaScript のプロジェクトを TypeScript に変換するときの便利ツール?

  • コードに下記を追加すると VS Code で処理を実行してくれるらしい

@ts-ignore

  • TypeScript に変換したときのコンパイルエラーを消してくれる

@ts-nocheck

  • @ts-ignore の処理をまとめて実行してくれる

@ts-check

  • JavaScript のコードに対して実行し、 TypeScript に変換したときにコンパイルエラーになるところを検出してくれる。

3. ClojureScript - Winning through simplicity

Problem of JavaScript

  • increase complexy
  • デバッグしにくい
  • window Scope -> TypeScriptと同じような問題

Choose simplicity over complexity

Compile はBavel で慣れてるでしょ?

Simplest language?

  • Lisp!: (function argument)
  • プログラムしないのが最強
  • s-Expression
  • tree/graph

Lisp

  • Lisp はシンプルだけでなく古い(歴史がある)
  • John McCarthy

Paul Graham

Brendan Eich

Clojure

ClojureScript

  • Clojure compiled to JS
  • リリースはRichHickeyによりOctober 2007に
  • Functional
  • Recursion
  • メジャーなエディターなら対応してる
  • インデントが意味を持つ(Python的な)
  • REPL: Command line
  • EDN

    Database

  • Datomic
  • DataScript
  • Datalog

  • bnomis (Simon Blanchard) - GitHub

summary

4. Modern GraphQL workflow

Slide

これまで

  • サーバーはリクエストに対して全データを返す
  • 必要なのはそのうちの一部(ほかは無駄になってしまう)

GraphQL

  • 必要な分だけ返す
  • リクエスト側にも見分けるための仕組みがある?

  • graphql

  • graphql-compose
  • merge-graphql-schemas
  • graphql-middleware
  • apollo-server

GraphQL Joker

summary

  • GraphQL の紹介(REST -> GraphQL への流れから)
  • GraphQL Joker の紹介

5. Building (Progressive) Web Apps

  • MaaS

What the hack PWA

  • Fast
  • Integrated
    • touch, GPS, etc.
  • Reliable
  • Engaging

Why PWAs?

  • 使用時間のうち87%がNative Appsを使っている

Shun the non PWA

  • ネイティブ的にテーマカラーに合わせるとか、そもそもJavaScriptがオフにされていたら無理
  • Offline時にレスポンスが遅い

Dev tool

Being Reliable

Angularの場合

  • ng add @angular/pwa
  • React, Vueもかんたんに入れられる

Workbox

Being Faster

Being Engaging & Interactive

6. 面向传统, Serverless 进化之路

Slide

memo

  • 阿里巴巴のサービスをServerlessにしたときの効果、苦労や手順など。
  • BFF 70%
  • Node.js
  • マシンや人の負荷を下げたい
  • すでに過去の遺産がたくさんある
  • Serverless への移行は 2018/10 に準備を開始した
    • 淘宝网,飞猪大BU に反映
  • コストは 100% -> 70% -> 40%
  • Ops のリソース
  • Dev 側は開発完了後はリソース不要(本当に減らすとリスクが高いため減らすことはできない)
  • Note.js のバージョン固定
  • 研究開発のリソース
  • 多能工
  • 開発者、部署が少なくてもできる
  • サーバーへの負荷軽減
    • 今までは Controller がひとまとめに受けてきたが、 Edge -> 業務ロジック -> データ と個別にアクセスできる
      • 負担軽減へ
  • クラウド環境での統合テスト
  • アップグレードモデルの研究開発
  • これまでの期間は一年、まだ完了はしていない
  • FaaS + BaaS
  • Egg / Mildloway Server
  • Http Server

7. Evolution of Quality, Support, Releases at NIKI

How we've transformed in 5 years

  • modernization - microservices / cloud
  • continous delivery
  • follow the sun / GEO and global
  • cultural shift

Memo

  • 綿密なテストを重ねて失敗を責める文化 -> 早く小さく失敗する文化へ
  • DevOps
  • Engineering Mindset
  • SRE
  • Fire Fighting
  • Share Ownership

From -> To

  • 長くテストをして長い時間をかけてリリースしてた -> CI/CD による早いリリース
  • 手動でテスト -> 自動化、Global Execution?、On demand test environments, sv

8. 响应式编程使你看得更远

Slide

過去話

  • RxJsは今までも毎回テーマに上がっていたらしい。どんな新しい知識が得られるか?といった紹介からスタート

Rxについて

Memo

  • callback -> promise
  • async/await
  • Message Driven / Responsive / Resilient / Elastic
  • Observables
  • Observer
  • MergeMap -> Stream に流れる○をマージする?
  • throttleTime(25)
  • debounce & throttle
  • switchmap - 競合問題の解決?
  • CombineLatest
    • withLatestFrom
  • TakeUntile
    • Cold Observable
    • Hot Observable
  • Share with "Subject"
  • rxjs-hooks
  • redux-observable
  • Cycle.js
  • 人と境界

Panel Discussion

概要

  • Wechat で参加者から集めた質問を、登壇者に投げかける
  • 質問は英語だったり中国語だったり
  • 司会の方が通訳をしていたが、端折ったり翻訳しないことも(まぁ大変すぎですよね)

TypeScript をどのように導入するか

  • 一度に全部切り替えるのではなく、一部から期間を決めて導入する
  • 阿里巴巴では大部分で TypeScript を使っている(ここ三年くらい)
  • 導入のメリットを鑑みる
    • APISDK で何をしたいのかが伝わりやすい
    • ただしまだハードルが高い場合がある
  • 新人も助ける

ClojureScript が強いところ

  • 特定の場面で、というわけではない
  • 複雑なデータや仕組みをもつものに使うのが良い

GraphQL

  • fluent
  • filtering
  • reasonable request
  • DB にとっても良いところ?

How the computability of PWAs

GraphQL には swagger や OpenAPI のような標準がある?

  • Browse Schema であり、 API ではないのでそのようなものはないのでは?

ThrottleTime と DebounceTime との違い?

JSConf China 2019 in 上海参加記録( 0 日目:移動日・上海旅行編)

はじめに

10/19~10/20 に上海で行われた、 JSConf China 2019 に参加してきましたので、メモっておくことにします。

といっても今回は 10/18 の移動 + ひさびさに上海(南京東路 - 人民広場あたり)を旅行した話です。

グレートファイアウォールを乗り越える

中国に来るときに面倒なのがグレートファイアウォールの存在。

TwitterGoogle などのサービスが使えないと、それに依存している私のような人間は困ってしまう、と。

一般的に使用されるのは VPN で、友人におすすめされた 12VPN というのを一ヶ月使えるようにしてみました。

また、モバイル Wi-Fi をイモトで借りることにしたのですが、中国プレミアム回線という TwitterYoutube が使えるサービスがある、ということで、そちらも試してみることにしました。

結果ですが、 WiFi の方だけで問題なく接続できていました。

ただ、専用回線?に接続詞にいっているためなのか、つながるまでに一分程度待つ必要がありました。

まぁ無理やり接続しているものには変わりないと思うので、その辺は仕方がないですね。

というわけで VPN はあまり使いませんでしたが、 Google Hangout などがつながらない場合に VPN を使うといける、という場合もあったため、まぁ両方ある方が安心かな~といったところ。

なお、500MB/日 という容量は、画像などのやりとりをしていると結構すぐ超えてしまうため、心配な場合はもう少し多いプランにしておくのが良さそうです。

キャッシュレス決済

中国ネタでよくあがる(特に中国スゴイ的な内容の場合)キャッシュレスの話。

せっかくだし使ってみたくはあったのですが、中国の銀行アカウントや携帯番号がないと利用できないという問題が( JSConf のチケット購入もこれで困りました)。

ということで、現金のみで頑張ることにしました。

結果としては、まぁなんとかなる、という感じです。

地下鉄の切符購入や食事など、問題なくできました。

ただ、キャッシュレス決済の方が明らかに便利そうな様子を見ながら現金払い(日本のICOCA 的な交通カードも現金だと購入できず)、という若干の辛さとか、お、お前キャッシュレス決済じゃないのか(まぁ中国に住んでる同年齢の人ならほとんど使っているでしょうから)的に言われる煩わしさはありました。

単に気にするかどうかぐらいのところではありますが。

本屋(上海書城)

初日は若干時間があった、ということで、本屋にも行ってみることにしました。

プログラミング関連の本の傾向は日本と違うのでしょうか。

Golang は数は少ないものの、 JSConf のセッション中の話によると、結構使われているようです。

ということで、状況は日本とあまり変わらないようです。

写真

あとは旅行っぽく写真でも。

f:id:mslGt:20191018144553j:plain

f:id:mslGt:20191024074313j:plain

f:id:mslGt:20191024074551j:plain

雑に Angular 再入門(Tour of Heroes App) 1

はじめに

色々あったりなかったりして、 Angular をもう一度触ってみることにしました。

Angular 2 の時代にハンズオンに参加したとはいえ、あれからずいぶん経っているわけなので、もう一度一からやってみるぞい!と思ってサイトを確認したところ、 Angular 8 の今も Tour of Heroes App は現役のようです。

(GETTING START が別にあるのですが、こちらはあらかじめ用意された環境を触ってみる、という感じだったので後にまわすことにしました)

ということで、気になるところは寄り道しつつツアーにお出かけしてみることにします。

準備

まずは準備から。

といっても TypeScript 触っていた影響から Node.js の環境はそろっているため、必要なものは Angular CLI だけです。

npm install -g @angular/cli

VS Code拡張機能としては Angular Language Service だけ入れてみました。

で、プロジェクトを作成します。

ng new angular-tour-of-heroes
cd angular-tour-of-heroes

※本当は ng-sample のような雑な名前にしていましたが、とりあえず合わせてみました。

Error

複数台で試したのですが、以前入れていたものが悪かったのか、 ng new の途中でパーミッションがないとエラーになりました。

が、 npm -g update のあと Angular CLI をインストールしなおしたら直りました。

Angular routing、CSS Framework

ng new を実行すると、 Angular routing を追加するかと CSS Framework を使うかを聞かれます。

  • CSS
  • SCSS
  • Sass
  • Less
  • Stylus

今回はルーティングはツアー中に追加するため追加せず、 CSS もサンプルコードに合わせて素の CSS を選択しました。

以前 PostCSS を使いましたが、 CSS Framework も近いうちに試したいところ。

実行してみる

とりあえず実行してみます。

ng serve --open

--open をつけることで、起動後に Web ブラウザでページを開いてくれます。便利!

...は良いのですが、

f:id:mslGt:20191009190554j:plain

Oh... ナンデコンナニゴウカナノ?

レスポンシブデザインにもなっているという気の利きよう。

どこかの Web Framework を思い出しました。

Empty テンプレートのようなものはあるのでしょうか。

空のページを作る

とりあえず今回は手動で空のページを作ってみます。

とはいえ、実は変更が必要なファイルは一つだけです。

  • src > app > app.component.html

CSS も含め先ほど表示された内容はすべて HTML ファイルに書かれているため、(若干の罪悪感はありつつも)丸ごと削除してやれば空のページが出来上がります。

まぁテスト( app.component.spec.ts )は失敗するようになるわけですが、一旦放置の方向で。

ともあれ、さっそく順番に進めていきますよ。

Directive って何

Introduction の、このチュートリアルを通してできるようになることの中に下記があります。

Angular のビルトインの Directive を使ってヒーローリストの要素を表示・非表示する

...Directive ってナンデスカ?

Angular.js のドキュメントでは

Angular のドキュメントでは Directive 自体についての説明が見つからず、 Angular.js 時代のドキュメントでは、DOM 要素に Angular 特有のふるまいを持たせるためのマーカー、となっていました(と認識)。

@Directive の説明に DOM 要素にカスタムのふるまいを持たせるのに @Directive が使える、というような説明があるため、 Directive の役割自体はあまり変わっていないのだと思われます。

あまり深入りしてもアレなので話を次に進めますが、 Directive には大きく3種類あります。

なぜ Component だけ Component "directive" でないのか、という疑問もありますが、それは置いといて、 Component から順に調べてみますよ。

Component

この 3 種類の中で最も頻繁に使用される Component 。

上記 Getting Started に出てくるもののことを指しているのだとすると、 ng generate component {Component 名} で生成される、下記 3 つのファイルからなる Component となります。
(テストファイルである heroes.component.spec.ts も生成されますが、 Component としては含まれないようです)

ng generate component heroes の場合

  • Component class (heroes.component.ts)
  • HTML template (heroes.component.html)
  • Component-specific styles (heroes.component.css)

まどろっこしい言い方をしているのは、 Component class の中に下記があるためです。

heroes.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
    ~省略~
}

どっちを指しているのか迷うところですが、前者は app.module.ts の @NgModule > declarations に Component class が定義され、ベースとなる HTML (app.component.html) に < app-heroes>< /app-heroes> が追加されることで動作します。

app.module.ts

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

import { AppComponent } from './app.component';
import { HeroesComponent } from './heroes/heroes.component';

@NgModule({
  declarations: [
    AppComponent,
    HeroesComponent,
  ],
~省略~

app.component.html

< h1>{{title}}< /h1>
< app-heroes>

強引に .NET に例えるならば下記のようになるでしょうか。

  • Component class -> コードビハインド
  • HTML template -> XAML
  • Component-specific styles -> デザインファイル

先ほどの Directive の役割が DOM 要素に Angular 特有のふるまいを持たせる、というところから見れば、やはり前者を指していると考えてよさそうです。
(間違ってたら訂正します)

Component-specific styles

とりあえず知りたいことは山ほどあるわけですが、比較的簡単に確認できそうなところから。

ページ全体に影響するようなスタイルは styles.css に書きますが、特定の Component のみに影響するようなものは Component-specific styles に書くと。

最終的には HTML として出力するわけですが、その上で他の Component に影響しないようにする方法とは? と思ったので、とりあえず試してみました。

heroes.component.css

.heroes {
    margin: 0 0 2em 0;
    list-style-type: none;
    padding: 0;
    width: 15em;
}

app.component.html

< app-heroes>
< div class="heroes">hello< /div>

こうするとどうなるか。

heroes.component.cssheroes クラスは、下記のように変換されます。

.heroes[ _ngcontent-vyr-c1] {
    margin: 0 0 2em 0;
    list-style-type: none;
    padding: 0;
    width: 15em;
}

そして app-heroes 以下の要素は(例えば div があったとすると)下記のようになります。

< app-heroes>
    < div _ngcontent-vyr-c1>hello< /div>

で、 app.component.html に書いた div はこうなると。

< div _ngcontent-vyr-c0>hello< /div>

ということで CSS のクラス名が異なるため適用されない、と。

なるほどねぇ~。

一旦切ります。

(まぁこういうことしているから終わらないんだよねぇ~。楽しいから良いのだけれども)