スポンサーリンク

【pleasanter】編集画面のスクリプト実践例~項目の有効無効の切替、自作スクリプト内に「更新」ボタンの処理を実装する~

プリザンター
スポンサーリンク
※当サイトは広告を含みます

javaScript初心者の事務パートおばちゃんが挑むプリザンター開発。
前回は編集画面にボタンを追加して、ボタンクリックで日付とユーザー名を入力させる、という実践例をやりました。
【pleasanter】編集画面にボタンを追加の実践例~ボタン表示・非表示、日付・ユーザーのセット~

今回は、似たような感じですがもう一つ実践例をやってみたいと思います。
初心者が書く、初心者向けの内容です。初心者の私がつまづいたところをくどくど書いてますので、不要なところは読み飛ばしてください。

※おことわり
2023年5月時点の情報です。プリザンターのバージョンは 1.3.20.0 です。Google Chrome でやっています。
javascript,html,cssともに初心者です。調べながら、やってみながら、きっとこうすればいいんだ!という感じで書いていますので、間違っている場合、効率的な書き方ではない可能性が大いにあります。間違ってるよ!とか、こうしたほうがいいよ!ということがありましたら、コメント等で教えていただけると大変ありがたいです。

1.実践例の内容

こんなことを、スクリプトで実現してみたいと思います。

1.項目Aを選択し、作成ボタンクリック

2.項目Aが空欄の場合、メッセージ表示

3.項目欄が空欄でなければ、
・状況を「申請中」に変更
・承認ボタン・取消ボタン表示
・項目Aを編集不可にする

4.「承認」ボタンクリックで、
・状況欄を「承認済」に変更
・管理者欄にボタンをクリックしたユーザー名を入力
・日時Aに現在日付を入力
・保存(サーバーへ送信?)

※「取り消し」ボタンクリック

●状況が「申請中」の場合
・状況を「起票中」に変更
・項目Aの編集不可を解除

●状況が「承認済」の場合
・状況を「起票中」に変更
・管理者欄、日付A欄の入力内容を消去
・承認ボタン再表示

2.使用するサイト、完成イメージ

(1)画面イメージ

(2)エディタでの項目の設定

■状況

100,起票中 (規定値)
200,申請中
900,承認済

■分類A

英検1級
英検2級
英検3級

■担当者

選択肢一覧:[[Users]]
規定値:[[Self]]
読取専用にチェック

■管理者

選択肢一覧:[[Users]]
検索機能を使うにチェック (後述の 5(2)に検索機能を使うにチェックする理由を記載しています)

■日付A

エディタの書式:年月日

(3)スタイル

スタイルタブにて以下のスタイルを指定しています。
・コピーボタンを非表示にする
・追加する承認ボタン、取消ボタンのスタイルを指定するクラス mystyle
myStyleのCSSは以前の記事で利用したものを使っています。
【pleasanter】スクリプトでボタンを編集画面に追加する

出力先は「新規」「編集」にチェックします。

画面イメージ

cssコード

/*コピーボタン非表示*/
#OpenCopyDialogCommand{
    display: none !important;
}
/*承認ボタンスタイル*/
.mystyle {
    background: pink;
    border: solid 2px red;
    padding: 4px 10px 4px 10px !important;
}

3.コード

スクリプトに以下のコードを入力します

//日付を文字列に変更する関数
const fnDateFormat= function (d) {
    let d2 = d.toLocaleString("ja-JP", {
        year: "numeric",
        month: "2-digit",
        day: "2-digit",
        hour: "2-digit",
        minute: "2-digit",
        second: "2-digit"
    });
    return d2.slice(0,10);
}

//////////////////////////////
//     処理
/////////////////////////////
//編集画面起動時
$p.events.on_editor_load = function () { 
    //承認ボタン、取消ボタンの作成
    $("#MainCommands button:last-child").after('<button id="btnSyounin" class="button button-icon ui-button ui-corner-all ui-widget applied mystyle" onclick="fnSyounin($(this));" data-action="Update" data-method="put">承認</button>');
    $("#MainCommands button:last-child").after('<button id="btnTorikesi" class="button button-icon ui-button ui-corner-all ui-widget applied mystyle" onclick="fnTorikesi();">取消</button>');
    //画面制御
    $('#Results_Status').prop('disabled', true);
    $('#Results_Ownner').prop('disabled', true);
    $('#Results_DateA').prop('disabled', true);    
    $('#Results_Manager').prop('disabled', true);
    fnGamenseigyo();
}

//作成・更新ボタンクリック
$p.events.before_validate = function (args) {
    if ($('#Results_Status').val() === '100' ) {
        if( $('#Results_ClassA').val() === '' ) {
            $p.clearMessage();
            $p.setMessage('#Message', JSON.stringify({
                Css: 'alert-warning',
                Text: '項目Aが選択されていません'
            }));
            return false;
        } else {
            $p.set($('#Results_Status'),'200');
            fnGamenseigyo(); 
            return true;   
        }  
    }   
}

//////////////////////////////
//     関数
/////////////////////////////
//画面制御
function fnGamenseigyo () {
    if ($('#Results_Status').val() === '100' ) {
        $('#Results_ClassA').prop('disabled', false);
        $('#btnSyounin').hide();
        $('#btnTorikesi').hide();
    }
    if ($('#Results_Status').val() === '200' ) {
        $('#btnSyounin').show(); 
    }
    if ( $('#Results_Status').val() === '900' ) {
        $('#btnSyounin').hide();
    }
    if ($('#Results_Status').val() === '200' || $('#Results_Status').val() === '900') {
        $('#btnTorikesi').show();
        $('#Results_ClassA').prop('disabled', true);
    }
}

//承認ボタンクリック
function fnSyounin(obj) {
    $p.set($('#Results_Manager'),$p.userId());
    $p.set($('#Results_DateA'),fnDateFormat(new Date()));
    $p.set($('#Results_Status'),'900');
    fnGamenseigyo();
    $p.send(obj);
}

//取消ボタンクリック
function fnTorikesi() {
    if ( $('#Results_Status').val() === '200' ) {
        $p.set($('#Results_Status'),'100');
    }
    if ( $('#Results_Status').val() === '900' ) {
        $p.set($('#Results_Manager'),'');
        $p.set($('#Results_DateA'),'');
        $p.set($('#Results_Status'),'100');
    }
    fnGamenseigyo(); 
}

4.コードの解説

上からざっくりと。。。

(1)日付を文字列に変更する関数

//日付を文字列に変更する関数
const fnDateFormat= function (d) {
    let d2 = d.toLocaleString("ja-JP", {
        year: "numeric",
        month: "2-digit",
        day: "2-digit",
        hour: "2-digit",
        minute: "2-digit",
        second: "2-digit"
    });
    return d2.slice(0,10);
}

自作の関数です。詳しい説明は前回の記事、またはjavaScript初心者メモ 日付のをテキストにする方法 をご参照ください。toLocaleStringを使用しない別の方法もリンク先の記事(javaScript初心者メモのほう)に記載しています。

引数で受け取った日付データから 2023/05/20 12:05:05 等の文字列を作成し、最後に slice で左から10文字 2022/05/20 を取り出して返却しています。
ここで slice で左から10文字を取り出す処理をせず、そのまま返しても、項目の設定が「年月日」のため、2022/05/20 だけが表示されます。が。なんとなくsliceしています。

(2)編集画面起動時の処理

//編集画面起動時
$p.events.on_editor_load = function () { 
    //承認ボタン、取消ボタンの作成
    $("#MainCommands button:last-child").after('<button id="btnSyounin" class="button button-icon ui-button ui-corner-all ui-widget applied mystyle" onclick="fnSyounin($(this));" data-action="Update" data-method="put">承認</button>');
    $("#MainCommands button:last-child").after('<button id="btnTorikesi" class="button button-icon ui-button ui-corner-all ui-widget applied mystyle" onclick="fnTorikesi();">取消</button>');
    //画面制御
    $('#Results_Status').prop('disabled', true);
    $('#Results_Ownner').prop('disabled', true);
    $('#Results_DateA').prop('disabled', true);    
    $('#Results_Manager').prop('disabled', true);
    fnGamenseigyo();
}

$p.events.on_editor_load = function () { 編集画面を立ち上げたときに実行したい処理をここに書く }

もう、何度も登場しています $p.events.on.editor_load。ボタンのhtmlを作成して追加し、編集不可にしたい項目を編集不可設定し、自作関数 fnGamenseigyo を実行しています。後述しますが fnGamenseigyo では状況欄に応じて、項目の有効無効の切り替え、ボタンの表示非表示の切り替えを制御するコードを書いています。

(3)承認ボタンのhtmlの作成と追加

上記のコードから承認ボタンの個所の説明です。

$("#MainCommands button:last-child").after('<button id="btnSyounin" class="button button-icon ui-button ui-corner-all ui-widget applied mystyle" onclick="fnSyounin($(this));" data-action="Update" data-method="put">承認</button>');

長いので分割して。。。

$(“#MainCommands button:last-child”).after( ここに追加したいhtml );

一番下のボタンの並びの最後に指定したhtmlを追加します。

<button> で始まり </button> で終わる個所がボタンタグのカタマリです。
以下、ボタンタグ内の内容を分割して解説

id=”btnSyounin”
ボタンにidを付与しています。ボタンを表示したり、非表示にしたりしたいときにはidがあると要素の指定がしやすく便利です。

class=”button button-icon ui-button ui-corner-all ui-widget applied mystyle”
ボタンにクラスを指定しています。button ~ applied まではコピペしてきたものです。ここまでだと、更新ボタンや作成ボタンと同じような見た目になります。
mystyle は自分でスタイルタグで指定したスタイルです。ボタンをピンクにしています。詳細は2(3)スタイルを参照してください。

onclick=”fnSyounin($(this));”
ボタンをクリックしたときに実行したい処理をここに記載します。
後ほど説明する fnSyounin() を指定しています。(後述 5(2))
ポイントは 引数に $(this) を渡しているところです。

data-action=”Update” data-method=”put”
ここもポイントです。 fnSyounin() で、「更新」ボタンを押した時のように、保存?というのかサーバーに送信?というのか、変更した内容を保存する処理を行いたい。その機能を持たせるためには button タグ内にこれをつけておかないとダメっぽい。上記の引数に $(this) を持たせるのも必要っぽい。
理由はわからん!詳しい人に聞いてください。私も知りたい!
なんかわからんけど、「更新」ボタンのhtmlを見てみたらついていたから入れた。入れないと、ダメだった。

(4)取消ボタンのhtmlの作成と追加

$(“#MainCommands button:last-child”).after(‘<button id=”btnTorikesi” class=”button button-icon ui-button ui-corner-all ui-widget applied mystyle” onclick=”fnTorikesi();”>取消</button>’

取消ボタンを生成している部分です。承認ボタンと違うところは onclick のところ。起動するfunction名が違います。取消ボタンで起動する処理 “fnTorikesi(); では、保存(サーバーへ送信)する処理は伴わないため、引数は持たせていません。data-action=”Update” data-method=”put” も必要ないため入れていません。

(5)画面制御 項目を読み取り専用にする

//画面制御
$('#Results_Status').prop('disabled', true);     //状況欄を編集不可に
$('#Results_Ownner').prop('disabled', true);     //担当者欄を編集不可に
$('#Results_DateA').prop('disabled', true);      //日付欄を編集不可に 
$('#Results_Manager').prop('disabled', true);    //管理者欄を編集不可に
fnGamenseigyo();

画面制御っていうんですかね?項目を編集できないように(無効に)しています。
「状況」「担当者」「日付A」「管理者」欄はボタン操作で自動で入力するようにしており、手入力で勝手に入れてもらいたくないため、編集不可にしています。
エディタタブの項目の詳細設定で「読取専用」にチェックを入れればいいんじゃね?と思うところですが、条件によって項目の編集可否をコントロールしたい場合、スクリプトで編集可否をコントロールしているほうが都合がよいときがあるため、そのようにしています。
また、項目の「読取専用」にチェックを入れると、値の取り出しや変更が難しくなるような気がしているため、スクリプトを使用するほうがよい場合もあるように思います。
ちなみに項目を読み取り専用にした場合の値の取り出し方はこちらにありました。
https://pleasanter.org/manual/faq-readonly-value

$( 要素セレクタ ).prop(‘disabled’, true)
とすると、指定した項目が編集できなくなります。無効にする、という言い方をするようです。

$( 要素セレクタ ).prop(‘disabled’, false)
とすると、指定した項目が編集できるようになります。
true と false で編集可否の切り替え(有効無効の切り替え)ができるんですね。

項目種類によっては
$(‘#Results_DescriptionA’).attr(‘readonly’,true);
とする場合もあります。
項目の編集可否については、別の記事で詳しくやっています。
【pleasanter】項目を読取専用(編集不可)にする

ちなみにこれらはjQuery の書き方なんですね。
https://qiita.com/pugiemonn/items/5db6fb8fd8a303406b17
項目の指定は $( 要素セレクタ ) 。ここではidで項目を指定しているため指定したい項目のid名の頭に#を付けます。 $(‘#Results_Status’) で状況欄を指定できます。

最後の fnGamenseigyo(); は自作の関数を実行しています。
ちなみに初心者の余談ですが、定義した関数名にカッコを付けることで関数を実行してくれるんです。ってね。
fnGamenseigyo; と書いても「しーーーーん」でなにも起こらず
fnGamenseigyo(); と書くと「はいーー!お呼びでーーー!」っと関数実行処理が走ります。
javaScriptでは () は、引数を入れるためのモノだけではないんですね。

(6)作成ボタンをクリックしたときの処理(または更新ボタン)

$p.events.before_validate = function (args) {
    if ($('#Results_Status').val() === '100' ) {
        if( $('#Results_ClassA').val() === '' ) {
            $p.clearMessage();
            $p.setMessage('#Message', JSON.stringify({
                Css: 'alert-warning',
                Text: '項目Aが選択されていません'
            }));
            return false;
        } else {
            $p.set($('#Results_Status'),'200');
            fnGamenseigyo(); 
            return true;   
        }  
    }   
}

作成または更新ボタンをクリックしたときに走る処理です。バリデーションチェックの前に走ります。今回は[状況]が100起票中の場合のみ処理(分類Aが空白でないことのチェック)を行うようにしています。

$p.events.before_validate については、公式マニュアルに説明があります。
似たような感じの $p.events.after_validate もあります。

よくわかってないけど、私の理解するところだと、「作成」ボタン、「更新」ボタンを押したときに入力必須にしている項目が入力されているか? や、数値項目に数値以外が入ってないか? 等、プリザンター自体の機能でチェックが入りますが、それがバリデーションチェックなのかな?と思っています。で、それの前、または後に $p.events.before_validate (またはafter) で指定した処理が走るのかな、と。

return で true または false を返している部分があります。
ここがポイントで$p.events.before(after)_validate が false を返すと、「作成」処理、または「更新」処理が行われません。要は、入力必須項目が入力されてなくてエラーメッセージが出たときと同じ「作成したくてもできない。。。」状態となります。

true を返すと、通常通り「作成」処理、または「更新」処理が行われます。

なので、$p.events.before(after)_validate は入力チェック等をスクリプトで行いたいときに使用することが多いです。

ちなみに、「作成」ボタンの時だけ動いてほしいよう、という場合は
$p.events.before_validate_Create

「更新」ボタンの時だけ動いてほしいよう、という場合は
$p.events.before_validate_Update

と最後にボタンの data-action 属性を付け足します。
data-action属性とは、開発者ツールで各ボタンのhtmlを見るとわかります。

data-action属性ってなんだ?というと、…わかりません!詳しい人に聞いてください!私も知りたい!
なんかわからんけど、data-XXXというのは、javaScriptでもともと決まっている属性ではなく、勝手に作れる属性らしい(多分)。なので、きっとプリザンターさんが独自に持っている、しかるべきところにこれをつけるといい感じに動いたりしてくれるものなんだろうな。。。と理解しています。

(7)メッセージを表示する

上記のコードでは分類Aを検査して、空白の場合、メッセージを表示して、falseを返します。
メッセージを表示する部分について、切り出して解説します。

$p.clearMessage();
$p.setMessage('#Message', JSON.stringify({
    Css: 'alert-warning',
    Text: '項目Aが選択されていません'
}));

こちらを見てコピペしたら、できました。
https://pleasanter.org/manual/script-set-message

alert(‘項目Aが選択されていません’) でもいいのですが、せっかくだから、ほかのプリザンターからのメッセージと同じように下のバーに出したいなと思って。

なんだかわからないけど、$p.setMessage の前に $p.clearMessage(); をやっておいた方がよいみたいなので、そうしました。

$p.setMessage はカタマリをコピペして、Css: のところと、Text: のところを修正して使用します。
Css: のところはリンクのマニュアルを見てください
Text: のところは表示したいメッセージを指定します。

(8)分類Aが空白でない場合の処理

else の部分の処理の解説です。
分類Aが空白でない場合、
・状況を「申請中(200)」に変更し、
・fnGamenseigyo(); を実行し
・true を返します

trueを返すと「作成」または「更新」の処理が行われます。

5.コードの解説(関数部分)

長くなってきたので、分割。。。

(1)画面制御とボタンの表示切替

//画面制御
function fnGamenseigyo () {
    //状況が起票中(100)の場合
    if ($('#Results_Status').val() === '100' ) {
        $('#Results_ClassA').prop('disabled', false);    //分類Aを無効にする
        $('#btnSyounin').hide();     //承認ボタン非表示
        $('#btnTorikesi').hide();    //取消ボタン非表示
    }
    //状況が申請中(200)の場合
    if ($('#Results_Status').val() === '200' ) {
        $('#btnSyounin').show();    //承認ボタン表示
    }
    if ( $('#Results_Status').val() === '900' ) {
        $('#btnSyounin').hide();   //承認ボタン非表示
    }
    //状況が申請中(200)または承認済(900)の場合
    if ($('#Results_Status').val() === '200' || $('#Results_Status').val() === '900') {
        $('#btnTorikesi').show();    //取消ボタン表示
        $('#Results_ClassA').prop('disabled', true);    //分類Aを編集不可にする
    }
}

fnGamenseigyo の解説です。編集画面起動時、作成(更新)ボタンクリック時、承認・取消ボタンクリック時、といろんなところから呼び出しされます。

前にやっているのでコードの解説は不要ですかね。この例では、状況欄に応じたボタン表示・非表示や項目の有効無効の切り替えをここでまとめてやるようにしています。あくまで一例であり、いろんなやり方があると思います。

ちなみに javaScript では「または」は || です。「かつ」は && です。

余談です。今回はないのですが、ajax通信などの非同期処理を行う場合、非同期処理はあとでまとめて行われるイメージなので、いろいろとタイミングに気を付けることが必要になります。今回はのっぺりした処理なので、タイミングを気にする必要もないため、こういった処理をまとめて行っています。

(2)承認ボタンの処理 画面の更新と保存($p.send)

//承認ボタンクリック
function fnSyounin(obj) {
    $p.set($('#Results_Manager'),$p.userId());    //管理者欄にユーザーIDをセット
    $p.set($('#Results_DateA'),fnDateFormat(new Date()));    //日付欄に日付をセット
    $p.set($('#Results_Status'),'900');    //状況欄を承認済(900)に変更
    fnGamenseigyo();        //fnGamenseigyo処理実行
    $p.send(obj);           //編集画面の現在の内容を保存(サーバーに送信)
}

承認ボタンをクリックしたときに fnSyounin が実行されるわけですが、その処理内容です。

$p.set($(‘#Results_Manager’),$p.userId());
ログインしているユーザーIDを「管理者」欄に入力します。
$p.userId() はログインしているユーザーIDを取得するプリザンターの関数です。

2(2)に記載していますが、「管理者」欄の選択肢には[[Users]]を設定しています。
こうすることで、ユーザーIDを入力すると、表示値がユーザー名となるわけです。

スクリプトでユーザーを入力するときは「検索機能」にチェックを入れておくことがおススメです。ユーザーが多いとき、プリザンターの設定状況で「200までしか表示しない」とかなっている場合、選択肢として表示される200内に該当のユーザーがいないと、そんなユーザーID見当たりませんと空欄表示になってしまいます。
ここはユーザーの多さと、現場のプリザンターの設定状況によりますが、チェック入れておいた方が無難だなと思っています。

$p.set($(‘#Results_DateA’),fnDateFormat(new Date()));
日付欄に今日の日付をセットしています。一番上に定義した fnDateFormat を実行し、戻り値の yyyy/mm/dd 形式の日付文字列を日付A欄に入力しています。

$p.set($(‘#Results_Status’),’900′);
状況欄を「承認済」(900)に変更します。

fnGamenseigyo();
fnGamenseigyoを実行します。一つ上で状況欄を900に変更しているので、状況欄900の場合の処理が実行されます。

$p.send(obj);
マニュアルには見当たらなかったのですが、こう書くと、更新ボタンをクリックしたときのように現在の編集画面の内容が保存されます。web界の人間ではないので「保存」という言葉を使ったほうがしっくりくるのですが、サーバーに送信される、といった方がいいのかもしれません。

なぜ、わざわざ保存しているかというと、「承認」したつもりでも、「戻る」ボタンで戻ったり(ほぞんされないよ?と注意メッセージは出るが)、×で画面を閉じてしまったりしたら、承認ボタンで更新した内容が保存されないので、それを回避したいためです。フロー内容によっては、確定しておかないとつじつまが合わなくなる場面が出てきます。

(3)取消ボタン

//取消ボタンクリック
function fnTorikesi() {
    //状況が申請中(200)の場合の処理
    if ( $('#Results_Status').val() === '200' ) {
        $p.set($('#Results_Status'),'100');    //状況欄を申請中から起票中(100)に
    }
    //状況が承認済(900)の場合の処理
    if ( $('#Results_Status').val() === '900' ) {
        $p.set($('#Results_Manager'),'');    //管理者欄の入力内容を消去
        $p.set($('#Results_DateA'),'');    //日付欄の入力内容を消去
        $p.set($('#Results_Status'),'200');    //状況欄を承認済から申請中(200)に
    }
    //必ず実行される処理。fnGamenseigyo実行
    fnGamenseigyo(); 
}

取消ボタンをクリックしたときには fnTorikesi が実行されるわけですが、その処理内容です。

「取消」ボタンは状況が「承認済」の場合と「申請中」の場合に表示されます。
どの状況において「取消」ボタンがクリックされたかにより、状況欄に応じた2通りの処理を記載しています。
どの場合であっても最後に fnGamenseigyo が実行されます。ここで項目の有効無効、ボタンの表示非表示が状況欄に応じた内容が設定されます。

6.最後に

また、長くなってしまった。。。

現実ではまあ、そのまま使うことはないだろうというフローですが、部分部分で利用できるコードはあるかと思っています。

お読みいただきありがとうございました!

7.参考文献・記事

jQueryのprop()でdisabled属性を切り替える
https://qiita.com/pugiemonn/items/5db6fb8fd8a303406b17

JavaScriptでフォームのボタンを無効にする方法を現役エンジニアが解説【初心者向け】
https://magazine.techacademy.jp/magazine/22329

プリザンター公式マニュアルより
https://pleasanter.org/manual/script-set-message
https://pleasanter.org/manual/script-clear-message
https://pleasanter.org/manual/script-events-on-editor-load
https://pleasanter.org/manual/script-events-before-validate
https://pleasanter.org/manual/script-events-after-validate
https://pleasanter.org/manual/faq-readonly-value

内部リンク
↓ボタンシリーズ
【pleasanter】スクリプトでボタンを編集画面に追加する
【pleasanter】編集画面のボタンを表示したり非表示にしたりする~スタイル・スクリプトの利用~
【pleasanter】編集画面にボタンを追加の実践例~ボタン表示・非表示、日付・ユーザーのセット~
【pleasanter】編集画面のスクリプト実践例~項目の有効無効の切替、自作スクリプト内に「更新」ボタンの処理を実装する~

↓その他ボタン関連
【pleasanter】スクリプトでhtmlの追加 リンクやボタンを編集画面に表示する ~実践例あり~
【pleasanter】拡張HTML~フィールドの右(左)に追加テキストを表示、ボタンの表示

↓その他
【pleasanter】いろいろ非表示にしてみた
【javaScript】日付をテキストにする方法(初心者のメモ帳)
【pleasanter】項目を読取専用(編集不可)にする

コメント

タイトルとURLをコピーしました