中安拓也のブログ

プログラミングについて書くブログ

【Python3】特徴的だと思った機能まとめ その3

引き続き、面白いな〜と思った機能をまとめていく(2)

前回の記事

【Python3】特徴的だと思った機能まとめ その2 - 中安拓也のブログ

バージョン情報

  • Python 3.7.2

出典元

https://www.oreilly.co.jp/books/9784873117386/

オライリーの「入門 Python 3」

学習メモ

del文によるリストや辞書の削除

del文を使用することで、指定したオフセットの要素または、配列(リスト)や辞書そのものを削除することができます。

>>> soulBorne = ['DARK SOULS', 'DARK SOULS II', 'DARK SOULS III', 'Bloodborne'] # リストを定義

>>> del soulBorne[1] # リストの2番目の要素を削除

>>> soulBorne # 要素が削除されていることを確認
['DARK SOULS', 'DARK SOULS III', 'Bloodborne']

>>> del soulBorne # 配列soulBorneごと削除

>>> soulBorne #削除されていることを確認
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'soulBorne' is not defined

あんまり使わなそうな機能な気がする。。Pythonの経験0なのでわかりませんが...

inを使った値の有無のテスト

リストや文字列、辞書や集合などに指定した値が含まれているかを確認する

>>> fromsoftware = ['DARK SOULS', 'DARK SOULS II', 'DARK SOULS III', 'Bloodborne', 'SEKIRO'] # リストを定義

>>> 'SEKIRO' in fromsoftware # 'SEKIRO'はリストに含まれている
True
>>> 'splatoon' in fromsoftware # 'splatoon'はリストに含まれていない
False

>>> 'hedoro' in 'Dorohedoro' # リストだけではなく、文字列にも使える
True
>>> 'hayashida' in 'Dorohedoro'
False

タプル

TypeScriptにもタプル型はあり、関数から複数の値を返す時などに使用することができます。
Pythonのタプルについても同様に一度に複数の変数に代入するなどの使い方もできますが、イミュータブルで値の追加・削除・変更ができない、定数リストとして使われることの方が多いそうです。

# ()を使って空のタプルを作成することができる
>>> empty_tuple = ()
>>> empty_tuple
()

# 2個の値を持ったタプルの作成
>>> brother = ('虎杖', '藤堂')
>>> brother
('虎杖', '藤堂')

# ()を省略してもカンマがあればタプルを作成できる
>>> freshman = '虎杖', '伏黒', '釘崎' 
>>> freshman
('虎杖', '伏黒', '釘崎')

# 一度に複数の変数に値を代入することができる(タプルのアンパックと呼ばれる)
>>> omimura, mikuriya = 'hinata', 'hikage'
>>> omimura
'hinata'
>>> mikuriya
'hikage'

次回の記事

【Python3】特徴的だと思った機能まとめ その4 - 中安拓也のブログ

【Python3】特徴的だと思った機能まとめ その2

引き続き、面白いな〜と思った機能をまとめていく

前回の記事

【Python3】特徴的だと思った機能まとめ その1 - L08084のブログ

バージョン情報

  • Python 3.7.2

出典元

https://www.oreilly.co.jp/books/9784873117386/

オライリーの「入門 Python 3」

学習メモ

「入門 Python 3」を読んでいて、印象に残った関数・機能をまとめています。

文字列操作関数の種類が豊富(isalnum(), title(), capitalize(), center(), ljust(), rjust())

文字列をあれこれする関数が多い。正直、これいる?みたいなのもある。

文字列が英数字のみか判定するisalnum()

文字列が英数字のみだと、Trueを返す関数

>>> old_commic = 'koroshiya1'
>>> yondenai = 'ホムンクルス1巻'
>>> new_commic = 'HIKARI-MAN'

>>> old_commic.isalnum() # 英数字しか含まれていないため、True
True
>>> yondenai.isalnum() # 英数字以外が含まれているのにTrue(えっ????)
True
>>> new_commic.isalnum() # 英数字以外(記号)が含まれているため、False
False

実際にサンプルコードを書いてみて初めて気づいたんですが、全角文字はTrueと判定されるらしい。まじで何の役に立つんだ??英語圏の人間が憎い...

>>> 'アイウエオ'.isalnum() # 全角文字はTrueと判定される
True
文字列の単語の頭文字を大文字にするtitle()

一生使わない気がする

>>> 'fight club'.title() # 英字単語の頭文字が大文字になる
'Fight Club'
先頭の一文字を大文字にするcapitalize()

capitalize には「〜を大文字で書く(始める)」という意味があるそうな

>>> 'django unchained'.capitalize() # 先頭の一文字を大文字にする
'Django unchained'
文字列の左寄せ、中央寄せ、右寄せ(ljust(), center(), rjust())

ljust(), center(), rjust()は、空白を追加することによって、指定した文字列を左寄せ、中央寄せ、右寄せしてくれる関数です。
第一引数に生成する文字列の長さを指定してあげる必要があります。

>>> 'serpent'.center(15) # 中央寄せ
'    serpent    '
>>> 'serpent'.ljust(15) # 左寄せ
'serpent        '
>>> 'serpent'.rjust(15) # 右寄せ
'        serpent'

三項演算子の文法が独特

JavaとかJavaScriptでもおなじみの条件(三項)演算子ですが、Pythonだと文法がだいぶ異なります。

  • JavaScriptの場合

JavaScriptの条件演算子の文法は次のようになっています。

[条件] ? [真の場合に返される値] : [偽の場合に返される値]

// 年齢が20才以上だったら、statusに'adult'の文字列が代入される
const status = age >= 20 ? 'adult' : 'child';
  • Pythonの場合

Pythonの場合はこうです。

[真の場合に返される値] if [条件] else [偽の場合に返される値]

status = 'adult' if age >= 20 else 'child'

Pythonの三項演算子、JavaScriptのそれと比べると書き方がちょっとくどいなって感じたんですが、文法が冗長なぶん読みやすいので、好みの問題かもしれないです。

以上

次回の記事

【Python3】特徴的だと思った機能まとめ その3 - 中安拓也のブログ

【Python3】特徴的だと思った機能まとめ その1

はじめに

仕事では主にTypeScript(JavaScript)とJavaしか使用していないんですが、使える言語の幅を広げたいな〜という思いからPythonの勉強を始めました。

この記事は、Pythonを学んでいる中でなじみが薄いな〜とか今までに経験した言語(少ないですが)にはなかった機能だな!と感じたものをまとめることによって、Pythonの特徴を理解し、実施中のPython学習の成果を高めようというものです。

バージョン情報

  • Python 3.7.2

出典元

https://www.oreilly.co.jp/books/9784873117386/

オライリーの「入門 Python 3」(ISBN978-4-87311-738-6)

学習メモ

JavaとかTypeScript(JavaScript)になくて、筆者にとってなじみが薄いと思ったPythonの機能をまとめています(筆者の経験が浅いために、普通にJava/TypeScriptにある機能が含まれている可能性もありますがご容赦ください)。

算術演算子(//)による切り捨て除算

サンプルコードはREPLで実行しています。おなじみの/演算子による割り算や%演算子による割り算の余りの計算だけでなく、Pythonだと演算子(//)で割り算の結果を整数(小数点以下切り捨て)で出力することができます(だからなんだという感じではある)

>>> 13 / 5 # 除算
2.6

>>> 13 % 5 # 除算の余り
3

>>> 13 // 5 # 切り捨て除算
2

intの制限の最大値が任意の数

int型(Python3の整数はint型のみです)の最大値が設定されておらずメモリが許す限り設定できるとのこと(なぜ...?)

スライス([start:end:step])による部分文字列の取得

スライスとは、文字列から部分文字列を取り出すことができる機能です。
先頭オフセット*1(start)、末尾オフセット(end)、ステップ(step)で定義されています。なお、各種値は省略可能です。

>>> japanese = 'あいうえおかきくけこさしすせそ' # 文字列を作成

>>> len(japanese) # 作成した文字列の長さをチェックする
15

>>> japanese[:] # [:]は、先頭から末尾までの文字列を抽出する
'あいうえおかきくけこさしすせそ'

>>> japanese[5:] # 先頭6文字目から末尾まで
'かきくけこさしすせそ'

>>> japanese[5:8] # 先頭6文字目から8文字目まで
'かきく'

>>> japanese[-3:] # 最後の3文字を取り出す
'すせそ'

>>> japanese[::2] # 先頭から末尾までを2文字ごとに取り出す
'あうおきけさすそ'

>>> japanese[1:10:2] # 2文字目から10文字目までを2文字ごとに取り出す
'いえかくこ'

次回の記事

【Python3】特徴的だと思った機能まとめ その2 - L08084のブログ

*1:基準点からの距離

【Docker入門】とりあえずDockerでWebサーバ(Nginx)を動かしてみる

はじめに

職場の開発環境構築を自動化したいのでDockerの勉強を始めました。今回はDocker for MacのインストールからDockerを使用したNginxの起動までをやります。

バージョン情報

  • macOS High Sierra:@10.13.6
  • docker@18.09.2

インストール

Install Docker Desktop on Mac | Docker Documentation

まず、上記URLからDocker for Macをインストールします。

Nginxの起動と停止

続いてHello world的な目的で、インストールしたDockerからWebサーバーのNginxを起動してみましょう。

https://hub.docker.com/_/nginx

f:id:l08084:20190303155019p:plain
Docker HubのNginxのページ

Docker HubのNginx公式Dockerイメージ画面に遷移して、「Copy and paste to pull this image」の項目に記載されているコマンド(docker pull nginx)をコピーします

f:id:l08084:20190303155153p:plain
"Docker Desktop is running"

メニューバーのDockerアイコンをクリックしてDockerが起動状態になっているのを確認した後、ターミナルを開いてDocker Hubからコピーしたコマンドを実行します

$ docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
6ae821421a7d: Pull complete 
58702d4af197: Pull complete 
b165f42e8fd4: Pull complete 
Digest: sha256:18c0755594af107923baa2e65fcef35aea4ab0cea7862d19c27aa127bacb458e
Status: Downloaded newer image for nginx:latest

docker image lsコマンドでNginxのDockerイメージのダウンロードに成功したことを確認する

$ docker image ls nginx
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               latest              8c9ca4d17702        3 days ago          109MB

コンテナを作成するdocker container runコマンドを実行して、Nginxを起動します。下記のコマンドでは80番ポートを使用してDockerイメージの「nginx」から「tutorialserver」という名前のコンテナを起動しています

MacBook-Pro-2:~ takuya$ docker container run --name tutorialserver -d -p 80:80 nginx

54569771cfef520e2cf74b3b818d5177f6fbdc74bc6a2407adfd0656a79d89b7
docker: Error response from daemon: driver failed programming external connectivity on endpoint  tutorialserver (994db4b2efc0aac1059feb64515cfe2ae346db83c7f85dbe04d2ad8cf784fa13): Error starting userland proxy: Bind for 0.0.0.0:80: unexpected error (Failure EADDRINUSE).

エラーが出力されてdocker container runコマンドが失敗しました。80番ポートをすでに占有しているアプリケーションがないか確認します。

$ sudo lsof -i -P | grep "LISTEN"
Password:
httpd       91           root    4u  IPv6 0x5f4e4fd2058c6d59      0t0  TCP *:80 (LISTEN)
xartstora  209           root    3u  IPv4 0x5f4e4fd2058faa01      0t0  TCP *:61500 (LISTEN)
xartstora  209           root    4u  IPv6 0x5f4e4fd2058c7319      0t0  TCP *:61500 (LISTEN)
httpd     2214           _www    4u  IPv6 0x5f4e4fd2058c6d59      0t0  TCP *:80 (LISTEN)
httpd     2220           _www    4u  IPv6 0x5f4e4fd2058c6d59      0t0  TCP *:80 (LISTEN)
httpd     2221           _www    4u  IPv6 0x5f4e4fd2058c6d59      0t0  TCP *:80 (LISTEN)

上記コマンド(sudo lsof -i -P | grep "LISTEN")の結果、80番ポートを占有しているアプリケーションがApache httpdであることがわかります。

Nginx起動のために、Apacheの方は停止してしまいましょう。

$ sudo apachectl stop

Apacheを停止して80番ポートの競合を解決したところで再度、docker container runコマンドを実行します。

$ docker container run --name tutorialserver -d -p 80:80 nginx
docker: Error response from daemon: Conflict. The container name "/tutorialserver" is already in use by container "f93a6126da0db6d74fdf8b812fa7814ef02ad691acdb46e262abbdbb53a6b240". You have to remove (or rename) that container to be able to reuse that name

またエラーが発生しました。エラーメッセージからコンテナの名前(tutorialserver)が重複しているのが原因であることがわかります。

邪魔なコンテナを削除するか、作成するコンテナ名を変更しましょう。

コンテナ名をtutorialserver2に変更して再度コンテナの作成を実行します。

$ docker container run --name tutorialserver2 -d -p 80:80 nginx
786a573afe09b43fd880e9f1fc8153b646ac254f5aeed9d974918e9680fafcbb

今度こそ成功しました。http://localhost:80/をwebブラウザを開いてNginxサーバーが稼働していることを確認しましょう。

<figure class="figure-image figure-image-fotolife" title="Nginxの起動が成功している時の画面">[f:id:l08084:20190303172133p:plain]<figcaption>Nginxの起動が成功している時の画面</figcaption></figure>

起動したコンテナを停止したいときは、docker stop [コンテナ名]コマンドを使用します。

$ docker stop tutorialserver2 
tutorialserver2

http://localhost:80/を再びwebブラウザで開くと、コンテナ「tutorialserver2」が停止して、Nginxに繋がらなくなっていることがわかります。

f:id:l08084:20190303172723p:plain
コンテナを停止した時の画像

参考記事

Dockerで80番へポートマッピングした際に起きたエラーについて | Hodalog

【Vue.js入門】Angularのツアー・オブ・ヒーローズチュートリアルをVueで作成する

はじめに

AngularのチュートリアルアプリをあえてVueで作成することによって、Vue.jsに入門してしまおうという試みです。

ツアー・オブ・ヒーローズチュートリアルとは?

Tour of Heroesとは、Angular公式ドキュメントのチュートリアルアプリ(v7.2.0時点)で、たしかv2.0の頃にはすでにあった気がするくらい昔からあるAngularユーザーには馴染深いアプリです(たぶん)。ヒーローの人材派遣会社のアプリという設定らしい、イメージがしづらい。

Angular 日本語ドキュメンテーション

f:id:l08084:20190220204952p:plain
Angular公式サイトから拝借したヒーローズチュートリアルの遷移図

完成後のソースコードはGitHubにアップロードしています。

github.com

バージョン情報

  • Vue CLI@3.4.0
  • vue-router@3.0.2

開発

環境構築

VueのCLIツールのインストールと初期アプリケーションプロジェクトの作成、プロジェクトのサーブまでをやっています。

$ npm install -g @vue/cli
$ vue create vue-tour-of-heros-tutorial
$ cd vue-tour-of-heros-tutorial
$ npm run serve

上記のコマンドをターミナルで実行した後、ブラウザでhttp://localhost:8080/を開くとデフォルト設定の画面が出てくるので、環境構築に成功していることがわかります。

f:id:l08084:20190205153631p:plain

axiosをインストールする

HTTPリクエストによるデータの取得を行う予定のため、HTTPクライアントのaxiosをインストールします。

$ npm i axios

https://jp.vuejs.org/v2/cookbook/using-axios-to-consume-apis.html

インストールしておいてなんですが、axios(というかHTTPリクエスト)なくても作成できるなって思ったので、axiosは途中でアンインストールしました

vue-routerをインストールする

画面のルーティング処理も必要なので、vue-routerをインストールします。

$ npm i vue-router

紹介 | Vue Router

lodashのインストール

JavaScriptで使える便利な関数を揃えているライブラリであるlodashをインストールします。このアプリではヒーロー検索コンポーネントでdebouceを使用するために入れています。

$ npm i lodash

完成後のコード

アプリが完成した後のフォルダ構成のキャプチャーです。

f:id:l08084:20190220210047p:plain
完成後のフォルダー構成

エントリファイル

完成後のコードについてファイル毎に自分なりのポイントなどを説明していきます。

まずVue.jsのエントリファイルであるmain.js、このファイルについてはVue CLIで自動生成されたデフォルトの状態からほとんど変更していません。 ルーティングの設定routerをインポートして追加したくらい...

Vue.jsのエントリファイル

ルートコンポーネント

f:id:l08084:20190223033202p:plain
ルートコンポーネント初期表示

続いて、ルートコンポーネントであるApp.vueファイルです。

dataオプションで定義しているタイトルの表示、 ダッシュボードコンポーネントとヒーローズコンポーネントへのリンクを表示、そして最下部には子コンポーネントであるメッセージコンポーネントの表示をしています。

ルートコンポーネント

<style>タグが2つありますが、scopedのついているほうがルートコンポーネント限定で適用されるCSSで、ついてないほうがアプリ共通に適用されるCSSになります。

シンプルなコンポーネントですが、単一ファイルコンポーネント(.vueファイル)では、dataオプションを関数形式で書かないといけないのを知らなかったのでつまづきました。

ルーティング設定ファイル

ファイル名がわかりづらいですが、各種画面のルーティング(URL)を設定しているファイルです。

path: '/',で設定しているところからわかる通り、初期表示ではダッシュボードコンポーネントが表示されます。そして、ヒーローズリンクをクリック(/heroes)するとヒーローズコンポーネントが表示されるという感じです。

vue-routerの設定ファイル

ヒーロー詳細コンポーネントへの遷移パスで(/detail/:id)、ヒーローIDを使用した動的ルートマッチングを使用していますが、他フレームワークでもよく見るおなじみの書き方だと思うので、特に難しい部分はなかったかなという感じです。

Store

今回作成したアプリの状態管理ですが、Vuexをわざわざ入れるのもどうかな...と思ったので単純なstoreパターンを採用しました。

単一のStoreファイルで、FluxでいうところのStateもActionもStoreも担当する形式になっています。

各種コンポーネントで共有したいデータであるheroes配列とmessages配列をstateとして設定し、そのstateを追加・取得・更新・削除するメソッド(getHeroes, clearなど)を集約しています。

Store

状態管理とstoreパターンについてはVue.jsの公式サイトに説明があります。

ダッシュボードコンポーネント

f:id:l08084:20190223033345p:plain
ダッシュボードコンポーネント

ダッシュボードコンポーネントでは、storeのheroes配列から2 - 5番目のヒーローを取得してボードとして表示するということをやっていて、ボードをクリックするとクリックしたヒーローの詳細画面に遷移します。

ダッシュボードコンポーネント

ダッシュボードコンポーネントの子コンポーネントとして、ヒーロー検索コンポーネントを設定しています。

ヒーローズコンポーネント

f:id:l08084:20190223033510p:plain
ヒーローズコンポーネント

ヒーローズコンポーネントでは、storeで保持している全ヒーローの一覧表示と、ヒーローの追加と削除機能を持っています。ダッシュボードコンポーネントと同様にリストをクリックすると、該当するヒーローの詳細画面に遷移します。

ヒーローズコンポーネント

メッセージコンポーネント

f:id:l08084:20190223033945p:plain
メッセージコンポーネント

storeで保持しているメッセージ配列(messages[])を表示するコンポーネント、メッセージのクリア機能もあります。

メッセージコンポーネント

ヒーロー詳細コンポーネント

f:id:l08084:20190223033553p:plain
ヒーロー詳細コンポーネント

ダッシュボードコンポーネント、ヒーローズコンポーネント、ヒーロー検索コンポーネントから遷移できるヒーローの詳細を表示するコンポーネントになります。ヒーローの名前を変更して保存する機能もついています。

ヒーロー詳細コンポーネント

ヒーロー検索コンポーネント

f:id:l08084:20190223033709p:plain
ヒーロー検索コンポーネント

フォームに入力した文字列からヒーローを検索するコンポーネントとなります。検索結果のリンクをクリックするとヒーロー詳細コンポーネントに遷移します。

ヒーロー検索コンポーネント

_.debounce(this.search(this.searchName), 500)の部分では、lodashのdebouceメソッドを呼び出していて、内容としては検索フォームの入力が終わってから500ミリ秒後に検索メソッドを呼び出すという処理になっています。

発生したバグ

本アプリを作成する過程で発生したエラーの一覧です。Vue.jsを初めて使った人がどういうエラーでつまづくのかのサンプルとしてご利用ください(?)。

dataオプションの定義に失敗する

エラーメッセージ
[Vue warn]: The "data" option should be a function that returns a per-instance value in component definitions.

Vue.jsのdataオプションでtitleプロパティを定義したタイミングで発生しました。
単一ファイル型コンポーネントでdataオプションを定義する場合は、function()returnする形式にしないといけない、というエラーのようです。

https://jp.vuejs.org/v2/guide/components.html#data-は関数でなければなりません

修正前(エラー発生時)のコード
<script>
export default {
  name: 'app',
  data: {
    title: 'Tour of Heroes',
  }
};
</script>
修正後のコード
<script>
export default {
  name: 'app',
  data: function() {
    return {
      title: 'Tour of Heroes',
    }
  }
};
</script>

親コンポーネントから子コンポーネントに値を渡すのに失敗する

エラーメッセージ
[Vue warn]: Error in render: "TypeError: Cannot read property XXX of undefined"

おなじみのundefinedエラーですね。親コンポーネントから子コンポーネントにオブジェクトを渡すタイミングで発生しました。
親コンポーネントから子コンポーネントに動的な値やオブジェクトを渡す場合は、v-bindが必要になりますが、それをつけ忘れていることが原因で発生しました。

https://jp.vuejs.org/v2/guide/components-props.html#オブジェクトの受け渡し

修正前(エラー発生時)のコード
<HeroDetail hero="selectedHero"></HeroDetail>

エラーが発生したコードです。静的な値を渡す場合はこのテンプレートの書き方でも親から子へ値を渡すことができます。

修正後のコード
<HeroDetail v-bind:hero="selectedHero"></HeroDetail>

今回は、親から子へオブジェクトを渡しているので、v-bindが必要になります。なお、v-bindは省略できるので、<HeroDetail v-bind:hero="selectedHero">ではなく、<HeroDetail :hero="selectedHero">という書き方でもOKです

JSONをaxiosのGETで取得できない

axiosは途中で削除したから、最終的なコードには含まれていないんですが、404が返ってくるというエラーに遭遇したりしました。

エラーメッセージ
GET http://localhost:8080/public/mock-heroes.json 404 (Not Found)

https://forum.vuejs.org/t/vue-cli-3-0-reading-json-with-axios/30053

修正前のコード
    methods: {
        getHeroes: function () {
            this.messages.push('HeroService: fetched heroes')
            return axios.get('/public/mock-heroes.json')
        }
    }

単純にJSONファイルへのパスが間違っているのが原因でした。public/ディレクトリ配下に配置している資源に対してパスを設定する場合、パスにはpublicを含める必要がありません。

f:id:l08084:20190223052338p:plain
mock-heroes.jsonをHTTPリクエストで取得したい...

修正後のコード
    methods: {
        getHeroes: function () {
            this.messages.push('HeroService: fetched heroes')
            return axios.get('/mock-heroes.json')
        }
    }

ウオッチャでエラーが発生する

[Vue warn]: Error in callback for watcher "searchName": "TypeError: Expected a function"

TypeError: Expected a function
    at Function.debounce (lodash.js?2ef0:10319)
    at VueComponent.debouncedGetHeroes (HeroSearch.vue?fa11:39)
    at VueComponent.searchName (HeroSearch.vue?fa11:31)
    at Watcher.run (vue.runtime.esm.js?2b0e:3418)
    at flushSchedulerQueue (vue.runtime.esm.js?2b0e:3160)
    at Array.eval (vue.runtime.esm.js?2b0e:1965)
    at flushCallbacks (vue.runtime.esm.js?2b0e:1891)

lodashのdebounceメソッドを使用していて発生したエラーです。現時点でも発生しています...

修正前のコード
    watch: {
        searchName: function () {
            this.debouncedGetHeroes()
        }
    },
    methods: {
        search: function (name) {
            this.heroes = store.search(name)
        },
        debouncedGetHeroes: function () {
            _.debounce(this.search(this.searchName), 500)
        }
    }

直し方わからないので直してない....まあ動くからええか....わかる人教えてください

おわりに

最終的に削除したもののmixinaxiosを使ったりしたので、Vue.jsの基本的な機能はあらかた触れたような気がするのが作成してよかった点です。特にmixinにつまづいて、mixinは継承に近い概念なんですけどコンポーネント間でのデータの共有に利用しようとして失敗するなどしました(mixinで共通のデータをコンポーネントに持たせようといても、各コンポーネントごとに同じ名前の別のデータプロパティを持つことになり、データの値自体はコンポーネント間で共有されない)。

Angularと比較するとVue.jsは基礎ガイドのボリュームが圧倒的に少なかったので使い始めるコストは少なかったものの、フルスタックフレームワークであるAngularほどフレームワーク単体でのサポートが少ないと感じたので、個人的にはAngularとVue.jsのどちらが初心者向けかと聞かれると難しい問いになる気がしました...Vue.jsだとすぐにVuexなどの状態管理ライブラリがないと書くのが辛くなってしまう気がする...その点Angularは状態管理ライブラリがなくてもある程度は書けるし...

【JavaScript】window.open()で開かれるウィンドウがタブではなく新規ウィンドウで開かれるようにしたい(IE11)

はじめに

アプリでリンク先の画像を開くとタブで開かれてしまうから、プログラム(JavaScript)で制御して常に新しいウィンドウで開かれるようにしてほしいとの依頼があったので、やり方を調べた

なお、リンク先の画像を開く処理はJavaScriptのwindow.openを利用している

環境

  • ブラウザはInternet Explorer 11(11.590.17134.0)
  • アプリはBtoBのwebアプリケーション
  • OSはWindows10

調査

ネットサーフィンした

window.open - Web API | MDN

  • 上記のMDNのドキュメントを読んだが新しいウィンドウを開く際に、タブで開くかウィンドウで開くかを設定するオプションはなさそう

どのようにリンクを開くかは、常に、完全にユーザの管理下にあるべきです

ブラウザの表示方法についてはユーザー自身が設定するべきであって、開発者が強制するべきではないみたいな思想を感じる

javascript - window.open opening new tab instead of new window in IE 11 - Stack Overflow

  • ブラウザの方で設定するしかないんじゃない?みたいな回答

javascriptのwindow.open()で別窓が開かれない|teratail

  • window.open()のオプション引数であるwindowFeaturestoolbarなりmenubarの設定を工夫すれば新規タブではなく新規ウィンドウで開いたとのこと
    • windowFeaturesに開かれるウィンドウをタブかウィンドウか直接制御するプロパティがない以上、上記サイトで紹介されている方法が不安定なものである(ブラウザの設定、マイナーバージョンによって左右されうる)認識

調査結果

JavaScript(window.open())で制御するのではなく、ユーザ(クライアント)にブラウザの設定を変更していただいたほうが確実ではないか

ブラウザ設定方法

  1. IE11の設定ボタンをクリック

  2. インターネットオプションを選択

  3. 全般タブを選択

  4. [タブ]ボタンをクリック

  5. ポップアップの発生時:で「常に新しいウィンドウでポップアップを開く」を選択

上記の設定でwindow.open()で開かれるウィンドウがタブではなく新規のウィンドウになる

Angular公式ドキュメント(翻訳されている部分は)全部読む

はじめに

日本語翻訳していただいた全ての人々に感謝.......

angular.jp

バージョン

v.7.2.0時点のドキュメント

学習メモ

勉強になった点や単なる感想についてまとめました。

今までは、共通のHTTPサービスを作成することによってスピナーの表示とかHTTPヘッダーの登録を行なっていたんですが、Angularの非同期バリデーションやインターセプターを利用する方法でも同じことができることを知ったのが一番の収穫だったと思います。

ライフサイクル

  • constructor()の後にngOnInit()が呼ばれる
  • Angular公式チームとしてはDIとローカル変数の初期化(つまり単純な処理)以外はngOnInit()でやってほしいとのこと
  • コストが高い(呼び出し回数の多い)ライフサイクルメソッドの処理に注意...!
    • ngOnChanges()とか...特にngDoCheck()はかなりコストが高いので軽い処理しかやらせないこと

パイプ

  • パイプには純粋パイプと不純パイプがあって、純粋パイプは値が変更された時にしか呼び出されない(オブジェクトの変更は検知できない)のに対して、不純パイプはコンポーネントの変更検知サイクル(ユーザのマウス移動など)の度に毎回呼び出される
  • 不純パイプは純粋パイプと違ってオブジェクトに対して使える代わりに呼び出されまくるのでコストが高い。純粋パイプと不純パイプはngOnChanges()に対するngDoCheck()みたいな関係だと思っておけばよし
  • asyncパイプは不純なパイプなので扱いに注意
    • 以前、全 stateの値をObservable型で管理していたことがあってほぼ全ての項目にasyncパイプを使ってたら処理が激重になったことがあったけどasyncパイプの使い方に問題があったのかもしれない
  • asyncパイプはObservable型だけじゃなくてPromiseにも実は使えるぞ

フォーム

  • 非同期バリデーション(AsyncValidator)というものがあり、通常のバリデーションが完了したタイミングで呼び出される。スピナーの表示とかに利用するのが一般的とのこと
  • formの状態をリセットできるreset()メソッドが便利そう

HTTPClient

  • RxJSにretry()メソッドというものがあるから、HTTPリクエストのリトライ処理が簡単に書けるとのこと
  • インターセプター(HttpInterceptor)というリクエスト直前とレスポンス直後に呼び出される仕組みがあって、共通ヘッダーの設定などに使える
  • ファイルのアップロードなどの重い処理で、進捗状況を表示できるようにする処理があるとのこと

Service WorkerとPWA

  • Service WorkerもPWAも初めて知った。ページをキャッシュしてくれるからパフォーマンス上がるのね〜〜なるなる
  • 素振りしてから本番投入してみたい

バージョニング

https://angular.jp/guide/releases#angularのバージョニング

メジャーバージョンを別のメジャーバージョンに更新する場合は、メジャーバージョンをスキップしないことをお勧めします。手順にしたがって、次のメジャーバージョンに段階的に更新し、各ステップでテストし、検証します。たとえば、バージョン4.x.xからバージョン6.x.xに更新する場合は、まず最新の5.x.xリリースに更新することをお勧めします。5.x.xに正常に更新した後には、6.x.xに更新できます

ほえ〜〜そうなん?めんどいから一気に上げてた....

チートシート

  • ngSwitchって初めて知った。ngIfElse書きまくるよりも綺麗にまとまりそう

あとがき

最近Vue.jsの公式ドキュメントを基礎部分だけ読んだんですけど、Angularのそれよりも圧倒的にボリュームが少なかったのでここら辺がAngularは学習コストが高いと言われるゆえんなのかな...と感じました。基礎部分で学習することが多いのが悪いことなのかは知りませんが