2017.08.10

SkyVisualEditor AppComponentでアドバンスドUXを実現する

  • このエントリーをはてなブックマークに追加
  • follow us in feedly
TerraSkyDay 2017が先日7月21日(金)に開催されおかげさまで大盛況でございました。
ご来場くださいました皆様本当にありがとうございます。

今回のブログの内容はDeepDiveセッションで講演しました「Salesforceの入力をもっと便利に!アドバンスドUX実現のためのコンポーネント開発手法を知る」のDEMO内容で、SkyVisualEditorを使いExcelからコピペでデータを登録できるAppComponentの紹介をいたしました。

セッション内でお約束しましたこのAppComponentの作成の仕方を紹介していきたいと思います。

【ご注意ください】
・本ブログの内容は個別の開発ですので標準サポートの対象外でございます。
・ご利用につきましては、ご自身の判断でご利用いただきテスト等は行ってください。
・コードの内容についてはサンプル程度とお考え下さい。ご質問はお受けできません。

目 次

  1. Excelからコピペ AppComponentの紹介
  2. SkyVisualEditorのStudioでの配置、設定の仕方
  3. ExcelからコピペAppComponentの作成方法
    1. Visualforceコンポーネントの作成
      1. 受け取る属性
      2. JavaScript
      3. UI「ダイアログ部分」
    2. ダイアログ呼び出し用のページスクリプトの作成
    3. XML定義ファイルの作成
  4. 仕上げに
  5. まとめ
  6. おまけ

Excelからコピペ

コピペ後

AppComponentは、上の画像で言いますとExcelのデータを張り付けるダイアログの部分になります。
Excelからコピペというカスタムボタンをクリックして開きます。このダイアログの部分はVisualforceコンポーネントで作成しております。このVisualforceコンポーネントをSkyVisualEditorの部品として配置できるようにするというものになります。

※Excelデータ列の順番とVisualforceページのテーブルの列項目の順番は一致している必要があります。
 ただし、選択用のチェックボックス、カスタムボタンは除きます。

Studioでの設定

①カスタムボタンの配置
SkyVisualEditorの標準のコンポーネントであるカスタムボタンを配置します。
配置場所は任意の場所でかまいませんが、テーブルの近くが良いかと思います。
配置後に、カスタムボタンのプロパティを設定します
・【 値 】ボタンのラベル
・【 幅 PX 】ボタンの幅を指定
・【 動作選択 】JavaScript/Apexを選択
・【 OnClick 】にページスクリプトに作成したJavaScriptを呼び出す設定をします。
openModal( 対象となるテーブルのId , ダイアログのタイトル );
②AppComponentを配置
以下で作成していく「ExcelからコピペAppComponent」を配置します。 配置場所は任意の場所でかまいませんが、テーブルの下辺りに配置したら良いと思います。
③AppComponentのプロパティを設定
設定するプロパティは【対象テーブルのId】【行追加メソッド】の2つです。
今回のExcelからコピペAppComponentの作成手順は以下になります。

I.Visualforceコンポーネントの作成

II.ダイアログ呼び出し用のページスクリプトの作成

III.XML定義ファイルの作成

I. Visualforceコンポーネントの作成

Visualforceコンポーネントの構成要素は3つになります。
 i.受け取る属性
  Studioのプロパティから設定される【対象テーブルのId】【行追加メソッド】の2つ

 ii.JavaScript
  メインロジックの部分になります。今回はApexは使用せずJavaScriptで行います。
  主に張り付けられたExcelデータの解析とデータのセットをします。

 iii.UI「ダイアログ部分」
  開いたダイアログでデータを張り付ける為のテキストエリア
  処理を実行させるボタン

それでは完成コードを元に解説していきます。
開発のメニューから【Visualforceコンポーネント】を選択します。
Visualforceコンポーネント名は【CopyDialog】としておきます。

<apex:component access="global">

    <!--  i.受け取る属性 -->
    <apex:attribute name="ComponentId" type="String" description="テーブルのId" />
    <apex:attribute name="method" type="ApexPages.Action" description="行追加のメソッド" />

    <!--  ii.JavaScript -->
    <script>

        // 貼り付けられたデータを配列で返す
        function pasteDataToArray(data){
            var records = [];
            var rows = data.split(/\r\n|\r|\n/);
            rows.forEach(function(rdata, i) {
                if(rdata.length > 0){
                    records.push(rdata.split(/\t/));
                }
            });
            return records;
        }
        
        // 貼り付けられたデータ分の行を作成
        function tableAddRows(tableId){
            var tableRows = [];
            var records = pasteDataToArray(jQuery('#' + tableId + '_copyTextArea').val());
            records.forEach(function(record, i){
                var row = {};
                tableRows.push(row);
            });
            tableRows.toJSON = null;
            jQuery('[id$=' + tableId + '_hiddenValue]').val(JSON.stringify(tableRows));
        }
        
        // 作成された行に貼り付けられたデータをセットする
        function setRecordData(tableId){
            var records = pasteDataToArray(jQuery('#'+ tableId + '_copyTextArea').val());
            var rows = jQuery('[id$=\\:' + tableId + '_table] tbody tr');
            rows.each(function(i,row){
                var inputs = jQuery(row).children('td').children('span').find('input[class!=btn],select,textarea');
                inputs.each(function(j,input){
                    jQuery(input).val(records[i][j]);
                });
            });
            jQuery('[id$=' + tableId + '_dialog]').dialog('close');
        }
        
    </script>
    
    
<!--  iii.UI「ダイアログ部分」 -->
<div id="{!componentId}_dialog" style="display:none;">
    <div class="body">
        <textarea id="{!componentId}_copyTextArea" style="width:100%; height:10em;"></textarea>
    </div>
    <div class="footer" style="padding:20px 5px; text-align:left;">
        <apex:commandButton value="コピーする" 
                            onclick="tableAddRows('{!componentId}');" 
                            action="{!method}" 
                            oncomplete="setRecordData('{!componentId}');" 
                            reRender="{!componentId},messagesPanel," />
    </div>
</div>

</apex:component>
Visualforceコンポーネント(完成コード)

i. プロパティを受け取る属性の作成

【対象テーブルのId】
 ダイアログやJavaScriptの処理を呼び出す時に目印として受け渡します。

【行追加メソッド】
 SkyVisualEditorが持っていますApexの行追加メソッドをそのまま利用します。
 データ型が「ApexPages.Action」でメソッドが受け取れます。
 詳しくは
 https://developer.salesforce.com/docs/atlas.ja-jp.pages.meta/pages/pages_compref_attribute.htm
<apex:attribute name="componentId" type="String" description="対象テーブルのId" />
<apex:attribute name="method" type="ApexPages.Action" description="行追加メソッド" />
プロパティを受け取る属性部分

ii. JavaScriptでデータを張り付ける処理を作成する

JavaScriptの解説をします。

【pasteDataToArray】
Excelからコピーして貼り付けられたデータを配列で返します。

【tableAddRows】
Excelからコピーして貼り付けられたデータの行数分の空オブジェクトを作成して、データテーブルの下にある隠しフィールドへJSON 文字列に変換してセットします。
 ※SkyVsialEditorのマスター取得機能の流用です。

【setRecordData】
ダイアログのボタンをクリックしてActionが呼ばれた後に、oncompleteの処理としてこのメソッドが呼ばれます。Actionが呼ばれるとデータテーブルに【tableAddRows】でセットした分の行が作成されますので、そこにExcelからコピーして貼り付けられたデータを貼り付けていく処理です。
// 貼り付けられたデータを配列で返す
function pasteDataToArray(data){
    var records = [];
    var rows = data.split(/\r\n|\r|\n/);
    rows.forEach(function(rdata, i) {
        if(rdata.length > 0){
            records.push(rdata.split(/\t/));
        }
    });
    return records;
}

// 貼り付けられたデータ分の行を作成
function tableAddRows(tableId){
    var tableRows = [];
    var records = pasteDataToArray(jQuery('#' + tableId + '_copyTextArea').val());
    records.forEach(function(record, i){
        var row = {};
        tableRows.push(row);
    });
    tableRows.toJSON = null;
    jQuery('[id$=' + tableId + '_hiddenValue]').val(JSON.stringify(tableRows));
}

// 作成された行に貼り付けられたデータをセットする
function setRecordData(tableId){
    var records = pasteDataToArray(jQuery('#'+ tableId + '_copyTextArea').val());
    var rows = jQuery('[id$=\\:' + tableId + '_table] tbody tr');
    rows.each(function(i,row){
        var inputs = jQuery(row).children('td').children('span').find('input[class!=btn],select,textarea');
        inputs.each(function(j,input){
            jQuery(input).val(records[i][j]);
        });
    });

    // クリアしてダイアログを閉じる
    jQuery('[id$=' + tableId + '_copyTextArea]').val('');
    jQuery('[id$=' + tableId + '_hiddenValue]').val('');
    jQuery('[id$=' + tableId + '_dialog]').dialog('close');
}
JavaScriptの部分

iii. UI 「ダイアログ部分」

ダイアログ部分ですが、divで囲みこの部分がダイアログとして開きます。
その中にはデータを張り付けるTextArea、処理を実行させるためのボタンを配置します
ボタンですが普通のHTMLのボタン【<input type="button" />】で配置するとApexの処理が呼び出せないので【<apex:commandButton />】Visualforceのコマンドボタンを利用します。
<!-- ダイアログのidにテーブルのidを利用する -->
<div id="{!componentId}_dialog" style="display:none;">
    <div class="body">
        <!-- textareaのidにテーブルのidを利用する -->
        <textarea id="{!componentId}_copyTextArea" style="width:100%; height:10em;"></textarea>
    </div>
    <div class="footer" style="padding:20px 5px; text-align:left;">

  <!-- 
    input type="button"ではなくapex:commandButtonを使う
    クリックでJSの処理、次にSkyVisualEditorのメソッドを呼びだす。
    oncompleteで更にJSの処理を行います。
  -->
        <apex:commandButton value="コピーする" 
                            onclick="tableAddRows('{!componentId}');" 
                            action="{!method}" 
                            oncomplete="setRecordData('{!componentId}');" 
                            reRender="{!componentId},messagesPanel," />
    </div>
</div>
UI 「ダイアログ部分」の部分

II. ダイアログ呼び出し用のページスクリプトの作成

ダイアログを呼び出す処理です。SkyVisualEditorではJavascriptのライブラリとしてjQuery UIを利用しているのでそのまま利用しダイアログを開きます。
このスクリプトは共通して使いますのでページスクリプトに記述します。
// jQuery UIのダイアログを開く
function openModal( compId, dialogTitle ){
    jQuery('#' + compId + '_dialog').dialog({
        autoOpen: true,  // 自動でオープンしない
        modal: true,      // モーダル表示する
        resizable: true, // リサイズしない
        draggable: true, // ドラッグしない
        show: "clip",     // 表示時のエフェクト
        hide: "fade",     // 非表示時のエフェクト
        width:"600",
        title:dialogTitle
    });
}
ダイアログを開くJavaScriptコード ※ページスクリプトに貼り付けてください

III. XML定義ファイルの作成

AppComponent.xmlファイルを編集します。AppComponent.xmlファイルはCDKをダウンロードして必要な部分を編集してください。変更箇所は【 】部分にとApexComponentの部分とPropertiesの部分になります。

CDKのダウンロードは以下からできます。
https://info.skyvisualeditor.com/developer/develop/
<?xml version="1.0" encoding="UTF-8" ?>
<ComponentDefinition xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="AppComponent_Schema.xsd" version="1.0">

        <Title>【AppComponentのタイトルを指定】</Title>

        <Description>【AppComponentの詳細説明】</Description>

        <Publisher>【AppComponent作成者 例)TerraSky Co.,Ltd.】</Publisher>

        <WebSite>【Webサイトの指定】</WebSite>

        <AppComponents>

                <AppComponent name="【AppComponent名 例)SVE_CopyDialog】">
                
                        <Title>【Studioで表示される名前 CopyDialog 】</Title>
                        <Description>【機能の詳細など 例)Excelからコピペできます】</Description>
                        <HelpURL>【ヘルプサイトなど指定 】</HelpURL>

                        <!-- inlineと指定するとSpanタグとして出力 blockの場合はDivタグで出力します -->
                        <Layout>【出力タグ指定 例)block】</Layout>
                        
                        <!-- 出力されたタグのサイズの指定 -->
                        <Height value="100" resizable="false" />
                        <Width value="350" resizable="false" />

                        <!-- 
                             コンポーネントペイン(部品が並ぶ所)とキャンバスに配置する時に
                             表示される画像を静的リソースそれぞれ指定できます -->
                        <!--
                        <ComponentImage resourceName="" />
                        <CanvasImage resourceName="" stretch="None" />
                        -->

                        <!-- 配置場所の指定 -->
                        <!-- 
                             anyPlace属性をfalseにし、子要素のPlaceで「pageBlock」など個別に指定ができます
                             anyPlaceをtrueにすることでどこでも配置が可能になります。
                             <PutablePlaces anyPlace="false">
                                 <Place name="pageBlock"/>
                             </PutablePlaces>
                         -->
                        <PutablePlaces anyPlace="true">
                        </PutablePlaces>
                        
                        <!-- 
                              CopyDialogという名前のComponentを呼び出す
                              ApexAttributeのvar属性とPropertiesの子要素のname属性が一致している必要があります。
                        -->
                        <ApexComponent componentName="CopyDialog">
                                <!-- スタジオで設定した変数の値を、指定したAttribute名でコンポーネントに渡す設定 -->
                                <ApexAttribute name="componentId" var="componentId" />
                                <ApexAttribute name="method" var="method" />
                        </ApexComponent>

                        <!-- SVEスタジオにおいて、ユーザーが値を設定できるようプロパティを設定 -->
                        <Properties>
                                <!-- 
                              【記入例】
                                <String name="string" label="ラベル名" defaultValue="60" isRequired ="false" tooltip="" />
                              【プロパティの種類】
                                Int、String、Color、Boolean、SobjectComboBox、StyleClass、ComboBox、
                                NumericUpDown、RadioButton、VisualforcePageComboBox、MultiLineString、
                                ParentSObjectColumns、ChildSObjectColumns、IDSelector  計14種類
                                -->
                                <IDSelector name="componentId" label="対象テーブルのId" isRequired ="true" />
                                <String name="method" label="行追加メソッド" isRequired ="true" defaultValue="{!対象テーブルのId.importByJSON}" />
                        </Properties>
                </AppComponent>
        </AppComponents>
</ComponentDefinition>
【 】の部分ApexComponentの子要素、Propertiesの子要素を編集してください。
1.作成したVisualforceコンポーネントが保存されていることを確認してください。
2.AppComponent.xmlファイルを「AppComponent」という名前で静的リソースに
  アップしてください。
3.SkyVisualEditorのStudioを更新して、作成したAppComponentを配置してプロパティを設定
4.カスタムボタンを配置し、ページスクリプトに定義したスクリプトを設定する
5.デプロイして確認
今回はAppComponentの開発の仕方を説明しましたが、パッケージングまではしていません。
いろいろな組織のページで作成するにはAppExchangeパッケージにしていただけると、他組織でも利用することができるようになります。

今回は、皆様に完成コードを提供しておきたいと思います。
こちらを参考に独自のAppComponentを作成されてもよいと思います。
また、そのまま作成される場合には以下のことに注意をお願いいたします。

※このAppComponentは新規作成時のみの使用を想定しています。編集用では使えません。
セッションでは、ボタンのラベルを変えたり、表示位置を右にしたいなどの要望に対応できるようにプロパティを追加したりしましたのでおまけとしましてプロパティを追加したバージョンを掲載しておきます。

Visualforceコンポーネント
コンポーネント名:CopyDialog

【 以下の2つ属性を増やしています 】
※属性の部分 ボタンの名称を指定できるようにしています。
<apex:attribute name="btnLabel" type="String" description="ボタンラベル" />
※属性の部分 ボタンの位置も指定できるようにしています。【 right left center 】
<apex:attribute name="btnPosition" type="String" description="ボタンの位置" />
<apex:component access="global">

    <!-- 受け取る属性 -->
    <apex:attribute name="ComponentId" type="String" description="テーブルのId" />
    <apex:attribute name="method" type="ApexPages.Action" description="行追加のメソッド" />
    <apex:attribute name="btnLabel" type="String" description="ボタンラベル" />
    <apex:attribute name="btnPosition" type="String" description="ボタンの位置" />

    <!-- JavaScript -->
    <script>
        
        // 貼り付けられたデータを配列で返す
        function pasteDataToArray(data){
            var records = [];
            var rows = data.split(/\r\n|\r|\n/);
            rows.forEach(function(rdata, i) {
                if(rdata.length > 0){
                    records.push(rdata.split(/\t/));
                }
            });
            return records;
        }
        
        // 貼り付けられたデータ分の行を作成
        function tableAddRows(tableId){
            var tableRows = [];
            var records = pasteDataToArray(jQuery('#' + tableId + '_copyTextArea').val());
            records.forEach(function(record, i){
                var row = {};
                tableRows.push(row);
            });
            tableRows.toJSON = null;
            jQuery('[id$=' + tableId + '_hiddenValue]').val(JSON.stringify(tableRows));
        }
        
        // 作成された行に貼り付けられたデータをセットする
        function setRecordData(tableId){
            var records = pasteDataToArray(jQuery('#'+ tableId + '_copyTextArea').val());
            var rows = jQuery('[id$=\\:' + tableId + '_table] tbody tr');
            rows.each(function(i,row){
                var inputs = jQuery(row).children('td').children('span').find('input[class!=btn],select,textarea');
                inputs.each(function(j,input){
                    jQuery(input).val(records[i][j]);
                });
            });
            jQuery('[id$=' + tableId + '_dialog]').dialog('close');
        }
        
    </script>
    
    
    <!-- ダイアログ部分 -->
    <div id="{!componentId}_dialog" style="display:none;">
        <div class="body">
            <textarea id="{!componentId}_copyTextArea" style="width:100%; height:10em;"></textarea>
        </div>
        <div class="footer" style="padding:20px 5px; text-align:{!btnPosition};">
            <apex:commandButton value="{!btnLabel}" 
                                onclick="tableAddRows('{!componentId}');" 
                                action="{!method}" 
                                oncomplete="setRecordData('{!componentId}');" 
                                reRender="{!componentId},messagesPanel," />
        </div>
    </div>

</apex:component>
Visualforceコンポーネント:CopyDialog
AppComponent.xmlファイル

静的リソースに【AppComponent】という名前でアップロードしてください。
<?xml version="1.0" encoding="UTF-8" ?>
<ComponentDefinition xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="AppComponent_Schema.xsd" version="1.0">
        <Title>TerraSkyDayAppCom</Title>
        <Description>TerraSkyDayDemoAppComponent</Description>
        <Publisher>TerraSky Co.,Ltd.</Publisher>
        <WebSite>http://www.terrasky.co.jp/</WebSite>
        <AppComponents>

                <AppComponent name="SVE_CopyDialog">

                        <!-- Studioで表示されるコンポーネントのタイトル -->
                        <Title>CopyDialog</Title>

                        <!-- コンポーネントの説明 -->
                        <Description></Description>

                        <HelpURL>http://www.terrasky.co.jp/</HelpURL>

                        <!-- 配置されるコンポーネントがDIVかSPANか block OR inline -->
                        <Layout>block</Layout>

                        <!-- Studioで配置された時の高さや幅 -->
                        <Height value="100" resizable="false" />
                        <Width value="350" resizable="false" />

                        <!-- コンポーネントペイン(部品が並ぶ所)とキャンバスに配置する時に表示される画像を静的リソースそれぞれ指定できます -->
                        <!-->
                        <ComponentImage resourceName="" />
                        <CanvasImage resourceName="" stretch="None" />
                        -->

                        <!-- すべての場所に配置可能 -->
                        <PutablePlaces anyPlace="true">
                        </PutablePlaces>
                        
                        <!-- 呼び出すVFコンポーネント名 CopyDialog -->
                        <ApexComponent componentName="CopyDialog">


                                <!-- スタジオで設定した変数の値を、指定したAttribute名でコンポーネントに渡す設定 componentId method -->
                                <!-- <ApexAttribute name="" var="" /> -->
                                <ApexAttribute name="componentId" var="componentId" />
                                <ApexAttribute name="method" var="method" />
                                <ApexAttribute name="btnLabel" var="btnLabel" />
                                <ApexAttribute name="btnPosition" var="btnPosition" />

                        </ApexComponent>

                        <!-- SVEスタジオにおいて、ユーザーが値を設定できるようプロパティを設定 -->
                        <Properties>
                                <!-- 
                                        <String name="string" label="ラベル名" defaultValue="60" isRequired ="false" tooltip="" />
                                        Int、String、Color、Boolean、SobjectComboBox、StyleClass、ComboBox、
                                        NumericUpDown、RadioButton、VisualforcePageComboBox、MultiLineString、
                                        ParentSObjectColumns、ChildSObjectColumns、IDSelector 計14種類
                                -->
                                <IDSelector name="componentId" label="対象テーブルのId" isRequired ="true" />
                                <String name="method" label="行追加メソッド" isRequired ="true" defaultValue="{!対象テーブルのId.importByJSON}" /> 
                                <String name="btnLabel" label="ボタンのラベル" isRequired ="false"/>
                                <String name="btnPosition" label="ボタンの位置" isRequired ="false"/>
                                
                        </Properties>

                </AppComponent>

        </AppComponents>

</ComponentDefinition>
AppComponent.xml
ダイアログ開くためのページスクリプトです

SkyVisualEditorのStudioでページスクリプトに貼り付けます。
カスタムボタンを配置し、プロパティを設定します。
【動作選択】:JavaScript/Apex呼び出し
【 OnClick 】:openModal( '対象のテーブルのId', ダイアログのタイトル );
// jQuery Uiのダイアログを開く
function openModal(compId, dialogTitle){
    jQuery('#' + compId + '_dialog').dialog({
        autoOpen: true,  // 自動でオープンしない
        modal: true,      // モーダル表示する
        resizable: true, // リサイズしない
        draggable: true, // ドラッグしない
        show: "clip",     // 表示時のエフェクト
        hide: "fade",     // 非表示時のエフェクト
        width:"600",
        title:dialogTitle
    });
}
ダイアログ開くためのページスクリプト
40 件

関連する記事