狛ログ

2018年12月11日火曜日

Angular5から7へのバージョンアップ・前編

12月 11, 2018

オフィス狛 技術部のKoma(Twitterアカウントの中の人&CEO)です。

少しAngularから離れていたのですが、過去のプロジェクトをアップデートする事になったので、備忘録で作業内容を残しておこうと思います。(他にもいくつかのプロジェクトでも同じ事する必要があるので・・・・)

基本的な作業としては、こちらの手順通りになります。 バージョンは(Angular Version)は、5.2から7.0でいきます。
アプリの複雑度(App Complexity)は・・・・Mediumぐらいですかね、とりあえず。
その他の依存関係(Other Dependencies)ですが、今回のプロジェクトは、AngularJSも混在していませんし、 Angular Materialも使用していないので、チェックは付けません。
パッケージ管理(Package Manager)はnpmを使っているので、npmを選択します。

・・・・・ん???
Warning: We do not recommend moving across multiple major versions.
複数のメジャーバージョンを跨いだアップデートはオススメしない??
・・・・まあ、モノは試し、オススメしない方法でとりあえずやってみよう。

「Show me how to update!」を押すと、下記のように手順が表示されるので、順番にやっていこうと思います。

更新する前に(Before Updating)

・・・これ、もしかして、アプリの複雑度(App Complexity)によって出てくる内容違うのでは?と思ったら、案の定違うので、念の為「Advanced」の方の記載も見ておいた方が良いかもしれません。
今回のプロジェクトはこのまま「Medium」で行きます。

まずは、
If you import any animations services or tools from @angular/core, you should import them from @angular/animations
です。
以前(Angular2ぐらい?)のバージョンでは、animations系の処理はcoreに含まれてたので、それは使えないよ、と言う事ですね。
まあ、今回は元のバージョンがAngular5なので、気にする必要はなさそうです。

続いての
Switch from HttpModule and the Http service to HttpClientModule and the HttpClient service. HttpClient simplifies the default ergonomics (You don't need to map to json anymore) and now supports typed return values and interceptors. Read more on angular.io
です。
これも似たような事ですが、HttpModuleは使えないからHttpClientModuleに変更してね、と言う事ですね。
HttpClientModuleは、確かAngular 4.3 ぐらいから使えるようになった機能です。

最後の
Choose a value of off for preserveWhitespaces in your tsconfig.json to gain the benefits of this setting, which was set to off by default in v6.
です。
Angular5から追加になったpreserveWhitespacesは、tsconfig.jsonに記載するのですが、これをoff(false)にする事で、コンパイル時に余計な空白を削除することができます。 Angular6から、これはデフォルトoff(false)だよ、と注意を促してくれています。

今回は全て問題ないので、そのまま次の手順に進んで行きます。

更新作業(During the Update)

Make sure you are using Node 8 or later

Node のバージョンは8以降である必要があります。
コマンドラインやターミナル等で「node -v」を実行して確認しましょう。

Update your Angular CLI globally and locally, and migrate the configuration to the new angular.json format by running the following:

続いて、Angular CLIの最新をインストールします。
以降は、バージョンアップを行いたいプロジェクトディレクトリで実行して下さい。
npm install -g @angular/cli
npm install @angular/cli
ng update @angular/cli
グローバルのAngular CLIを更新した後、ローカルの更新を行い、最後にローカルのパッケージの更新を行なっています。
ただ、上記「npm install @angular/cli」だとローカル側のAngular CLIのバージョンが変わらなかったので、「npm install @angular/cli@latest」として最新をインストールしました。

正しく動いていると、
DELETE .angular-cli.json
CREATE angular.json (4321 bytes)
こんな感じで、旧バージョン形式である「angular-cli.json」が削除され、新バージョンの「angular.json」が作成されているはずです。

その後、もう一度「ng update @angular/cli」を行なっています。

ちなみに、プロジェクトでngrxを使っていると、モジュールに対してのワーニングが出ます。
ここでは無視して構いません。

Update any scripts you may have in your package.json to use the latest Angular CLI commands. All CLI commands now use two dashes for flags (eg ng build --prod --source-map) to be POSIX compliant.

package.jsonに記載されているscriptsは、最新のコマンドで更新してね、と言っていますね。
よくあるのが、「"build": "ng build --prod",」とかで、コマンドを簡略化するパターンですが、
最終的なコマンドはAngular CLIなので、そこは最新のコマンドに書き換えする必要があります。

Update all of your Angular framework packages to v6, and the correct version of RxJS and TypeScript.

続いて、Angular自体のアップデートを行います。(下記のコマンドを実行)
ng update @angular/core
注意書きとして、
After the update, TypeScript and RxJS will more accurately flow types across your application, which may expose existing errors in your application's typings
とあります。
AngularをUpdatesする事で、型チェック等が厳密になる為、既存のプログラムでエラーが出る可能性があるよ、と言う事ですね。まあ、その辺は想定内というか、多分別のところでエラーになるでしょう・・・。

ngModelChange is now emitted after the value/validity is updated on its control instead of before to better match expectations. If you rely on the order of these events, you will need to begin tracking the old value in your component.

Angular6以前でngModelChangeを使用している場合、実装方法によっては、変更前の値が取れてしまう問題がありましたが、
それがAngular6で修正されているので、ngModelChangeを使用しているところは注意してね、という事です。

Use ng update or your normal package manager tools to identify and update other dependencies.

先程は、Angular自体のアップデートを行ったので、個々のパッケージの依存関係は「ng update」などで更新してね、という事です。
という訳で、
ng update --all
をやっておきましょう。

ただ、ngrxを使っている場合はエラーでupdate出来ないと思います。
Package "@ngrx/store" has an incompatible peer dependency to "@angular/core" (requires "^6.0.0", would install "7.1.1")

ここは一旦強制的にupdateしてしまいます。
ng update --all --force
ng updateの度に、
@ngrx/store@6.1.2 requires a peer of @angular/core@^6.0.0 but none is installed. You must install peer dependencies yourself.
のようなワーニングは出てしまうのですが、
6.1.2はangular7に対応はしているので、一旦このままで行きます。
関連: https://github.com/ngrx/platform/issues/1397

If you have TypeScript configured to be strict (if you have set strict to true in your tsconfig.json file), update your tsconfig.json to disable strictPropertyInitialization or move property initialization from ngOnInit to your constructor. You can learn more about this flag on the TypeScript 2.7 release notes.

TypeScript 2.7から追加されたstrictPropertyInitializationについての記載です。より厳密な初期化チェックを行うかどうかのフラウですが、元々から存在しているstrictが有効になっている場合、サブセットであるstrictPropertyInitializationも有効になってしまうので、tsconfig.jsonで無効にするか、厳密なチェックに対応するようプログラムを変更してね、という事です。
(プロパティの初期化は、ngOnInitではなく、コンストラクタでやらないといけない)

更新後作業(After the Update)

Remove deprecated RxJS 6 features using rxjs-tslint auto update rules For most applications this will mean running the following two commands:

deprecatedされた機能を削除する、という事で、
これは、指示通り、以下のコマンドを実行します。
npm install -g rxjs-tslint
rxjs-5-to-6-migrate -p src/tsconfig.app.json
そうすると、変更されたファイルが表示されます。
Fixed 5 error(s) in /Users/officekoma/hoge-web-prj/src/app/auth/services/hoge1.service.ts
Fixed 1 error(s) in /Users/officekoma/hoge-web-prj/src/app/auth/services/hoge2.service.ts
// (中略)
Fixed 5 error(s) in /Users/officekoma/hoge-web-prj/src/app/home/store/effects/my-page.effect.ts
Fixed 5 error(s) in /Users/officekoma/hoge-web-prj/src/app/home/store/effects/my-page-edit.effect.ts

WARNING: /Users/officekoma/hoge-web-prj/src/app/auth/services/hoge1.service.ts[3, 1]: duplicate RxJS import

Once you and all of your dependencies have updated to RxJS 6, remove rxjs-compat.

続いて、rxjs-compatを削除します。
npm uninstall rxjs-compat

If you use the Angular Service worker, migrate any versionedFiles to the files array. The behavior is the same.

Angular Service workerを使っている場合もマイグレーションを行う必要があるようですが、
今回のプロジェクトでは使用していないので、特に何もしません。

という事で、5.2から7.0へのアップデートを行いました。
後は、個々のプログラムの中を見て、エラーとなっている部分を修正していく事になります。
長くなったので、その辺は次回ブログに記載しようと思います。

それでは、良いAngularライフを!

2018年12月7日金曜日

Photoshopでスライスツールを使わずにパーツを一気に書き出す方法。

12月 07, 2018
こんにちは、オフィス狛 デザイン部のSatoです。

今回はPhotoshopで作ったモック画像からスライスツールを使わないで細かい画像を書き出す方法をご紹介しようと思います。

モック画像からスライスを設定して書き出しするのは地味に大変な作業だと自分は思っているので、できるだけ楽に作業したいと思っていましたが実はスライスツールを使うよりも、もっと楽に細かいウェブデザインのパーツを書き出す方法があったのでご紹介させていただきます。

方法1 画像アセット機能で書き出す。


1.書き出したいレイヤーのレイヤー名またはグループのグループ名を「(書き出したい画像のタイトル).(書き出したい画像拡張子)」にします。

2.Photoshopのメニューバーの「ファイル」の「生成」にマウスカーソルを合わせると「画像アセット」という項目が出てくるのでクリックします。

psdファイルを保存している場所に「(書き出したpsdファイル名)-assets」というフォルダーができているので、その中に書き出されています。


書き出せる拡張子はJPEGや透過PNG透過GIFはもちろんの事、SVGも書き出せるそうです。
個人的にはSVGが書き出せるのがうれしいですね。

詳しい設定などはAdobeのブログで解説されていますので是非参考にしてみてください。


方法2 選択したレイヤーを「書き出し形式」で書き出す。


1.書き出したいアイコンなどのパーツを移動ツール+shiftキーで全て選択します。

2.レイヤーパネルの上で右クリックして出てきたメニューから「書き出し形式」を選択します。

3.画像のサイズや拡張子を選択する画面になるのでお好みで設定を変えてください。

書き出し設定を全て終えたら、右下の全て書き出しボタンを押します。
保存するフォルダーを選択して開くボタンを押すと指定したフォルダー中に書き出されています。

この方法なら色々なサイズのアイコンを一気に書き出せるのでとても便利です。

ただ、この方法で自分が少し躓いたところが一つあります。
画像のサイズや拡張子を選択する画面で画像の解像度を変えたはずなのに、1ファイルのみその拡張子で書き出され、他の画像はもともとの拡張子のままで書き出されていました。
実は左の書き出す画像の一覧の部分で、選択した画像しか設定が反映されないようです。
書き出す画像全ての拡張子を変えたい場合はCtrlキーを押しながら全てクリックして選択してから拡張子を変えてから書き出すと全部同じ拡張子になってくれます。


どちらの機能も背景を消してスライスツールでスライスを引いて……という作業を省略できる素晴らしい機能なのですごい時短になります!
みなさんもぜひ使ってみてください。

2018年12月5日水曜日

C# foreachでListに要素を追加する方法。

12月 05, 2018

オフィス狛 技術部のHammarです。

最近業務でC#を使うことが多いのですが、個人的にはまだまだやり始めでわからないことが多く、ちょっとしたことで詰まる今日この頃なんですが、基本的なところで「こういう書き方もあるんだなー」とわかったことを今回は書きたいと思います。

■foreachでListに要素追加方法

C#のCollectionであるListクラスは配列っぽいイメージですが、動的に長さ(サイズ)を自由に変えられることと、データを途中に挿入できる点が配列との大きな違いとなっています。

で、例えばListや配列を回しながら、ある条件のとき等にListに要素を追加したいとき等がよくあると思いますが、最初自分は下記のように記述していました。
ちなみに下記は配列を回しながら、indexの値と配列の中身を新たなListに作っているだけの記述です。

public class animalList
{
    public int Seq { get; set; }
    public string AnimalName { get; set; }
}

public void TestMethod()
{
    string[] animals = new string[] { "lion", "zebra", "elephant" };
    var index = 0;

    List<animalList> result = new List<animalList>();
    animalList animalListdata = null;

    foreach (var name in animals)
    {
        animalListdata = new animalList
        {
            Seq = index,
            AnimalName = name
        };
        index++;
        result.Add(animalListdata);
    }
}

上記でも作りたい形になるのですが、ほんの少しだけ簡単に記述できる方法がありました。
※animalListクラスは同じものを使います。

public void TestMethod()
{
    string[] animals = new string[] { "lion", "zebra", "elephant" };
    var index = 0;

    var result = new List<animalList>();
    foreach (var name in animals)
    {
        result.Add(new animalList
        {
            Seq = index,
            AnimalName = name
        });
        index++;
    }
}

たった2~3行だけですが、回すと同時にListにAddしているので個人的にはこっちのほうがスッキリしているように見えます。

というわけで、C#をよくご存知の方にはそんなことは基本中の基本と怒られそうですが、まだ触れてから日の浅い自分にとってはちょっとしたことですが勉強になりました。

2018年11月15日木曜日

AngularのFormでのselect要素の使い方(selected指定の方法)

11月 15, 2018

オフィス狛 技術部のKoma(Twitterアカウントの中の人&CEO)です。

何だか分かり難い題名ですいません。良い題名が思いつきませんでした。

さて、やりたい事から説明しようと思います。

Angularで、Formに入力した値をPOSTする時、ちょっとやり方迷うのがselect要素です。
プルダウンってやつですね。

こんな感じのFormは良くあるかと思います。
初期表示時に先頭を選択状態にしておくのは簡単ですが、
データの修正を行う機能などは、「APIで取得した値を選択状態に」って事は良くあります。

例えば、このようなJSONがAPIから返却されるとします。
{"list": [
        {"api-data-status": 1},
        {"api-data-status": 2},
        {"api-data-status": 3}
 ]}

まずはプルダウンに表示する値(value)と名称(name)を定義します。
【constants.ts】
export namespace testDataStatus {
  export const STATUS_1 = { value: 1, name: 'ステータス1' };
  export const STATUS_2 = { value: 2, name: 'ステータス2' };
  export const STATUS_3 = { value: 3, name: 'ステータス3' };
}

続いて、コンポーネント側でプルダウン用のリスト定義します。
先程の定義した定数を使用しています。
【test-data-status.component.ts】
  testDataStatusOption: Array<{ status: number; name: string }> = [
    {
      status: Constants.testDataStatus.STATUS_1.value,
      name: Constants.testDataStatus.STATUS_1.name
    },
    {
      status: Constants.testDataStatus.STATUS_2.value,
      name: Constants.testDataStatus.STATUS_2.name
    },
    {
      status: Constants.testDataStatus.STATUS_3.value,
      name: Constants.testDataStatus.STATUS_3.name
    }
  ];

そして、最後はView(html)側ですが・・・下記のように記載すると、うまく行きません
【test-data-status.component.html】(NGな例)
<ng-container *ngFor="let item of statusList.list">
    <select class="form-control" formControlName="testDataStatus">
        <option *ngFor="let hoge of testDataStatusOption"
              [ngValue]="hoge.status"
              [selected]="hoge.status === item.api-data-status">
              {{report.name}}
        </option>
    </select>
</ng-container>
「statusList.list」がAPIで取得したデータです。
それを最初の「ngFor」で繰り返してデータ数分のプルダウンを作り、二番目の「ngFor」からプルダウンの値(value)と名称(name)を作り、APIの値と比較して「selected」に値を設定する・・・一見うまく行きそうなんですけどね。これではダメです。

という訳で、うまく行く書き方は以下の通りです。
【test-data-status.component.html】(OKな例)
<ng-container *ngFor="let item of statusList.list">
  <select class="form-control form-control-sm" [(ngModel)]="item.api-data-status" formControlName="testDataStatus">
    <option *ngFor="let hoge of testDataStatusOption"
        [ngValue]="hoge.status">
        {{hoge.name}}
    </option>
  </select>
</ng-container>
まあ、良く考えると当たり前なのですが、
「[(ngModel)]」に値を設定する事で、APIから取得した値で選択状態とする事が出来ます。

view(html)表示だけだと、そこまで難しくないのですが、FormControlを使うと少しややこしくなるので、自分の備忘録の為に記事にしました。

それでは、良いAngularライフを!


2018年11月2日金曜日

C#でプロキシ経由でJSONデータを外部APIにわたす。

11月 02, 2018

オフィス狛 技術部のHammarです。

今回も業務でちょっとハマったことについて、解決方法を書いていこうと思います。
今回はC#でプロキシ経由でAPIを呼び出す方法です。
いつもは社内のネットワーク内でAPIを呼び出していて、あるタイミングで社外のAPIを
呼び出すとなったときに、プロキシ設定しているため通常の呼び出し方ではリクエストが届かないという感じでちょっとハマりました。

まず下記のようなJSONを、あるAPIに送るとします。
{
    "id" : 1,
    "name" : "テスト"
}

プロキシ設定を考えずに書くと、HttpClientを使って下記のような感じでPOSTします。
var json = "{ \"id\" : 1, \"name\" : \"テスト\"  }";

using (var client = new HttpClient())
{
    string apiUrl = "http://hostname/testapi";
    HttpContent content = new StringContent(json, Encoding.UTF8, "application/json");
    HttpResponseMessage response = await client.PostAsync(apiUrl, content).Result;
}
※上記のJSON部分は一旦わかりやすいように書いていますが、実際にはJson.NETを使ってシリアライズしてやり取りします。

で、上記の状態でAPIをコールしてもプロキシに引っかかり、API側にリクエストが届きません。
ですので、上記に利用するプロキシ設定情報を付与してあげると外にでれます。
そのやり方が下記になります。
var json = "{ \"id\" : 1, \"name\" : \"テスト\" }";
var httpClientHandler = new HttpClientHandler
{
    Proxy = new WebProxy("http://localhost:8888", false),
    UseProxy = true
};
using (var client = new HttpClient(httpClientHandler))
{
    string apiUrl = "http://hostname/testapi";
    HttpContent content = new StringContent(json, Encoding.UTF8, "application/json");
    HttpResponseMessage response = await client.PostAsync(apiUrl, content).Result;
}
上記でわかるようにHttpClientHandlerクラスのProxyプロパティを使ってあげます。
これでこのプロキシ設定情報でリクエストされ、API側に届くようになります。

ちなみにこのやり方は認証ない場合で、認証が必要な場合は下記のようにHttpClientHandlerクラスに認証情報のCredentialsプロパティを追加するだけです。
var httpClientHandler = new HttpClientHandler
{
    Proxy = new WebProxy("http://localhost:8888", false),
    Credentials = new NetworkCredential(@"username", @"password");
    UseProxy = true
};

HttpClientHandlerクラスにはその他いろんなプロパティがあるので、HttpClientを使ったリクエストを行う際にはこちらも参考にしてみてください。
https://msdn.microsoft.com/ja-jp/library/system.net.http.httpclienthandler(v=vs.110).aspx

2018年10月4日木曜日

CSSでテキストの1文字目を大きく表示する。

10月 04, 2018

こんにちは、オフィス狛 デザイン部のSatoです。

先日、ドロップキャップをウェブサイトで表現する必要があったのですが調べるまで正しいやり方がわからなかったのでメモしたいと思います。
(テキストの1文字目のみを大きく表示する表現のことをドロップキャップって言うんですね。実は知らなかったです。)

最初大きくしたい1文字目の部分を<span>で囲んでclassで…と思ったのですが、実は「:first-letter」という擬似要素で簡単に実現できます。
いちいち<span>で囲まなくてもいいんです。
この擬似要素はブロックレベル要素の最初の文字にのみスタイルが適用されるので、ドロップキャップを実現できるのですね。


「p:first-letter」にfloat設定しp要素でfloat解除するといい感じになりました。


2018年10月1日月曜日

ESLint のエラー「Use object destructuring prefer-destructuring」に対応する。

10月 01, 2018

オフィス狛 技術部のKoma(Twitterアカウントの中の人&CEO)です。

Node.js + ESLint で開発を行っていると、良く出てくるエラーが、
「Use object destructuring prefer-destructuring」です。

直訳すると、「オブジェクトの非構造化の使用」なのですが、初見だと、何をどう修正すれば良いのか分からないんですよね・・・

例えば、以下のようなプログラムがあったとします。(あくまで例なので、意味の無いプログラムになっています)
  const response = await hogeService.getUser();
  if (response.id) {
    const userId = response.id;
  } else if (response.name) {
    const userName = response.name;
  }

「hogeService.getUser()」は、DBやAPIからデータを取得するイメージです。
「hogeService.getUser()」の戻り値の中には、「{id: 1234, name: "test"}」のような構造化されたデータが入っている事が前提で、そのデータと構成を「response」に設定している、という感じです。

これでESLintのチェックに掛けると、見事に
error Use object destructuring prefer-destructuring
が出ます。

「オブジェクトの非構造化の使用」を促されているので、問題なのは構造化されている「response」だと分かります。
(正確に言うと、構造化された後の使い方ですが)

と言う事で、
  const { id, name } = await hogeService.getUser();
  if (id) {
  } else if (name) {
  }

と直す事で、ESLintのエラーが出なくなります。
「const { id, name }」とする事で、構造化から解放されていますね。

初見だと分かり難いですが、
お決まりのパターンの修正なので、慣れるのが一番ですね。