チュートリアル:カスタム ウィジェットの構築

  • リリースバージョン: Zurich
  • 更新日 2025年07月31日
  • 所要時間:22分
  • このチュートリアルに従って、サービスカタログ アイテムを表示するカスタム ウィジェットを構築します。このチュートリアルは、サービスポータルの高度なスクリプト作成機能について理解するためのモデルとして使用してください。

    このチュートリアルでは、クイック注文ウィジェットを作成します。このウィジェットには以下の特徴があります。
    • 検索結果より前に人気のアイテムをユーザーに表示します。
    • サービスカタログ にクエリを実行し、使用可能なオプションをユーザーに表示します。
    • 埋め込み SC カタログ アイテム ウィジェットが含まれているため、ユーザーはクイック注文ウィジェット内でアイテムを表示して注文できます。
    • Angular プロバイダーを使用して、クエリが実行された各アイテムの横にカテゴリ アイコンを表示します。

    ウィジェットの作成とテンプレートのセットアップ

    クイック注文ウィジェットを作成し、サービスカタログ でアイテムのクエリを実行します。

    始める前に

    必要なロール:admin または sp_admin

    手順

    1. 移動先 すべて > サービスポータル > Service Portal の構成 をクリックし、[ ウィジェットエディター] をクリックします。
    2. [新規ウィジェットの作成] をクリックします。
    3. 次の値を定義します。
      • ウィジェット名:クイック注文
      • ウィジェット ID:quick_order
      • テスト ページを作成:有効
      • ページ ID:quick_order

      テスト ページにウィジェットを追加すると、次の各テーブルでレコードが作成されます。

      • sp_page
      • sp_container
      • sp_row
      • sp_column
      • sp_instance
      • sp_widget
      注:
      [サービス ポータル設定] でページ エディターを使用し、テスト ページ上の要素の階層を表示できます。
    4. [送信] をクリックします。
    5. ウィジェット エディターから、クイック注文ウィジェットを開きます。
    6. 次のシンプルなテンプレートを HTML フィールドに追加します。
      <div class="panel panel-primary">
       <div class="panel-heading">Request an item from the catalog</div>
       <div class="panel-body">
         My catalog results
       </div>
      </div>
      
    7. [保存] をクリックします。
    8. URL <yourInstanceUrl>/sp?id=quick_order を使用して、新しいタブでテスト ページをプレビューします。

      ウィジェット テンプレートがテスト ページに表示されます。

      ページでのウィジェットのレンダリング

    インスタンス テーブルにクエリを実行するサーバー スクリプトの追加

    ウィジェットを追加して基本テンプレートを作成すると、ユーザーがインスタンステーブルからデータのクエリを実行できるようにする高度なクライアントおよびサーバースクリプトを定義できます。データベースからデータのクエリを実行してユーザーに表示し、更新をサーバーに戻すことにより、クライアントとサーバー間でデータモデルを渡すことができます。

    始める前に

    必要なロール:admin または sp_admin

    手順

    1. ウィジェット エディターから、クイック注文ウィジェットを開きます。
    2. [サーバー スクリプト] を選択し、サーバー スクリプト フィールドを開きます。
    3. デフォルトのサーバー スクリプトを次のカスタム スクリプトに置き換えます。
      (function() {
      	if (input.keywords != null && input.keywords != '')
      		data.items = getCatalogItems(input.keywords);
      	
      	function getCatalogItems(keywords) {
      		var sc = new GlideRecord('sc_cat_item');
      		sc.addActiveQuery();
      		sc.addQuery('123TEXTQUERY321', keywords);
      		sc.addQuery('sys_class_name', 'NOT IN', 'sc_cat_item_wizard,sc_cat_item_content');
      		sc.addQuery('sc_catalogs', 'e0d08b13c3330100c8b837659bba8fb4');
      		sc.setLimit(100);
      		sc.orderByDesc("ir_query_score");
      		sc.query();
      		var results = [];
      		while (sc.next()) {
      			if (!$sp.canReadRecord(sc))
      				continue;
      
      			var item = {};
      			$sp.getRecordDisplayValues(item, sc, 'name,price,sys_id');
      			item.category = sc.getValue('category');
      			results.push(item);
      		}
      		return results;
      	}
      })();
      

      このスクリプトは、123TEXTQUERY321 クエリ メソッドを使用して sc_cat_item テーブルのキーワード検索を実行します。

    4. HTML テンプレートを次のスクリプトに置き換えます。
      <div class="panel panel-primary">
       <div class="panel-heading">Request an item from the catalog</div>
       <div class="panel-body">
         <input class="form-control" type="search" placeholder="Start typing here to search the list of catalog items" ng-model="c.data.keywords" ng-change="c.server.update()" ng-model-options="{debounce: 250}" />
         <ul class="list-group result-container">
           <li class="list-group-item" ng-repeat="item in c.data.items">
             <a href>{{item.name}}</a><span class="pull-right">{{item.price}}</span>
           </li>
         </ul>
       </div>
      </div>
      

      このテンプレートは検索フィールドを追加し、次の Angular ディレクティブを使用して、サーバー スクリプトで実行されたクエリの結果を表示します。これらのディレクティブの詳細については、「Angular API リファレンス」を参照してください。

      表 : 1. テンプレートで使用される Angular ディレクティブ
      Angular ディレクティブ 説明
      ng-model 値の変更をモデル変数 c.data.keywords に自動的に読み書きします。
      ng-model-options ng-model の動作を構成します。このテンプレートでは、ng-model はユーザーが 250 ミリ秒間入力を停止した後にモデルを更新します。
      ng-change モデル値が変更された後、c.server.update() を実行します。この関数は、データ オブジェクトをサーバー スクリプトに投稿します。スクリプトが実行された後、データ オブジェクトはサーバーにより生成されたデータ オブジェクトによる新しい値で自動的に更新されます。
      ng-repeat 親要素と子要素からテンプレートを作成します。c.data.items のアイテムごとに、テンプレートのインスタンスが作成され、式 {{item.name}} および {{item.price}} が各アイテムの値に置き換えられます。
    5. 次のスクリプトを [CSS - SCSS] フィールドに追加します。
      .result-container {
       margin-top: 10px;
      }
      
    6. 変更を表示するには、テスト ページのプレビューを更新します。

      検索ボックスに入力すると、一致するカタログ アイテムが表示されます。「ipad」を検索してみてください。

      検索により、iPad の検索結果が表示されます。

    ウィジェットの空の状態の管理

    検索用語が入力される前に、人気のアイテムのリストをユーザーに表示します。

    始める前に

    必要なロール:admin または sp_admin

    このタスクについて

    ウィジェットの初期化時に検索が実行されていないため、サーバーの入力変数は未定義です。この空の状態は、ユーザーが初めてウィジェットを操作するときに混乱を招く可能性があります。この問題を解決するには、入力変数が空のときウィジェットに何かが表示されるようにします。この初期データは、ユーザーが初めてウィジェットを操作するときにガイドとなります。

    手順

    1. ウィジェット エディターから、クイック注文ウィジェットを開きます。
    2. 既存のサーバー スクリプトを次のスクリプトに置き換えます。
      (function() {
          if (input.keywords != null && input.keywords != '')
              data.items = getCatalogItems(input.keywords);
          else data.items = getPopularItems();
      
          function getCatalogItems(keywords) {
              var sc = new GlideRecord('sc_cat_item');
              sc.addActiveQuery();
              sc.addQuery('123TEXTQUERY321', keywords);
              sc.addQuery('sys_class_name', 'NOT IN', 'sc_cat_item_wizard,sc_cat_item_content');
              sc.addQuery('sc_catalogs', 'e0d08b13c3330100c8b837659bba8fb4');
              sc.setLimit(100);
              sc.orderByDesc("ir_query_score");
              sc.query();
              var results = [];
              while (sc.next()) {
                  if (!$sp.canReadRecord(sc))
                      continue;
      
                  var item = {};
                  $sp.getRecordDisplayValues(item, sc, 'name,price,sys_id');
                  item.category = sc.getValue('category');
                  results.push(item);
              }
              return results;
          }
      
          function getPopularItems() {
              var items = [];
              var count = new GlideAggregate('sc_req_item');
              count.addAggregate('COUNT', 'cat_item');
              count.groupBy('cat_item');
              count.addQuery('cat_item.sys_class_name', 'NOT IN', 'sc_cat_item_guide,sc_cat_item_wizard,sc_cat_item_content');
              count.addQuery('cat_item.sc_catalogs', 'e0d08b13c3330100c8b837659bba8fb4');
              count.orderByAggregate('COUNT', 'cat_item');
              count.query();
              while (count.next() && items.length < 9) {
                  if (!$sp.canReadRecord("sc_cat_item", count.cat_item.sys_id.getDisplayValue()))
                      continue; // user does not have permission to see this item
      
                  var item = {};
                  item.name = count.cat_item.name.getDisplayValue();
                  item.category = count.cat_item.category.toString();
                  item.price = count.cat_item.price.getDisplayValue();
                  item.sys_id = count.cat_item.sys_id.getDisplayValue();
                  items.push(item);
              }
              return items;
          }
      })();

      このスクリプトは、データベースにクエリを実行して、入力変数が空のときに人気のアイテムを返す getPopularItems() を導入します。

    3. HTML テンプレートを次のスクリプトに置き換えます。
      <div class="panel panel-primary">
        <div class="panel-heading">Request an item from the catalog</div>
        <div class="panel-body">
          <input class="form-control" type="search" placeholder="Start typing here to search the list of catalog items" ng-model="c.data.keywords" ng-change="c.server.update()" ng-model-options="{debounce: 250}" />
          <h5 ng-if="!c.data.keywords">Showing the most popular items</h5>
          <ul class="list-group result-container">
            <li class="list-group-item" ng-repeat="item in c.data.items">
              <a href>{{item.name}}</a><span class="pull-right">{{item.price}}</span>
            </li>
          </ul>
        </div>
        <div class="panel-footer" ng-if="c.data.keywords">
          <ng-pluralize count="c.data.items.length"
                       when="{'0': 'No items found for ',
                           '1': 'One item matching ',
                           'other': 'Found {} items matching '}">
          </ng-pluralize>
          {{c.data.keywords}}
        </div>
      </div>
      

      このスクリプトは、サーバー スクリプトから返される人気のアイテムを表示するためのテンプレートを提供します。

    4. 変更を表示するには、テスト ページのプレビューを更新します。

      このウィジェットは、検索入力の前にユーザーに人気のアイテムを表示します。

      「カタログ アイテムのリストを検索するにはここに入力してください」というテキストが表示された検索ボックス。

    既存のウィジェットの埋め込み

    SC カタログ アイテム ウィジェットを埋め込むことで、ユーザーがクイック注文ウィジェットで サービスカタログ アイテムを表示して購入できるようにします。

    始める前に

    必要なロール:admin または sp_admin

    このタスクについて

    コードを複製する代わりに、ウィジェットを埋め込んで既存の機能を活用することができます。SC カタログ アイテム ウィジェットは、ユーザーが サービスカタログ アイテムを表示および購入できるようにする基本システム ウィジェットです。

    手順

    1. SC カタログ アイテム ウィジェットを調べます。
      SC カタログ アイテム ウィジェットを埋め込む前に、ウィジェットを調べて、アクセスする必要があるデータを理解してください。場合によっては、クイック注文ウィジェットのクライアントまたはサーバー スクリプトを更新して、正しいデータが埋め込みウィジェットに渡されるようにする必要があります。
      1. <yourInstanceURL>/sp_config?id=widget_editor に移動します。
      2. SC カタログ アイテム ウィジェットを開きます。
      3. ウィジェット ID は widget-sc-cat-item であることに注意してください。
        この ID を使用して、クライアント スクリプトにウィジェット モデルを埋め込みます。
      4. サーバー スクリプトを調べます。

        data オブジェクトには、input または options オブジェクトのいずれかによって値が入力される sys_id プロパティが含まれている点に注目してください。入力オプションにもsys_idが含まれていない場合、$sp.getParameter() メソッドは要求クエリ文字列からsys_idを取得します。

        If Else ステートメントを使用して sys_id を取得するサンプル サーバー スクリプト。

        input オブジェクトに値を入力するには、クイック注文ウィジェットのクライアント スクリプトからカタログ アイテム sys_id を渡します。

    2. ウィジェット エディターから、クイック注文ウィジェットを開きます。
    3. クイック注文ウィジェットのクライアント スクリプトを次のスクリプトに置き換えます。
      function($location, spUtil) {
          var c = this;
      
          c.select = function(item_id) {
              if (c.openItem == item_id) {
                  c.openItem = null;
                  return;
              }
      
              renderCatalogItemWidget(item_id);
          }
      
          function renderCatalogItemWidget(item_id) {
              c.catalogItemWidget = null;
              spUtil.get("widget-sc-cat-item", {
                  sys_id: item_id
              }).then(function(response) {
                  c.catalogItemWidget = response;
                  c.openItem = item_id;
              });
          }
      }

      このスクリプトは、spUtil.get() を使用して ID によってウィジェット モデルを取得し (widget-sc-cat-item)、{sys_id: item_id} オブジェクトを定義します。このオブジェクトは、サーバー スクリプトを入力として投稿します。

    4. HTML テンプレートを次のスクリプトに置き換えます。
      <div class="panel panel-primary">
        <div class="panel-heading">Request an item from the catalog</div>
        <div class="panel-body">
          <input class="form-control" type="search" placeholder="Start typing here to search the list of catalog items" ng-model="c.data.keywords" ng-change="c.server.update()" ng-model-options="{debounce: 250}" />
          <h5 ng-if="!c.data.keywords">Showing the most popular items</h5>
          <ul class="list-group result-container">
            <li class="list-group-item" ng-repeat="item in c.data.items">
              <a href ng-click="c.select(item.sys_id)">{{item.name}}</a><span class="pull-right">{{item.price}}</span>
              <div class="catalog-item" ng-if="item.sys_id == c.openItem">
                  <sp-widget ng-if="c.catalogItemWidget" widget="c.catalogItemWidget" />
              </div>
            </li>
          </ul>
        </div>
        <div class="panel-footer" ng-if="c.data.keywords">
          <ng-pluralize count="c.data.items.length"
                       when="{'0': 'No items found for ',
                           '1': 'One item matching ',
                           'other': 'Found {} items matching '}">
          </ng-pluralize>
          {{c.data.keywords}}
        </div>
      </div>

      このテンプレートは、以下の操作を行います。

      • ng-click ディレクティブを使用してクリック時の動作を追加する。
      • sp-widget ディレクティブを使用して、埋め込み SC カタログ アイテム ウィジェットを表示する。
    5. CSS を次のスクリプトに置き換えます。
      .result-container {
        margin-top: 10px;
      }
      
      .catalog-item {
        background-color: #f5f5f5;
        padding: 10px;
        @include border-top-radius($panel-border-radius);
        @include border-bottom-radius($panel-border-radius);
      }
      
    6. 変更を表示するには、テスト ページのプレビューを更新します。

      検索結果を選択すると、そのアイテムが埋め込み SC カタログ アイテム ウィジェットで開きます。

      埋め込みウィジェットに表示されている選択アイテムが表示されたテスト ページ。

    再利用可能なディレクティブを作成してウィジェットに追加

    Angular プロバイダーは複数のウィジェットに追加できる再利用可能なコンポーネントです。[ウィジェット Angular プロバイダー] テーブルを使用して、クイック注文ウィジェットの各結果の横にカテゴリ アイコンを表示するディレクティブを作成します。

    始める前に

    必要なロール:admin または sp_admin

    このタスクについて

    Angular プロバイダーを使用すると、クライアント スクリプト コントローラーに挿入できる Angular のディレクティブとサービスを構築できます。プロバイダーのコードは、特定のモジュールに追加されることはなく、匿名でなければならないため、標準の Angular ディレクティブまたはサービスとは異なります。

    手順

    1. 移動先 すべて > サービスポータル > Service Portal の構成 > ポータルテーブル > ウィジェット Angular プロバイダー.
      [ウィジェット Angular プロバイダー] テーブルが開きます。
    2. [新規] を選択して、新しいレコードを作成します。
    3. フォームに記入します。
      1. タイプと名前を追加します。
        • タイプ:ディレクティブ
        • :categoryIcon
      2. クライアント スクリプトを追加します。
        function() {
            return {
                template: '<span class="fa fa-stack fa-lg"><i class="fa fa-circle fa-stack-2x"></i><i class="fa fa-{{::icon}} fa-stack-1x fa-inverse"></i></span>',
                restrict: 'E',
                replace: true,
                scope: {
                    category: '='
                },
                link: function(scope, element) {
        
                    var _iconMap = {
                        "b06546f23731300054b6a3549dbe5dd8": "tablet",
                        /* Tablets */
                        "15706fc0a0a0aa7007fc21e1ab70c2f": "question",
                        /* Can we help you? */
                        "d68eb4d637b1300054b6a3549dbe5db2": "mobile-phone",
                        /* Mobiles */
                        "109cdff8c6112276003b17991a09ad65": "print",
                        /* Office and Print */
                        "5d643c6a3771300054b6a3549dbe5db0": "print",
                        /* Printers */
                        "2c0b59874f7b4200086eeed18110c71f": "plug",
                        /* Peripherals */
                        "2809952237b1300054b6a3549dbe5dd4": "desktop", 
                        /* Software */
                    };
        
                    scope.icon = _iconMap[scope.category] || "shopping-cart";
                }
            }
        }

        このスクリプトは、Category レコードの sys_id を サービスカタログ アイテムに関連付けます。表示されるアイコンは、サービスカタログ のカテゴリ レコードで定義されているアイコンです。

      3. [Save (保存)] を選択します。
    4. 新しい Angular ディレクティブをクイック注文ウィジェットに関連付けます。
      1. 移動先 すべて > サービスポータル > ウィジェット.
      2. クイック注文ウィジェットを開きます。
      3. [関連リスト] で、[Angular プロバイダー] を選択します。
      4. [Angular プロバイダー] リストで [ 編集 ] を選択し、categoryIcon Angular プロバイダーをクイック注文ウィジェットに関連付けます。
      5. [Save (保存)] を選択します。
    5. categoryIcon ディレクティブをクイック注文 HTML テンプレートに追加します。
      1. ウィジェット エディターから、クイック注文ウィジェットを開きます。
      2. HTML テンプレートを次のスクリプトに置き換えます。
        <div class="panel panel-primary">
          <div class="panel-heading">Request an item from the catalog</div>
          <div class="panel-body">
            <input class="form-control" type="search" placeholder="Start typing here to search the list of catalog items" ng-model="c.data.keywords" ng-change="c.server.update()" ng-model-options="{debounce: 250}" />
            <h5 ng-if="!c.data.keywords">Showing the most popular items</h5>
            <ul class="list-group result-container">
              <li class="list-group-item" ng-repeat="item in c.data.items">
                <a href ng-click="c.select(item.sys_id)"><category-icon category="item.category" style="margin-right: 10px"></category-icon>{{item.name}}</a><span class="pull-right">{{item.price}}</span>
                <div class="catalog-item" ng-if="item.sys_id == c.openItem">
                    <sp-widget ng-if="c.catalogItemWidget" widget="c.catalogItemWidget" />
                  </div>
              </li>
            </ul>
          </div>
          <div class="panel-footer" ng-if="c.data.keywords">
            <ng-pluralize count="c.data.items.length"
                         when="{'0': 'No items found for ',
                             '1': 'One item matching ',
                             'other': 'Found {} items matching '}">
            </ng-pluralize>
            {{c.data.keywords}}
          </div>
        </div>
        
    6. 変更を表示するには、テスト ページのプレビューを更新します。

      各結果の横にカテゴリ アイコンが表示されます。