Angular、Bootstrap、およびAPIXU APIを使用して天気アプリを構築する方法

著者は、Write for DOnationsプログラムの一部として寄付を受け取るためにNPowerを選択しました。

前書き

Angularは、Googleによって構築されたフロントエンドWebフレームワークです。 これにより、開発者は、model-view-controller(MVC)またはmodel-view-viewmodel(MVVM)ソフトウェアアーキテクチャパターンをモデルにしたシングルページアプリケーションを構築できます。 このアーキテクチャは、アプリケーションを異なるが接続された部分に分割し、並行開発を可能にします。 このパターンに従って、AngularはそのさまざまなコンポーネントをWebアプリケーションの各部分に分割します。 そのコンポーネントは、そのコンポーネントに関連するデータとロジックを管理し、それぞれのビューにデータを表示し、アプリの他の部分から受信するさまざまなメッセージに基づいてビューを調整または制御します。

Bootstrapは、開発者がレスポンシブWebサイト(さまざまなデバイスに適応するサイト)を迅速かつ効果的に構築するのに役立つフロントエンドライブラリです。 各ページを12列に分割するグリッドシステムを使用します。これにより、ページがどのデバイスで表示されていても、ページのサイズとスケールが適切に維持されます。

APIXUは、APIを介して全球気象データをユーザーに提供します。 APIXUを使用すると、ユーザーは世界のあらゆる場所の最新の天気予報と将来の天気予報を取得できます。

このチュートリアルでは、Angular、Bootstrap、およびAPIXU APIを使用して天気アプリを作成します。 検索フォームに場所を入力し、そのフォームを送信すると、その場所の現在の天気の詳細がアプリに表示されます。 このチュートリアルで使用されるAngularバージョンは7.2.0で、使用されるBootstrapバージョンは4.2.1です。

前提条件

このチュートリアルを始める前に、次のものが必要です。

[[step-1 -—- installing-angular]] ==ステップ1—Angularのインストール

アプリの作成を開始する前に、Angularをインストールする必要があります。 端末を開き、次のコマンドを実行して、Angular CLIをマシンにグローバルにインストールします。

npm install -g @angular/cli

Angular CLIは、Angularのコマンドラインインターフェイスです。 これは、新しいAngularプロジェクトとAngularプロジェクトを構成するさまざまなサブ要素を作成する主な方法として機能します。 -g引数を使用すると、グローバルにインストールされます。

しばらくすると、次の出力が表示されます。

Angularのインストールからの出力

...
+ @angular/[email protected]
...

これで、Angularがローカルマシンにインストールされました。 次に、Angularアプリケーションを作成します。

[[step-2 -—- creating-your-angular-app]] ==ステップ2—Angularアプリを作成する

この手順では、新しいAngularアプリケーションを作成および構成し、BootstrapやjQueryなどのすべての依存関係をインストールし、最後にデフォルトのアプリケーションが期待どおりに動作することを確認します。

まず、ngコマンドを使用してAngularアプリケーションを作成します。これは、ターミナルから実行できます。

[.note]#Note: Windowsを使用している場合、Node.jsとnpmを正しくインストールしていても、コマンドプロンプトからngコマンドを実行しようとすると問題が発生する可能性があります。 たとえば、ng is not recognized as an internal or external commandなどのエラーが発生する場合があります。 これを解決するには、WindowsのNode.jsフォルダーにあるインストール済みのNode.jsコマンドプロンプト内でngコマンドを実行してください。

ngコマンドは、コマンドラインからAngularでアクションを実行するための前提条件です。 たとえば、新しいプロジェクトを構築する場合でも、コンポーネントを作成する場合でも、テストを作成する場合でも、必要な各機能の前にngコマンドを付けます。 このチュートリアルでは、新しいアプリケーションを作成する必要があります。これは、ng newコマンドを実行することで実現できます。 ng newコマンドは、新しいAngularアプリケーションを作成し、必要なライブラリをインポートして、アプリケーションに必要なすべてのデフォルトのコードスキャフォールディングを作成します。

新しいアプリケーションを作成することから始めます。このチュートリアルでは、weather-appと呼ばれますが、必要に応じて名前を変更できます。

ng new weather-app

ng newコマンドは、新しいアプリケーションに追加する機能に関する追加情報の入力を求めます。

OutputWould you like to add Angular routing? (y/N)

Angularroutingを使用すると、ルートとコンポーネントを使用して、さまざまなビューを持つシングルページアプリケーションを構築できます。 先に進み、yと入力するか、ENTERを押してデフォルトを受け入れます。

OutputWhich stylesheet format would you like to use? (Use arrow keys)

ENTERを押して、デフォルトのCSSオプションを受け入れます。

アプリは作成プロセスを続行し、しばらくすると次のメッセージが表示されます。

Output...
CREATE weather-app/e2e/src/app.e2e-spec.ts (623 bytes)
CREATE weather-app/e2e/src/app.po.ts (204 bytes)
...
Successfully initialized git.

次に、テキストエディタで、weather-appフォルダを開きます。

ディレクトリの構造を見ると、いくつかの異なるフォルダーとファイルが表示されます。 これらすべてのファイルがhereを実行することの完全な説明を読むことができますが、このチュートリアルの目的のために、これらは理解するための最も重要なファイルです。

  • package.jsonファイル。 ルートweather-appフォルダーにあり、他のNode.jsアプリケーションと同じように機能し、アプリケーションが使用するすべてのライブラリ、アプリケーションの名前、テスト時に実行するコマンドなどを保持します。 主に、このファイルには、Angularアプリケーションが適切に実行するために必要な外部ライブラリに関する詳細が保持されます。

  • app.module.tsファイル。 このファイルは、weather-app/srcフォルダー内のappフォルダーにあり、アプリケーションをアセンブルする方法をAngularに指示し、アプリケーションのコンポーネント、モジュール、およびプロバイダーに関する詳細を保持します。 imports配列内に、インポートされたモジュールBrowserModuleが既に存在します。 BrowserModuleは、アプリケーションに不可欠なサービスとディレクティブを提供し、常にimports配列の最初にインポートされるモジュールである必要があります。

  • angular.jsonファイル。 アプリのルートweather-appフォルダーにある、これはAngularCLIの構成ファイルです。 このファイルには、Angularアプリケーションの実行に必要なものの内部構成設定が保持されます。 アプリケーション全体のデフォルトを設定し、テスト時に使用する構成ファイル、アプリで使用するグローバルスタイル、ビルドファイルを出力するフォルダーなどのオプションがあります。 これらのオプションの詳細については、公式のAngular-CLI documentationをご覧ください。

次にBootstrapをインストールするので、これらのファイルはすべてそのままにしておくことができます。

ブートストラップには、Angularで正しく機能するためにインストールする必要がある2つの依存関係があります—jQuerypopper.jsjQueryはクライアントサイドスクリプトに焦点を合わせたJavaScriptライブラリであり、popper.jsは主にツールチップとポップオーバーを管理するポジショニングライブラリです。

ターミナルで、ルートweather-appディレクトリに移動します。

cd weather-app

次に、次のコマンドを実行してすべての依存関係をインストールし、package.jsonファイルへの参照を保存します。

npm install --save jquery popper.js bootstrap

--saveオプションは、参照をpackage.jsonファイルに自動的にインポートするため、インストール後に手動で参照を追加する必要はありません。

次のように、インストールされたバージョン番号を示す出力が表示されます。

これで、Bootstrapとその依存関係が正常にインストールされました。 ただし、これらのライブラリをアプリケーションに含める必要もあります。 weather-appは、これらのライブラリが必要であることをまだ認識していません。したがって、jquerypopper.jsbootstrap.js、およびbootstrap.cssへのパスを追加する必要があります。 angular.jsonファイルに。

popper.jsの場合、含める必要のあるファイルはnode_modules/popper.js/dist/umd/popper.jsです。 jQueryにはnode_modules/jquery/dist/jquery.slim.jsファイルが必要です。 最後に、Bootstrapには2つのファイル(JavaScriptファイルとCSSファイルの両方)が必要です。 これらはそれぞれnode_modules/bootstrap/dist/js/bootstrap.jsnode_modules/bootstrap/dist/css/bootstrap.cssです。

必要なファイルパスがすべて揃ったので、テキストエディタでangular.jsonファイルを開きます。 styles配列は、CSSファイルへの参照を追加する場所ですが、scripts配列はすべてのスクリプトを参照します。 これらの配列は両方とも、angular.jsonファイルの先頭近くの"options":JSONオブジェクト内にあります。 次の強調表示されたコンテンツをファイルに追加します。

angular.json

...
"options:" {
...
"styles": [
    "node_modules/bootstrap/dist/css/bootstrap.css",
     "src/styles.css"
],
"scripts": [
    "node_modules/jquery/dist/jquery.slim.js",
    "node_modules/popper.js/dist/umd/popper.js",
    "node_modules/bootstrap/dist/js/bootstrap.js"
]},
...

これで、Bootstrapが正しく機能するために必要なメインの.jsファイルと.cssファイルがインポートされました。 angular.jsonファイルからこれらのファイルへの相対パスを指定しました。.cssファイルをstyles配列に追加し、.jsファイルをangular.jsonのsc​​ripts配列に追加します。 このコンテンツを追加した後、angular.jsonファイルを保存したことを確認してください。

次に、ng serveコマンドを使用してアプリケーションを起動し、すべてが正しく機能していることを確認します。 ターミナルのweather-appディレクトリから、次のコマンドを実行します。

ng serve --o

--o引数は、アプリケーションを表示するブラウザウィンドウを自動的に開きます。 アプリケーションの構築には数秒かかり、ブラウザに表示されます。

端末に次の出力が表示されます。

Output** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **
...

ブラウザが開くと、デフォルトのAngularアプリのページが表示されます。

Image of default created app in Angular

これらの出力が表示されない場合は、この手順を再度実行して、すべてが正しいことを確認してください。 Port 4200 is already in use. Use '--port' to specify a different portのようなエラーが表示された場合は、次のように入力してポート番号を変更できます。

ng serve --o --port 

この潜在的なエラーメッセージの理由は、マシンのポート4200が別のプログラムまたはプロセスによって使用されているためです。 そのプロセスが何かを知っている場合は、それを終了するか、上記の手順に従って別のポート番号を指定できます。

これで、アプリケーションの足場を設定できました。 次に、メインフォームと検索場所の関連する天気の詳細を含む天気コンポーネントを作成します。

[[step-3 -—- creating-your-weather-component]] ==ステップ3—天気コンポーネントの作成

Angularアプリケーションは、主にcomponentsで構成されています。これは、アプリケーション内で特定の機能を持つロジックの一部です。 このコンポーネントは、アプリケーションの画面の一部を管理するいくつかのlogicで構成されています。これは、viewと呼ばれます。

たとえば、このチュートリアルでは、次の2つのタスクの処理を担当するWeather Componentを作成します。

  • 場所を検索する

  • その場所に関連する気象データを表示する

最初の目的を達成するために、場所を検索できるフォームを作成します。 フォームの検索ボタンをクリックすると、その場所を検索する機能がトリガーされます。

2番目の目的を達成するために、取得したデータをきちんと表示するネストされた<p>タグを持つ<div>があります。

端末ウィンドウからアプリを実行している間は、その特定のウィンドウに他のものを入力することはできません。 したがって、他のngコマンドを実行する場合は、新しいターミナルウィンドウでweather-appディレクトリを開きます。 または、CTRL + Cを押して、元のターミナルウィンドウでのアプリの実行を停止することもできます。 その後、新しいコンポーネントをインストールし、その後、ng serve --oと入力してアプリを再起動します。

次のコマンドを実行して、Weather Componentを作成し、それをapp.module.tsファイルに自動的にインポートします。 app.module.tsファイルには、アプリケーション内のすべてのコンポーネント、モジュール、およびプロバイダーに関する詳細が含まれていることに注意してください。

ng generate component weather

次のような出力が表示されます(正確なバイトサイズは異なる場合があります)。

OutputCREATE src/app/weather/weather.component.css (0 bytes)
CREATE src/app/weather/weather.component.html (26 bytes)
CREATE src/app/weather/weather.component.spec.ts (635bytes)
CREATE src/app/weather/weather.component.ts (273 bytes)
UPDATE src/app/app.module.ts (400 bytes)
...

この出力は、Angularがコンポーネントに必要な4つのファイルを作成したことを示しています。

  • ビューの.cssファイルと.htmlファイル

  • コンポーネントをテストするための.spec.tsファイル

  • コンポーネントの機能を保持するための `.component.ts`ファイル

Angularは、新しく作成されたコンポーネントへの参照を追加するために、src/app/app.module.tsファイルも更新しました。 コンポーネントファイルは常にsrc/app/name-of-componentディレクトリの下にあります。

新しいコンポーネントをインストールしたので、ブラウザーに戻ってアプリを表示します。 新しいコンポーネントをインストールするために実行中のアプリを停止した場合は、次を入力して再起動します。

ng serve --o

ページに「アプリへようこそ!」(デフォルトのコンポーネント)が表示されたままであることに気付くでしょう。 新しく作成したコンポーネントは表示されません。 次のセクションでは、これを変更して、localhost:4200に移動するたびに、Angularのデフォルトコンポーネントではなく、新しく作成した天気コンポーネントにアクセスできるようにします。

[[step-4 -—- accessing-your-weather-component]] ==ステップ4—天気コンポーネントへのアクセス

標準のHTMLでは、新しいページを作成するときはいつでも、新しい.htmlファイルを作成します。 たとえば、新しく作成したページに移動する既存のHTMLページがすでにある場合は、その新しいページを指すanchorタグを持つhref属性があります。 。 例えば:

preexisting.html

ただし、Angularでは、これは少し異なります。 この方法でhref属性を使用して、新しいコンポーネントに移動することはできません。 コンポーネントにリンクする場合は、AngularのRouterライブラリを利用して、コンポーネントに直接マップされるファイル内で目的のURLパスを宣言する必要があります。

Angularでは、このファイルをroutes.tsと呼びます。 これには、ルート(リンク)のすべての詳細が保持されます。 このファイルが正しく機能するためには、@angular/routerライブラリからRoutesタイプをインポートし、タイプRoutesの目的のリンクをリストします。 これは、これらがアプリ内のナビゲーション用のルートのリストであることをAngularに伝えます。

テキストエディタでファイルroutes.tsを作成し、src/appディレクトリに保存します。 次に、次のコンテンツをroutes.tsファイルに追加します。

src/app/routes.ts

import { Routes } from '@angular/router'

ここで、URLパスとコンポーネントをsrc/app/routes.tsで宣言します。 ホームページ(http://localhost:4200)にアクセスしたときに、新しく作成した天気予報コンポーネントにアクセスできるようにアプリを作成します。 これらの行をファイルに追加します。これにより、作成したばかりのWeatherコンポーネントにルートURLがマップされます。

src/app/routes.ts

import { Routes } from '@angular/router'
import { WeatherComponent } from './weather/weather.component';

export const allAppRoutes: Routes = [
  { path: '', component: WeatherComponent }
];

WeatherComponentをインポートし、タイプRoutesの配列である変数allAppRoutesを作成しました。 allAppRoutes配列は、それぞれがURLパスとマップ先のコンポーネントを含むルート定義オブジェクトを保持します。 ルートURL(“)に移動するたびに、WeatherComponentに移動するように指定しました。

最終的なroutes.tsファイルは次のようになります。

src/app/routes.ts

import { Routes } from "@angular/router";
import { WeatherComponent } from "./weather/weather.component";

export const allAppRoutes: Routes = [
  { path: '', component: WeatherComponent }
];

次に、これらのルートをメインのapp.module.tsファイルに追加する必要があります。 作成したばかりの配列—allAppRoutes —をRouterModuleと呼ばれるAngularモジュールに渡す必要があります。 RouterModuleは、ルーター(すべてのアプリナビゲーションの実行を担当)を初期化および構成し、allAppRoutesからのルーティングデータをルーターに提供します。 次の強調表示されたコンテンツを追加します。

src/app/app.module.ts

...
import {WeatherComponent} from './weather/weather.component';
import {RouterModule} from '@angular/router';
import {allAppRoutes} from './routes';
...
@NgModule({
    declarations:[
      ...
    ],
    imports: [
        BrowserModule,
        RouterModule.forRoot(allAppRoutes)
    ]
    ...
})
...

このファイルでは、ルートオブジェクトのRouterModuleおよびallAppRoutes配列をインポートしました。 次に、allAppRoutes配列をRouterModuleに渡して、ルーターがURLのルーティング先を認識できるようにします。

最後に、ルーティング自体を有効にする必要があります。 app.component.tsファイルを開きます。 その特定のコンポーネントのHTMLを指定するtemplateUrlプロパティがあります:./app.component.html。 このファイルsrc/app/app.component.htmlを開くと、localhost:4200ページのすべてのHTMLが含まれていることがわかります。

app.component.html内に含まれるすべてのHTMLを削除し、次のように置き換えます。

src/app/app.component.html

router-outletタグはルーティングをアクティブにし、ユーザーがブラウザーに入力したURLを、allAppRoutes変数の下のroutes.tsファイルで以前に作成したルート定義と照合します。 次に、ルーターはHTMLでビューを表示します。 このチュートリアルでは、<router-outlet></router-outlet>タグの直後にweather.component.htmlコードを表示します。

ここで、http://localhost:4200に移動すると、ページにweather works!が表示されます。

アプリケーションでルーティングを設定しました。 次に、場所を検索して関連する詳細を表示できるフォームと詳細セクションを作成します。

[[step-5 -—- defining-the-user-interface]] ==ステップ5—ユーザーインターフェイスの定義

Bootstrapを使用して、アプリケーションビューの足場として機能します。 ブートストラップは、あらゆるデバイス(モバイル、タブレット、またはデスクトップ)に適合する既製のレスポンシブWebサイトを作成するのに役立ちます。 これは、Webページ上のすべての行を幅12列として扱うことで実現しています。 Webページでは、行はページの一方の端からもう一方の端までの単なる線です。 つまり、すべてのページのコンテンツはその行内に含まれ、12列に等しくなければなりません。 12列に等しくない場合、別の行にプッシュダウンされます。 たとえば、Bootstrapのグリッドシステムでは、12列の行が6列の2つのセクションに分割され、次の12列の行が4列の3つのセクションに分割されます。

Bootstrap documentationで、このグリッドシステムの詳細を読むことができます。

ページを6列の2つのセクションに分割し、左側の列に検索フォームを、右側に天気の詳細を表示します。

src/app/weather/weather.component.htmlを開いて、WeatherComponentHTMLコードにアクセスします。 現在ファイル内にある段落を削除してから、次のコードを追加します。

src/app/weather/weather.component.html

Search for Weather:

Weather Details:

すべてのコンテンツを保持するために、クラスcontainer<div>を作成しました。 次に、それぞれ6列の2つのセクションに分割する行を作成しました。 左側には検索フォームが、右側には天気データが表示されます。

次に、フォームを作成するために、最初のcol-md-6列で作業します。 また、フォームに入力した内容をAPIXUに送信するボタンを追加します。APIXUは、要求された天気の詳細を返します。 これを行うには、最初のcol-md-6クラスを識別し、<h3>タグの下に次の強調表示されたコンテンツを追加します。

src/app/weather/weather.component.html

...

Search for Weather:

...

フォームを追加し、検索バーを保持するform-groupクラスを追加しました。 また、天気を検索するためのボタンを作成しました。 ブラウザでは、天気アプリのページは次のようになります。

Image of weather app page so far

これは少しコンパクトに見えるので、CSSを追加してページをより良い間隔でスタイルできます。 Bootstrapの主な利点は、独自のCSSを追加することなくHTMLに追加できるスペーシングクラスが付属していることです。 ただし、Bootstrapの標準クラスがカバーしていない追加のCSSを組み込む場合は、必要に応じて独自のCSSを作成できます。 このチュートリアルでは、Bootstrapの標準クラスを使用します。

<h3>タグごとに、.my-4 BoostrapCSSクラスを追加します。 mは要素にマージンを設定し、yは要素にmargin-topmargin-bottomの両方を設定し、最後に4は追加するマージンの量を指定します。 さまざまな間隔のタイプとサイズhereの詳細を確認できます。 weather.component.htmlファイルで、次の強調表示されたコンテンツを追加して、現在の<h3>タグを置き換えます。

src/app/weather/weather.component.html

Search for Weather:

Weather Details:

ブラウザでページをリロードすると、スペースが増えていることがわかります。

Image of spacing applied to weather app

フォームと、APIXU APIから受け取った情報を表示するセクションを作成しました。 次に、フォームを接続して、現在地を正しく入力できるようにします。

[[step-6 -—- wiring-up-your-form]] ==ステップ6—フォームの配線

Angularでは、アプリケーションでユーザー入力用のフォームを作成する方法が2つあります—reactiveまたはtemplate-drivenです。 同じ結果が得られますが、各フォームタイプはユーザー入力データの処理方法が異なります。

リアクティブフォームを使用して、.component.tsファイルにフォームのさまざまな要素のリストを作成します。 次に、それらをそれぞれの.component.htmlファイル内で作成したHTMLフォームに接続します。 これは厳密に一方向です。つまり、データはHTMLから.component.tsファイルに流れ、双方向のデータの流れはありません。

テンプレート駆動フォームでは、通常のHTMLの場合と同じようにフォームを作成します。 次に、ngModelなどのディレクティブを使用して、HTMLから一方向または双方向のデータバインディングを作成し、コンポーネントのデータモデルに戻すことができます。その逆も可能です。

それぞれのアプローチには長所と短所がありますが、一般的に、次の理由により、リアクティブ形式が望ましいです:

  • さまざまな複雑さのフォームを作成する柔軟性。

  • コンポーネントの.component.tsファイル内の各フォームコントロールの状態をチェックすることによる単体テストの単純さ。

  • フォーム内の値に対するsubscribeの機能。 開発者は、フォームの値ストリームにサブスクライブして、フォームに入力された値に対してリアルタイムでアクションを実行できます。

これらの長所にもかかわらず、リアクティブフォームは実装がより複雑になる場合があります。 これにより、テンプレート駆動型フォームと比較して、開発者がより多くのコードを記述することになります。 フォームタイプと最良の使用例の両方の包括的な概要を確認するには、Angular’s official guideが出発点として適しています。 このチュートリアルでは、リアクティブフォームを使用します。

リアクティブフォームを使用するには、ファイルapp.module.tsを開きます。 次に、ファイルの先頭に向かってインポートを宣言して、ReactiveFormsModuleをインポートします。

src/app/app.module.ts

...
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
    ...
})
...

最後に、ReactiveFormsModuleをインポートのリストに追加します。

src/app/app.module.ts

...
@NgModule({
    ...
    imports: [
        BrowserModule,
        RouterModule.forRoot(allAppRoutes),
        ReactiveFormsModule
    ]
    ...
})
...

これらのコードを追加すると、app.module.tsは次のようになります。

src/app/app.module.ts

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

import { AppComponent } from "./app.component";
import { WeatherComponent } from "./weather/weather.component";
import { RouterModule } from "@angular/router";
import { allAppRoutes } from "./routes";
import { ReactiveFormsModule } from "@angular/forms";

@NgModule({
  declarations: [AppComponent, WeatherComponent],
  imports: [
    BrowserModule,
    RouterModule.forRoot(allAppRoutes),
    ReactiveFormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}

これらの行を両方とも追加したら、weather.component.tsファイルを開き、FormBuilderクラスとFormGroupクラスをインポートします。

src/app/weather/weather.component.ts

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

次に、FormGroupを参照する変数をweather.component.tsファイルに作成します。

weather.component.ts

export class WeatherComponent implements OnInit {
   public weatherSearchForm: FormGroup;
   constructor() { }
...

フォームでアクションを実行するたびに、weatherSearchForm変数を介してアクションを参照します。 次に、FormBuilderインポートをconstructorに追加して、コンポーネントで使用できるようにします。

weather.component.ts

...
public weatherSearchForm: FormGroup;
constructor(private formBuilder: FormBuilder) {}
...

formBuilderconstructorに追加することにより、FormBuilderクラスのインスタンスが作成され、コンポーネント内で使用できるようになります。

これで、FormGroupとそれぞれの値をweather.component.tsファイルに作成する準備が整いました。 フォームに複数の入力オプションがある場合は、FormGroupで囲むことをお勧めします。 このチュートリアルでは、1つ(位置入力)しかありませんが、練習のためにとにかくFormGroupを使用します。

コンポーネントに移動するときにフォームを使用する準備ができていることが重要です。 リアクティブフォームを使用しているため、フォームをHTMLにバインドする前に、まずフォーム内に要素のツリーを作成する必要があります。 これを実現するには、WeatherComponent内のngOnInitフックにフォーム要素を作成する必要があります。 ngOnInitメソッドは、コンポーネントの初期化時に1回実行され、コンポーネントを使用する準備が整う前に実行する必要があると指定したロジックを実行します。

したがって、HTMLプロセスへのバインディングを完了する前に、フォームを作成する必要があります。

WeatherComponentで、ngOnInitフック内でフォームを初期化します。

src/app/weather/weather.component.ts

...
constructor(private formBuilder: FormBuilder) {}
ngOnInit() {
    this.weatherSearchForm = this.formBuilder.group({
      location: ['']
    });
  }

リアクティブフォームスタイルに従ってフォームの最初の部分を作成しました。weather.component.tsファイルでフォームコンポーネントを定義します。 フォームの複合要素のグループを作成しました(現時点では、locationという1つの要素があります)。 ['']配列を使用すると、フォーム入力にいくつかの追加オプションを指定できます。たとえば、データを事前に入力したり、バリデーターを使用して入力を検証したりできます。 このチュートリアルではこれらのいずれも必要ないため、空白のままにしておくことができます。 要素プロパティhereに渡すことができるものについて詳しく知ることができます。

フォームが完成する前に、さらに2つの作業が必要です。 まず、weather.component.htmlファイルを開きます。 フォームにプロパティ[formGroup]を割り当てる必要があります。 このプロパティは、weather.component.tsファイルで宣言した変数weatherSearchFormと同じになります。 次に、location要素(weather.component.tsファイルで宣言されている)をHTMLにバインドする必要があります。 weather.component.htmlに、次の強調表示されたコンテンツを追加します。

src/app/weather/weather.component.html

...
formControlName="location" />
...

[formGroup]プロパティを追加し、フォームをHTMLにバインドしました。 また、この特定のinput要素がweather.component.tsファイルのlocation要素にバインドされることを宣言するformControlNameプロパティを追加しました。

ファイルを保存してブラウザに戻ると、アプリがまったく同じに見えることがわかります。 これは、フォームが正しく接続されていることを意味します。 この段階でエラーが発生した場合は、前の手順に戻って、ファイルのすべてが正しいことを確認してください。

次に、入力データをフォームに受け入れることができるようにボタンを接続します。

[[ステップ-7 ---ボタンを接続する]] ==ステップ7—ボタンを接続する

このステップでは、ユーザーの入力データを受け入れることができるように、検索ボタンをフォームに接続します。 また、最終的にユーザーの入力データをAPIXU天気APIに送信するメソッドの足場を作成します。

weather.component.htmlのコードを振り返ると、ボタンのタイプがsubmitであることがわかります。

src/app/weather/weather.component.html

...

これは、アクションを実行するためにフォームの値を何らかの関数に送信する標準のHTML値です。

Angularでは、その関数を(ngSubmit)イベントで指定します。 フォーム内のボタンをクリックすると、タイプがsubmitである限り、(ngSubmit)イベントがトリガーされ、その後、割り当てられたメソッドが呼び出されます。 この場合、ユーザーが入力した場所を取得し、それをAPIXU APIに送信できるようにします。

まず、これを処理するメソッドを作成します。 weather.component.tsで、1つの引数(フォームに入力した値)を受け取るメソッドsendToAPIXU()を作成します。 次の強調表示されたコンテンツをファイルに追加します。

src/app/weather/weather.component.ts

...
ngOnInit() {
    this.weatherSearchForm = this.formBuilder.group({
      location: [""]
    });
  }

sendToAPIXU(formValues) {

}
...

次に、ngSubmitイベントをHTMLに追加し、送信されたフォームの値をsendToAPIXU()メソッドに渡します。

weather.component.html

...
...
...

ngSubmitイベントをフォームに追加し、フォームを送信するときに実行するメソッドを接続し、weatherSearchFormの値を引数としてハンドラーメソッド(%(weatherSearchForm.value)。 これで、console.logを使用してformValuesを出力することにより、これが機能することをテストできます。sendToAPIXU()メソッドで、次の強調表示されたコンテンツをweather.component.tsに追加します。

weather.component.ts

...
sendToAPIXU(formValues){
    console.log(formValues);
}

ブラウザに移動し、Webサイトページの任意の場所を右クリックしてコンソールを開き、Inspect Elementをクリックします。 ウィンドウ上にConsoleというタブがポップアップ表示されます。 フォームにLondonと入力します。 Search for Weatherボタンをクリックすると、場所が囲まれたオブジェクトが表示されます。

Output from console after updating the sendToAPIXU method

コンソールからの出力はJSONオブジェクト{location: "London"}です。 ロケーション値にアクセスする場合は、formValues.locationにアクセスしてアクセスできます。 同様に、フォーム内に他の入力がある場合は、.locationを他の要素名と交換します。

[.note]#Note:
リアクティブフォームのすべての値はオブジェクトに格納されます—ここで、キーはformBuilder.group({})に渡した値の名前です。

これでボタンが配線され、正しく入力を受け取ることができます。 次に、sendToAPIXU()メソッドにAPIXUAPIへのHTTPリクエストを作成させます。

[[step-8 -—- calling-the-apixu-api]] ==ステップ8— APIXUAPIを呼び出す

APIXU APIは、位置情報を受け入れ、その位置の現在の天気の詳細を検索し、それらをクライアントに返します。 次に、アプリを変更して、位置データをAPIに送信し、応答を取得して、結果をページに表示するようにします。

AngularでHTTPリクエストを行うには、HttpClientModuleをインポートする必要があります。 src/app/app.module.tsを開き、次の強調表示された行を追加します。

src/app/app.module.ts

...
import { ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
    ...
    imports: [
        BrowserModule,
        RouterModule.forRoot(allAppRoutes),
        ReactiveFormsModule,
        HttpClientModule
    ]
    ...
})
...

次に、APIXU APIへのHTTP呼び出しを行うコードを記述する必要があります。 HTTPリクエストを行うためにAngularserviceを作成することをお勧めします。 懸念事項の分離は、作成するアプリで重要です。 サービスを使用すると、アプリが作成するすべてのHTTPリクエストを1つのファイルに移動して、作成した.component.tsファイル内で呼び出すことができます。 これらのHTTPリクエストを特定の.component.tsファイルに「合法的に」書き込むことはできますが、これはベストプラクティスではありません。 たとえば、リクエストの一部が複雑であり、データの受信後に後処理アクションを実行する必要がある場合があります。 アプリ内のいくつかの異なるコンポーネントがHTTPリクエストの一部を使用する可能性があり、同じメソッドを何度も記述したくない場合があります。

新しいターミナルウィンドウから、または現在のターミナルセッションでサーバーを停止して、次のコマンドを実行し、apixuというサービスを作成します。

ng g service apixu

次のような出力が表示されます。

Outputcreate src/app/apixu.service.spec.ts (328 bytes)
create src/app/apixu.service.ts (134 bytes)
...

このコマンドは、サービスファイル(apixu.service.ts)とテストファイル(apixu.service.spec.ts)を作成しました。

次に、このサービスをプロバイダーとしてapp.module.tsファイルに追加する必要があります。 これにより、アプリ内で使用できるようになります。 このファイルを開き、最初にApixuServiceをインポートします。

src/app/app.module.ts

...
import { HttpClientModule } "@angular/common/http";
import { ApixuService } from "./apixu.service";
...

次に、新しくインポートされたApixuServiceをプロバイダーとしてprovidersブロックに追加します。

src/app/app.module.ts file

...
@NgModule({
    ...
    providers: [ApixuService],
    ...
})
...

Angularでは、作成したサービスを使用する場合は、そのサービスをmodule.tsファイル内のプロバイダーとして指定する必要があります。 この場合、アプリケーション全体のプロバイダーとしてapp.module.tsで指定しました。

最後に、src/app/apixu.service.tsファイルを開きます。 サービスを作成するために必要なものの定型コードが表示されます。最初に、AngularからInjectableインターフェースをインポートします。次に、サービスがprovidedInルートインジェクターを使用する必要があるという事実(アプリケーション全体)。次に、サービスのdecorating(これは事実上指定することを意味します)を@Injectableとして指定します。

src/app/apixu.service.ts

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class ApixuService {

  constructor() { }
}

サービスを@Injectableとして装飾すると、このサービスをweather.component.tsのコンストラクター内に挿入して、コンポーネント内で使用できるようになります。

アプリケーションを停止した場合は、次を実行して再起動します。

ng serve --o

前述のように、サービスはAPIXU APIにHTTPリクエストを送信し、app.module.tsファイルにHttpClientModuleをインポートして、アプリケーション全体でHTTPリクエストを作成する必要があります。 さらに、HttpClientライブラリをapixu.service.tsファイルにインポートして、apixu.service.tsファイル自体からAPIXUAPIへのHTTP要求を行う必要があります。 apixu.service.tsファイルを開き、次の強調表示されたコンテンツを追加します。

src/app/apixu.service.ts

...
import { HttpClient } from '@angular/common/http';
...

次に、場所という1つのパラメーターを受け取るメソッドgetWeather()を作成する必要があります。 このメソッドは、APIXUにAPIリクエストを行い、取得した位置データを返します。

これには、APIXU APIにサインアップしたときに提供されたAPIキーが必要です。 APIXUにログインすると、ダッシュボードが表示されます。

APIXU Dashboard

キーが表示され、その下に、Current WeatherForecast Weatherの両方にキーが事前に入力されたAPIURLへのリンクが表示されます。 Current Weatherの詳細のHTTPSリンクをコピーすると、次のようになります。

https://api.apixu.com/v1/current.json?key=YOUR_API_KEY&q=Paris

このURLは、パリの現在の天気の詳細を提供します。 代わりに、フォームから&q=パラメータにlocationを渡せるようにする必要があります。 したがって、apixu.service.tsファイルに追加するときにURLからParisを削除します。

src/app/apixu.service.ts

...
export class ApixuService {

  constructor(private http: HttpClient) {}

  getWeather(location){
      return this.http.get(
          'https://api.apixu.com/v1/current.json?key=YOUR_API_KEY&q=' + location
      );
  }
}

[.note]#Note:コード内で直接APIキーを使用しました。 本番環境では、これをサーバー側に安全に保存し、このキーを安全な方法で取得して、アプリケーション内で使用する必要があります。 サーバー側に安全に保存するか、Hashicorp VaultAzure Key Vaultなどのキー管理アプリケーションを使用していくつか例を挙げます。

これで、HttpClientをインポートしてコンストラクターに挿入し、使用できるようになりました。 また、locationパラメータを受け取り、指定されたURLに対してGETリクエストを行うメソッドgetWeather()を作成しました。 メソッドのlocationパラメータから直接この場所を指定するため、&q=パラメータは空白のままにしました。 最後に、メソッドを呼び出した人にデータを返しました。

これでサービスが完了しました。 サービスをWeatherComponentにインポートし、コンストラクターに挿入して使用してから、sendToAPIXU()メソッドを更新して、新しく作成したサービスに場所を送信する必要があります。 weather.component.tsファイルを開き、強調表示されたコンテンツを追加して、これらのタスクを完了します。

src/app/weather.component.ts

...
import { FormBuilder, FormGroup } from "@angular/forms";
import { ApixuService } from "../apixu.service";
...
constructor(
    private formBuilder: FormBuilder,
    private apixuService: ApixuService
  ) {}
...
ngOnInit(){...}
sendToAPIXU(formValues){
    this.apixuService
      .getWeather(formValues.location)
      .subscribe(data => console.log(data));
}

sendToAPIXU()メソッドの以前のconsole.logステートメントを削除し、このコンテンツで更新しました。 これで、フォームから前に作成したsendToAPIXU()メソッドに場所を渡すことになります。 次に、そのデータをApixuServicegetWeather()メソッドに渡し、その後、その場所でAPIにHTTPリクエストを送信しました。 次に、返された応答をサブスクライブし、この例では、そのデータをコンソールに記録しました。 返されるObservable応答を読み取る方法が得られるまで要求は開始されないため、HTTP要求では常にsubscribeメソッドを呼び出す必要があります。 Observablesは、パブリッシャーとサブスクライバーの間でメッセージを送信する方法であり、あらゆる種類のデータをやり取りできます。 サブスクライバーがサブスクライブするまで、オブザーバブルからデータを受信することはできません。その時点まで実行されないためです。

ブラウザでコンソールをもう一度開きます。 ここで、London, UKと入力し、Search for Weatherをクリックします。 タブの矢印をクリックすると、コンソールに天気の詳細のリストが表示されます。

Console output from looking for current weather in London

出力には、必要なすべての天気情報を含むJSONオブジェクトが表示されます。 返されるオブジェクトは、currentオブジェクトとlocationオブジェクトの2つです。 前者は希望する天気の詳細を提供し、後者はあなたの場所に関する詳細を提供します。

これで、天気データがコンソールに正常に表示されました。 このチュートリアルを完了するには、これらの天気の詳細をHTMLで表示します。

[[step-9 --- displaying-your-weather-data-in-your-app]] ==ステップ9—アプリに天気データを表示する

コンソールに結果を表示することは、すべてが機能していることを確認するための良い最初のステップです。 ただし、最終的にはユーザーの天気データをHTMLで表示する必要があります。 これを行うには、返された気象データを保持する変数を作成し、HTMLでinterpolationを使用してそれを表示します。

補間により、ビューにデータを表示できます。 これを行うには、{{ }}スタイルを介してプロパティをバインドし、そのプロパティをHTMLで表示する必要があります。

weather.component.tsファイルを開き、weatherDataという変数を作成します。この変数に、APIから取得したJSONデータを割り当てます。 さらに、以前は.subscribe()括弧で囲まれていたコードを削除し、次の強調表示されたコードに置き換えます。

src/app/weather/weather.component.ts

...
export class WeatherComponent implements OnInit {
public weatherSearchForm: FormGroup;
public weatherData: any;
...
sendToAPIXU(formValues){
    this.apixuService
    .getWeather(formValues.location)
    .subscribe(data => this.weatherData = data)
      console.log(this.weatherData);
    }
}

変数weatherDataを作成し、anyタイプのデータを保持できることを宣言しました。 次に、API呼び出しから受け取るデータをその変数に割り当てました。 最後に、console.log()ステートメントを追加して、weatherDataが取得したすべての情報を保持していることを再確認しました。

この段階では、weather.component.tsファイルは次のようになっているはずです。

src/app/weather/weather.component.ts

import { Component, OnInit } from "@angular/core";
import { FormBuilder, FormGroup } from "@angular/forms";
import { ApixuService } from "../apixu.service";

@Component({
  selector: "app-weather",
  templateUrl: "./weather.component.html",
  styleUrls: ["./weather.component.css"]
})
export class WeatherComponent implements OnInit {
  public weatherSearchForm: FormGroup;
  public weatherData: any;

  constructor(
    private formBuilder: FormBuilder,
    private apixuService: ApixuService
  ) {}

  ngOnInit() {
    this.weatherSearchForm = this.formBuilder.group({
      location: [""]
    });
  }

  sendToAPIXU(formValues) {
    this.apixuService.getWeather(formValues.location).subscribe(data => {
      this.weatherData = data;
      console.log(this.weatherData);
    });
  }
}

戻ってLondon, UKをもう一度検索すると、オブジェクトが通常どおりコンソールに出力されます。 ここで、このデータをHTMLで表示します。 コンソールで取得した気象データからcurrentオブジェクトを調べると、conditionfeelslike_cfeelslike_ftemp_c、などの値が表示されます。 temp_fなど。これらの5つのプロパティすべてを利用します。

weather.component.htmlファイルをもう一度開き、表示するデータに字幕を追加します。 次の<p>タグを2番目のcol-md-6内に追加します。

src/app/weather/weather.component.html

...

Weather Details:

Current weather conditions:

Temperature in Degrees Celsius:

Temperature in Degrees Farenheit:

Feels like in Degrees Celsius:

Feels like in Degrees Farenheit:

Location Searched:

次に、JSONオブジェクトから受け取ったデータをHTMLに追加します。

weather.component.html

...

Weather Details:

Current weather conditions: {{this.weatherData?.current.condition.text}}

Temperature in Degrees Celsius: {{this.weatherData?.current.temp_c}}

Temperature in Degrees Farenheit: {{this.weatherData?.current.temp_f}}

Feels like in Degrees Celsius: {{this.weatherData?.current.feelslike_c}}

Feels like in Degrees Farenheit: {{this.weatherData?.current.feelslike_f}}

Location Searched: {{this.weatherData?.location.name}}, {{this.weatherData?.location.country}}

HTML内のweatherData変数からデータを取得するときに、演算子?を使用しました。 この演算子はElvis Operatorと呼ばれます。

HTTP呼び出しを行っているため、asynchronousリクエストを行っています。 ある時点でそのデータを取得できますが、すぐに応答するわけではありません。 ただし、Angularは引き続き、weatherData変数から指定したデータをHTMLに入力します。 Angularが段落にデータを入力し始めるまでにデータを受信して​​いない場合、Angularがそのデータを見つけられないというエラーが表示されます。 たとえば、.currentまたは.locationは未定義として表示されます。

エルビス演算子はsafe navigatorであり、これが発生するのを防ぎます。 Angularに、weatherDataが最初に定義されているかどうかを確認してから、そのデータをHTMLで表示するように指示します。 weatherDataにすべての情報が含まれると、Angularはバインディングを更新し、データを通常どおりに表示します。

最終的なweather.component.tsファイルは次のようになります。

weather.component.html

Search for Weather:

Weather Details:

Current weather conditions: {{ this.weatherData?.current.condition.text }}.

Temperature in Degrees Celsius: {{ this.weatherData?.current.temp_c }}

Temperature in Degrees Farenheit: {{ this.weatherData?.current.temp_f }}

Feels like in Degrees Celsius: {{ this.weatherData?.current.feelslike_c }}

Feels like in Degrees Farenheit: {{ this.weatherData?.current.feelslike_f }}

Location Searched: {{ this.weatherData?.location.name }}, {{ this.weatherData?.location.country }}.

目的のデータを出力するために、返されたJSON天気オブジェクトのパターンに従いました。 ファイルを保存してブラウザに戻り、London, UKと入力すると、右側に天気データが表示されます。

Finished app showing weather data from London

San Francisco, USDakar, SenegalHonololu, Hawaiiなどのさまざまな場所で試してください。 これらすべての場所について、それぞれの気象データが表示されます。

結論

Angular、Bootstrap、およびAPIXU APIを使用して天気アプリを作成しました。 Angularのベストプラクティスに従って、Angularプロジェクトをゼロからセットアップし、アプリケーションが適切に設計およびセットアップされていることを確認しました。

Angularは、小さなWebアプリケーションから大規模で複雑なアプリケーションまで、あらゆるものを簡単に作成できる高度なフレームワークです。 Angularは、他のフレームワークと同様に学習曲線を持っていますが、このような小さなプロジェクトは、すぐに学習し、生産的に使用するのに役立ちます。

アプリケーションへの追加を検討するもう1つの機能は、HTTPリクエストからのhandling errorsです。たとえば、無効な場所に入力した場合です。 別の機能強化は、温度が特定のしきい値の間にある場合に異なる画像を表示することです。 他のAPIを使用して、Angularでさまざまなアプリケーションを作成することもできます。

Angular用に構築された特別なタイプのブートストラップであるNgBootstrapを使用することもできます。 これにより、すべての標準Bootstrap JavaScriptウィジェットと、Angularに特に適合した標準インストールに含まれていない特別なウィジェットを使用できます。

このチュートリアルの完全なコードは、GitHubで入手できます。