SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Webページ用UIフレームワーク「Svelte」の活用

「Svelte」でコンポーネントの表示・更新時に処理を実行できるライフサイクル関数の利用

Webページ用UIフレームワーク「Svelte」の活用 第4回

  • X ポスト
  • このエントリーをはてなブックマークに追加

コンポーネントの表示更新直前に実行されるbeforeUpdate関数

 beforeUpdateは、コンポーネントの表示内容が「更新される直前」に実行されるライフサイクル関数です。

 利用例を図3のサンプルで説明します。ラジオボタンでスマートフォンのメーカーを選択すると画面に表示されますが、「Blackberry」を選択した時だけアラート表示するとともに、Blackberryは選択されずに元の選択内容に戻ります。

図3:beforeUpdate関数のサンプル(p002-beforeUpdate)
図3:beforeUpdate関数のサンプル(p002-beforeUpdate)

 図3の画面表示(HTML)は、リスト3の通り記述されています。ラジオボタンの<input>要素にbind:group属性を記述することで、選択値がvalue変数に紐づけられています。value変数の内容は(1)で画面に表示されます。

[リスト3]図3のサンプルのHTML記述(p002-beforeUpdate/src/routes/+page.svelte)
<div>どのメーカーのスマホを使いますか?</div>
<label>
  <input type="radio" bind:group={value} value="Apple" />Apple
</label>
<label>
  <input type="radio" bind:group={value} value="Google" />Google
</label>
<label>
  <input type="radio" bind:group={value} value="Blackberry" />Blackberry
</label>
<label>
  <input type="radio" bind:group={value} value="motorola" />motorola
</label>
<div>
  あなたが選んだのは:<span id="selectedValue">{value}</span> <!--(1)-->
</div>

 一方、<script>部はリスト4の通りです。

[リスト4]図3のサンプルの<script>部(p002-beforeUpdate/src/routes/+page.svelte)
<script lang="ts">
  import { beforeUpdate } from 'svelte';
  // 選択された値を格納する変数
  let value = 'Apple';
  // コンポーネントの表示更新直前に実行される処理 ...(1)
  beforeUpdate(() => {
    // 変更前の選択値を取得 ...(1a)
    const beforeSelectedValue = (
      document.getElementById('selectedValue') as HTMLSpanElement
    ).innerText;
    // 変更後の選択値で条件分岐 ...(1b)
    if (value === 'Blackberry') {
      alert(`${value}はもうスマホを売っていないのです...`);
      // 変更前の選択値に戻す ...(1c)
      value = beforeSelectedValue;
    }
  });
</script>

 beforeUpdate関数は(1)で利用しています。(1a)ではリスト3(1)で画面に表示された選択値の内容をbeforeSelectedValue変数に格納しています。beforeUpdate関数は画面が更新される直前に実行されるため、beforeSelectedValue変数には変更前の選択値が格納されます。

 一方、ラジオボタンと紐づけられたvalue変数は、この時点で変更後の選択値に更新されているため、(1b)でvalue変数の値(変更後の選択値)が「Blackberry」の場合だけ、アラートでメッセージを表示後、(1c)でvalueにbeforeSelectedValue変数の値(変更前の選択値)を代入します。この処理によって、Blackberryの選択を取り消して、直前の選択状態に戻るようになります。

コンポーネントの表示更新直後に実行されるafterUpdate関数

 afterUpdateは、コンポーネントの表示内容が「更新された直後」に実行されるライフサイクル関数です。

 利用例を図4のサンプルで説明します。スマートフォンの一覧とともに、画面下部にテキストボックスと「追加」ボタンが表示されます。テキストボックスに文字列を入力して「追加」ボタンをクリックすると、一覧の一番下に入力内容が追加されるとともに、テキストボックスが常に表示されるように画面がスクロールしていきます。

図4:afterUpdate関数のサンプル(p003-afterUpdate)
図4:afterUpdate関数のサンプル(p003-afterUpdate)

 図4のサンプルの実装内容を説明します。まず、一覧に表示する内容は、<script>部のphoneList配列に、リスト5の通り定義します。また、テキストボックスの入力内容を格納するinputPhone変数も同時に定義します。

[リスト5]一覧表示とテキストボックスに対応する変数(p003-afterUpdate/src/routes/+page.svelte)
// 画面に一覧表示する内容
let phoneList: Array<string> = [
  'Apple iPhone 16',
  'motorola edge 50 pro',
  'SHARP AQUOS wish4',
  'OPPO Reno11 A'
];
// テキストボックスに入力された文字列
let inputPhone: string = '';

 画面表示(HTML)はリスト6の通りです。(1)のリスト部では{#each}~{/each}記述により、phoneList配列の内容を一覧表示します。(2)の入力部ではテキストボックスの<input>要素に記述されたbind:value属性により、テキストボックスの入力内容をinputPhone変数に紐づけています。

[リスト6]図4のサンプルのHTML記述(p003-afterUpdate/src/routes/+page.svelte)
<!-- リスト部 ...(1)-->
<div id="listArea">
  {#each phoneList as phone}
    <div class="phone">{phone}</div>
  {/each}
</div>

<!-- 入力部 ...(2)-->
<div id="inputArea">
  <input type="text" bind:value={inputPhone} />
  <button on:click={addToList}>追加</button>
</div>

 リスト6(2)の追加ボタンクリック時に実行されるaddToList関数の内容はリスト7の通りです。

[リスト7]追加ボタンクリック時の処理(p003-afterUpdate/src/routes/+page.svelte)
const addToList = () => {
  // 入力された文字列をphoneListに追加 ...(1)
  phoneList = [...phoneList, inputPhone];
  // テキストボックスの文字列を初期化 ...(2)
  inputPhone = '';
  // スクロール位置設定フラグを設定する ...(3)
  isSetScrollPosition = true;
};

 (1)で、テキストボックスに入力された文字列(inputPhone変数)を追加して、phoneList配列を更新しています。(2)でテキストボックスの文字列を初期化後、(3)ではスクロール位置設定フラグ(isSetScrollPosition変数)をtrueにしています。このフラグはafterUpdate関数の処理で参照します。

[補足]画面に反映されるように配列を更新する方法

 リスト7(1)では、スプレッド構文「...phoneList」でphoneList配列の各要素を取得し、inputPhoneと合わせて新しい配列を生成してphoneListに代入しています。これは、Svelteのリアクティブ機能が代入をきっかけに実行されるためです。詳細はSvelteの公式チュートリアルも参照してください。

 ここで配列への要素追加を「phoneList.push(inputPhone)」で実行すると、リアクティブ機能が実行されず、要素追加が画面に反映されません。

 ここでの本題となるafterUpdate関数の実装は、リスト8の通りです。

[リスト8]図4のサンプルのafterUpdate関数(p003-afterUpdate/src/routes/+page.svelte)
afterUpdate(() => {
  // スクロール位置設定フラグが立っている場合のみ処理 ...(1)
  if (isSetScrollPosition) {
    // リスト部と入力部の高さを求める ...(2)
    const listArea = document.getElementById('listArea') as HTMLDivElement;
    const inputArea = document.getElementById('inputArea') as HTMLDivElement;
    const listAreaHeight = listArea.offsetHeight;
    const inputAreaHeight = inputArea.offsetHeight;
    // スクロール位置を設定 ...(3)
    window.scrollTo(0, listAreaHeight + inputAreaHeight - window.innerHeight);
  }
  // スクロール位置設定フラグを戻す ...(4)
  isSetScrollPosition = false;
});

 afterUpdate関数は、コンポーネントのあらゆる表示更新時(例えばこのサンプルでは、テキストボックスの内容が変更されるたび)に実行されます。そのため(1)の条件分岐で、isSetScrollPosition変数がtrueの場合のみスクロール位置調整の処理が行われるようにしています。

 スクロール位置調整の処理では、まず(2)で、リスト部と入力部の高さを求めています。afterUpdate関数は画面更新直後に実行されるため、ここで取得される高さは、画面更新後のものであることに注意してください。取得した高さを利用して、(3)でスクロール位置を設定しています。リスト部と入力部の高さから、表示領域自体の高さ(window.innerHeight)を減算したものがスクロール位置となります。一連の処理後、(4)でスクロール位置設定フラグを戻しています。

[補足]body要素のマージンを0に設定

 図4のサンプルでは、マージンによるスクロール位置の誤差を防ぐため、ページ自体のHTMLファイル(app.html)に、body要素のマージンを0にする記述を行っています。

[リスト9]マージンを0にする記述(p003-afterUpdate/src/app.html)
<style>body { margin: 0; }</style>

次のページ
コンポーネントの状態が画面に反映するのを待つtick関数

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Webページ用UIフレームワーク「Svelte」の活用連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト  吉川 英一(ヨシカワ エイイチ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/20520 2024/12/02 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング