onozaty / redmine-view-customize-scripts

Code examples for "Redmine View Customize Plugin"
https://github.com/onozaty/redmine-view-customize
111 stars 26 forks source link

ファイル形式でのカスタムフィールドに対し、別のカスタムフィールド値を元に表示非表示を変更したい #149

Closed HirokiNakatani closed 4 days ago

HirokiNakatani commented 2 weeks ago

ファイル形式でのカスタムフィールドに対し、別のカスタムフィールド値を元に表示非表示を変更するサンプルコードはありますか。 表示時はファイル添付を必須とし、非表示時は任意に戻したいです。

onozaty commented 2 weeks ago

下記を参考にしていただければと思います。

もしファイル形式だとうまく行かない場合には、ブラウザのDevToolsなどで該当箇所のIDがどうなっているかご確認いただけますでしょうか。

HirokiNakatani commented 1 week ago

うまく実装できておりません。

{8794277E-8012-411B-8014-97B2E0A39042} ■ファイル添付前

<pre>
<p class="custom-field-filedroplistner" style=""><label for="issue_custom_field_values_251_blank"><span title="処置記録のPDFを添付
  非本番環境:起票済み処置記録
  本番環境 :完了済み処置記録" class="field-description">処置記録ファイル</span><span class="required"> *</span></label><input type="hidden" name="issue[custom_field_values][251][blank]" id="issue_custom_field_values_251_blank" value="" autocomplete="off">
<span class="attachments_form">
  <span class="attachments_fields">
  </span>
  <span class="add_attachment" style="">
    <input type="file" name="issue[custom_field_values][251][dummy][file]" class="file_selector custom-field-filedrop" onchange="addInputFiles(this);" data-max-number-of-files-message="このファイルはアップロードできません。同時にアップロードできるファイル数の上限(1)を超えています。" data-max-file-size="10485760" data-max-file-size-message="このファイルはアップロードできません。添付ファイルサイズの上限(10 MB)を超えています。" data-max-concurrent-uploads="2" data-upload-path="/uploads.js" data-param="issue[custom_field_values][251]" data-description="false" data-description-placeholder="説明 (任意)" required="required">
    (サイズの上限: 10 MB)
  </span>
</span>
</p>
</pre>

■ファイル添付後

<p class="custom-field-filedroplistner" style=""><label for="issue_custom_field_values_251_blank"><span title="処置記録のPDFを添付
  非本番環境:起票済み処置記録
  本番環境 :完了済み処置記録" class="field-description">処置記録ファイル</span><span class="required"> *</span></label><input type="hidden" name="issue[custom_field_values][251][blank]" id="issue_custom_field_values_251_blank" value="" autocomplete="off">
<span class="attachments_form">
  <span class="attachments_fields">
  <span id="attachments_1" class=""><input type="text" class="icon icon-attachment filename readonly" name="issue[custom_field_values][251][1][filename]" readonly="readonly"><input type="hidden" class="token" name="issue[custom_field_values][251][1][token]" value="18978.4fe02224192cbc574e98f77a9f9d5e42c0ec4ccf7ad39358b60bc3eb68efbc2e"><a href="/attachments/18978.js?attachment_id=1" class="icon-only icon-del remove-upload" style="display: inline-block;" data-remote="true" data-method="delete">&nbsp;</a></span></span>
  <span class="add_attachment" style="display: none;"><input type="file" name="issue[custom_field_values][251][dummy][file]" class="file_selector custom-field-filedrop" onchange="addInputFiles(this);" data-max-number-of-files-message="このファイルはアップロードできません。同時にアップロードできるファイル数の上限(1)を超えています。" data-max-file-size="10485760" data-max-file-size-message="このファイルはアップロードできません。添付ファイルサイズの上限(10 MB)を超えています。" data-max-concurrent-uploads="2" data-upload-path="/uploads.js" data-param="issue[custom_field_values][251]" data-description="false" data-description-placeholder="説明 (任意)" required="required">

    (サイズの上限: 10 MB)
  </span>
</span>

</p>

以下のソースで制御を試みており、表示/非表示自体はできましたが、表示&必須にした際に、チケット保存が出来なくなります。function toggleFileField(show)の必須処理に問題があるのではと考察しておりますが、どう修正すべきかわかっておりません。

■ソース

$(function() {
  if ($('#issue_project_id').val() !== '1251') {
    return; 
  }

  // カスタムフィールドのID定義
  const field250Id = '#issue_custom_field_values_250';         // 申請種別
  const issueTrackerId = '#issue_tracker_id';                  // トラッカーID
  const fileFieldId = '#issue_custom_field_values_251_blank';  // 処置記録ファイル

  // 制御対象のカスタムフィールドID
  const controlledFieldIds1 = ['#issue_custom_field_values_248', '#issue_custom_field_values_242'];  // オブジェクト、オブジェクト操作
  const controlledFieldIds2 = ['#issue_custom_field_values_249'];                                    // 手動実行
  const allControlledFieldIds = [...controlledFieldIds1, ...controlledFieldIds2];                    // すべての制御対象フィールドをまとめる

  // 必須マークのHTML
  const requiredMark = '<span class="required"> *</span>';

  // フィールドの表示/非表示を切り替える関数
  function toggleField(id, show) {
    const fieldInput = $(id);
    const fieldDisplay = $('div.cf_' + id.split('_').pop() + '.attribute');
    const labelElement = fieldInput.closest('.cf_' + id.split('_').pop()).prev('label');

    fieldInput.parent().toggle(show);
    fieldDisplay.toggle(show);
    labelElement.toggle(show);

    if (show) {
      fieldInput.attr('required', 'required');
      if (!labelElement.find('.required').length) {
        labelElement.append(requiredMark);
      }
    } else {
      fieldInput.removeAttr('required');
      labelElement.find('.required').remove();
    }
  }

  // 処置記録ファイルフィールドの表示/非表示を切り替える関数
  function toggleFileField(show) {
    const fileFieldContainer = $(fileFieldId).closest('p');
    const labelElement = fileFieldContainer.find('label');

    fileFieldContainer.toggle(show);

    if (show) {
      fileFieldContainer.find('input[type="file"]').attr('required', 'required');
      if (!labelElement.find('.required').length) {
        labelElement.append(requiredMark);
      }
    } else {
      fileFieldContainer.find('input[type="file"]').removeAttr('required');
      labelElement.find('.required').remove();
    }
  }

  // リストのオプションをフィルタリングする関数
  function filterOptions(fieldId, filterValue) {
    let field = $(fieldId);
    field.find('option').each(function() {
      let optionValue = $(this).val();
      $(this).toggle(optionValue.startsWith(filterValue));
    });
  }

  // トラッカー名称から先頭3文字を取得する関数
  function getTrackerPrefix() {
    const selectedOption = $(issueTrackerId + ' option:selected');
    const trackerName = selectedOption.text().trim();
    return trackerName.substring(0, 3);
  }

  // フィールドの表示状態を更新する関数
  function updateFieldVisibility() {
    const value250 = $(field250Id).val();  // 申請種別の値を取得
    const trackerPrefix = getTrackerPrefix();  // トラッカー名称の先頭3文字を取得

    // すべての制御対象フィールドを一旦非表示に
    allControlledFieldIds.forEach(id => toggleField(id, false));
    toggleFileField(false);

    // 申請種別に基づいてフィールドを表示
    switch (value250) {
      case 'オブジェクト変更':
        toggleField(controlledFieldIds1[0], true); // オブジェクトを表示
        toggleField(controlledFieldIds1[1], true); // オブジェクト操作を表示
        break;
      case '手動実行':
        toggleField(controlledFieldIds2[0], true); // 手動実行を表示 
        break;
      case '移送':
        toggleFileField(true); // 処置記録ファイルを表示 
        break;
    }

    // トラッカー名称の先頭3文字でリストをフィルタリング
    if (trackerPrefix) {
      filterOptions(controlledFieldIds1[0], trackerPrefix); // オブジェクトフィールド値をフィルタリング
      filterOptions(controlledFieldIds2[0], trackerPrefix); // 手動実行フィールド値をフィルタリング
    }
  }

  // イベントリスナーの設定
  $(field250Id).on('change', updateFieldVisibility);
  $(issueTrackerId).on('change', updateFieldVisibility);

  // ページ読み込み時に初期実行
  updateFieldVisibility();
});
HirokiNakatani commented 1 week ago

チケット保存時のブラウザエラーは以下の通りです。

new:1 An invalid form control with name='issue[custom_field_values][251][dummy][file]' is not focusable. <input type=​"file" name=​"issue[custom_field_values]​[251]​[dummy]​[file]​" class=​"file_selector custom-field-filedrop" onchange=​"addInputFiles(this)​;​" data-max-number-of-files-message=​"このファイルはアップロードできません。同時にアップロードできるファイル数の上限(1)​を超えています。" data-max-file-size=​"10485760" data-max-file-size-message=​"このファイルはアップロードできません。添付ファイルサイズの上限(10 MB)​を超えています。" data-max-concurrent-uploads=​"2" data-upload-path=​"/​uploads.js" data-param=​"issue[custom_field_values]​[251]​" data-description=​"false" data-description-placeholder=​"説明 (任意)​" required=​"required">​

fileFieldContainer.find('input[type="file"]').attr('required', 'required'); 部分をやめて、フォーム送信時に入力チェックするロジックを追加することで回避はできそうですが、できれば他のフィールドと同じようなロジックで必須項目のチェックをさせたいです。

onozaty commented 1 week ago

必須とすると、サーバ側でもチェックされたはずなので、どちらにせよ必須の状態のまま送らないといったことはできないと思います。

HirokiNakatani commented 1 week ago

承知しました。 必須でないカスタムフィールドに対し、本プラグインで必須に変更し、Redmine側の必須制御ロジックに含めさせることは可能でしょうか。

HirokiNakatani commented 1 week ago

カスタムフィールドの設定画面で必須にすると、ステータス以外での表示非表示の制御が出来ないので、サーバーサイドは必須にせず、クライアントサイドで表示+必須制御を入れたいのですが、ファイル形式のカスタムフィールドのみうまく制御できておりません。

onozaty commented 1 week ago

カスタムフィールドのファイルですが、下記のような形で入力済みかどうか判断できそうでした。 14の部分は、カスタムフィールドのIDに応じて変えてください。

// 入力済みの場合
if ($("input[name^='issue[custom_field_values][14]'][name*='[token]']").length > 0) {
HirokiNakatani commented 1 week ago

ありがとうございます。 頂いた内容参考にしてますが、うまく処理できておりません。

onozaty commented 6 days ago

入力済みかどうかの判定がうまくいかない感じでしょうか?

HirokiNakatani commented 4 days ago

チケット作成画面ではうまくいって、その後のチケット編集画面ではうまくいかなさそうでした。別の方法で解決できました。ありがとうございます。