Summernote Editor - 将中继器字段添加到对话框

Summernote Editor - Add repeater field to dialog

我想在我的 summernote 对话框中使用转发器字段。我正在使用 jquery.repeater library.

在下面找到我的最小可行示例:

$(document).ready(function() {
  $('.summernote').summernote({
    height: 300,
    tabsize: 2,
    toolbar: [
      ['insert', ['synonym', 'codeview']]
    ],
  });
});

(function(factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define(['jquery'], factory);
  } else if (typeof module === 'object' && module.exports) {
    // Node/CommonJS
    module.exports = factory(require('jquery'));
  } else {
    // Browser globals
    factory(window.jQuery);
  }
}(function($) {

  $.extend($.summernote.plugins, {
    'synonym': function(context) {
      var self = this;
      var ui = $.summernote.ui;
      var $editor = context.layoutInfo.editor;
      var options = context.options;

      context.memo('button.synonym', function() {
        return ui.button({
          contents: '<i class="fa fa-snowflake-o">',
          tooltip: 'Create Synonym',
          click: context.createInvokeHandler('synonym.showDialog')
        }).render();
      });

      self.initialize = function() {
        var $container = options.dialogsInBody ? $(document.body) : $editor;

        var body = '<div class="form-group">' +
          'IF(<input type="text" name="text-input" value="A"/>) <input type="text" name="text-input" value="A"/>' +
          '<div data-repeater-list="group-a">' +
          '<div data-repeater-item>' +
          '<select name="select-input">' +
          '<option value="A" selected>ELSE</option>' +
          '<option value="B">ELSEIF</option>' +
          '</select>' +
          '(<input type="text" name="text-input" value="A"/>)' +
          '<input type="text" name="text-input" value="A"/>' +
          '<input data-repeater-delete type="button" value="Delete"/>' +
          '</div>' +
          '</div>' +
          '<input data-repeater-create type="button" value="Add"/>' +
          '<br/>' +
          '@ENDIF'
        var footer = '<button href="#" class="btn btn-primary ext-ifElse-btn">OK</button>';

        self.$dialog = ui.dialog({
          title: 'minimal dialog title',
          fade: options.dialogsFade,
          body: body,
          footer: footer
        }).render().appendTo($container);
      };

      self.destroy = function() {
        self.$dialog.remove();
        self.$dialog = null;
      };

      self.showDialog = function() {
        self
          .openDialog()
          .then(function(data) {
            // [workaround] hide dialog before restore range for IE range focus
            ui.hideDialog(self.$dialog);
            context.invoke('editor.restoreRange');
            self.insertToEditor(data);
            // do something with dialogData
            console.log("dialog returned: ", data)
            // ...
          })
          .fail(function() {
            context.invoke('editor.restoreRange');
          });

      };

      self.openDialog = function() {
        return $.Deferred(function(deferred) {
          var $dialogBtn = self.$dialog.find('.ext-synonym-btn');
          var $elemInput = self.$dialog.find('#input-element')[0];
          var $synonymInput = self.$dialog.find('#input-synonym')[0];

          var selectedText = $.selection()
          $('#input-element').val(selectedText);
          console.log("show dialog: " + selectedText)

          ui.onDialogShown(self.$dialog, function() {
            context.triggerEvent('dialog.shown');

            $dialogBtn
              .click(function(event) {
                event.preventDefault();

                deferred.resolve({
                  element: $elemInput.value,
                  synonym: $synonymInput.value
                });
              });
          });

          ui.onDialogHidden(self.$dialog, function() {
            $dialogBtn.off('click');

            if (deferred.state() === 'pending') {
              deferred.reject();
            }
          });

          ui.showDialog(self.$dialog);
        });
      };

      this.insertToEditor = function(data) {
        console.log("synonym: " + data.synonym)

        var $elem = $('<synonym>', {
          words: data.synonym
        });;

        $elem.text(data.element)

        context.invoke('editor.insertNode', $elem[0]);
      };
    }
  });
}));
<link href="https://stackpath.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.10/summernote.css" />

<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.10/summernote.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.selection/1.0.1/jquery.selection.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.repeater/1.2.1/jquery.repeater.min.js"></script>

<script type="text/javascript">
  $(document).ready(function() {
    // repeater field
    $(document).ready(function() {

      $('.repeater').repeater({
        defaultValues: {
          'textarea-input': 'foo',
          'text-input': 'bar',
          'select-input': 'B'
        },
        show: function() {
          $(this).slideDown();
        },
        hide: function(deleteElement) {
          /*
          if(confirm('Are you sure you want to delete this element?')) {
              $(this).slideUp(deleteElement);
          }
          */
        },
        ready: function(setIndexes) {

        }
      });

      window.outerRepeater = $('.outer-repeater').repeater({
        isFirstItemUndeletable: true,
        defaultValues: {
          'text-input': 'outer-default'
        },
        show: function() {
          console.log('outer show');
          $(this).slideDown();
        },
        hide: function(deleteElement) {
          console.log('outer delete');
          $(this).slideUp(deleteElement);
        },
        repeaters: [{
          isFirstItemUndeletable: true,
          selector: '.inner-repeater',
          defaultValues: {
            'inner-text-input': 'inner-default'
          },
          show: function() {
            console.log('inner show');
            $(this).slideDown();
          },
          hide: function(deleteElement) {
            console.log('inner delete');
            $(this).slideUp(deleteElement);
          }
        }]
      });
    });
  });
</script>
<div class="container">
  <div class="summernote">
    <p>Hello World!</p>
    This text should be replaced by the dialog. </div>
</div>

 <!-- START -->
      IF(<input type="text" name="text-input" value="A"/>) <input type="text" name="text-input" value="A"/>
      <div data-repeater-list="group-a">
        <div data-repeater-item>
            <select name="select-input">
            <option value="A" selected>ELSE</option>
            <option value="B">ELSEIF</option>
          </select> 
          (<input type="text" name="text-input" value="A"/>)
          <input type="text" name="text-input" value="A"/>
          <input data-repeater-delete type="button" value="Delete"/>
        </div>
      </div>
      <input data-repeater-create type="button" value="Add"/>
   <br>
   @ENDIF
   <!-- START -->

正如您在按下 snowflake 按钮时看到的,转发器字段不起作用。因此,当按下 Add 时,不会在对话框中添加更多字段。

对我做错了什么有什么建议吗?

感谢您的回复!

您必须在要重复的包装器中分配 repeater。测试下面的代码滚动到底部。

$(document).ready(function() {
  $('.summernote').summernote({
    height: 300,
    tabsize: 2,
    toolbar: [
      ['insert', ['synonym', 'codeview']]
    ],
  });
});

(function(factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define(['jquery'], factory);
  } else if (typeof module === 'object' && module.exports) {
    // Node/CommonJS
    module.exports = factory(require('jquery'));
  } else {
    // Browser globals
    factory(window.jQuery);
  }
}(function($) {

  $.extend($.summernote.plugins, {
    'synonym': function(context) {
      var self = this;
      var ui = $.summernote.ui;
      var $editor = context.layoutInfo.editor;
      var options = context.options;

      context.memo('button.synonym', function() {
        return ui.button({
          contents: '<i class="fa fa-snowflake-o">',
          tooltip: 'Create Synonym',
          click: context.createInvokeHandler('synonym.showDialog')
        }).render();
      });

      self.initialize = function() {
        var $container = options.dialogsInBody ? $(document.body) : $editor;

        var body = '<div class="form-group">' +
          'IF(<input type="text" name="text-input" value="A"/>) <input type="text" name="text-input" value="A"/>' +
          '<div data-repeater-list="group-a">' +
          '<div data-repeater-item>' +
          '<select name="select-input">' +
          '<option value="A" selected>ELSE</option>' +
          '<option value="B">ELSEIF</option>' +
          '</select>' +
          '(<input type="text" name="text-input" value="A"/>)' +
          '<input type="text" name="text-input" value="A"/>' +
          '<input data-repeater-delete type="button" value="Delete"/>' +
          '</div>' +
          '</div>' +
          '<input data-repeater-create type="button" value="Add"/>' +
          '<br/>' +
          '@ENDIF'
        var footer = '<button href="#" class="btn btn-primary ext-ifElse-btn">OK</button>';

        self.$dialog = ui.dialog({
          title: 'minimal dialog title',
          fade: options.dialogsFade,
          body: body,
          footer: footer
        }).render().appendTo($container);
      };

      self.destroy = function() {
        self.$dialog.remove();
        self.$dialog = null;
      };

      self.showDialog = function() {
        self
          .openDialog()
          .then(function(data) {
            // [workaround] hide dialog before restore range for IE range focus
            ui.hideDialog(self.$dialog);
            context.invoke('editor.restoreRange');
            self.insertToEditor(data);
            // do something with dialogData
            console.log("dialog returned: ", data)
            // ...
          })
          .fail(function() {
            context.invoke('editor.restoreRange');
          });

      };

      self.openDialog = function() {
        return $.Deferred(function(deferred) {
          var $dialogBtn = self.$dialog.find('.ext-synonym-btn');
          var $elemInput = self.$dialog.find('#input-element')[0];
          var $synonymInput = self.$dialog.find('#input-synonym')[0];

          var selectedText = $.selection()
          $('#input-element').val(selectedText);
          console.log("show dialog: " + selectedText)

          ui.onDialogShown(self.$dialog, function() {
            context.triggerEvent('dialog.shown');

            $dialogBtn
              .click(function(event) {
                event.preventDefault();

                deferred.resolve({
                  element: $elemInput.value,
                  synonym: $synonymInput.value
                });
              });
          });

          ui.onDialogHidden(self.$dialog, function() {
            $dialogBtn.off('click');

            if (deferred.state() === 'pending') {
              deferred.reject();
            }
          });

          ui.showDialog(self.$dialog);
        });
      };

      this.insertToEditor = function(data) {
        console.log("synonym: " + data.synonym)

        var $elem = $('<synonym>', {
          words: data.synonym
        });;

        $elem.text(data.element)

        context.invoke('editor.insertNode', $elem[0]);
      };
    }
  });
}));
<link href="https://stackpath.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.10/summernote.css" />

<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.10/summernote.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.selection/1.0.1/jquery.selection.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.repeater/1.2.1/jquery.repeater.min.js"></script>


<script type="text/javascript">
  $(document).ready(function() {
    // repeater field
    $(document).ready(function() {

      $('.repeater').repeater({
        defaultValues: {
          'textarea-input': 'foo',
          'text-input': 'bar',
          'select-input': 'B'
        },
        show: function() {
          $(this).slideDown();
        },
        hide: function(deleteElement) {
          /*
          if(confirm('Are you sure you want to delete this element?')) {
              $(this).slideUp(deleteElement);
          }
          */
        },
        ready: function(setIndexes) {

        }
      });

      window.outerRepeater = $('.outer-repeater').repeater({
        isFirstItemUndeletable: true,
        defaultValues: {
          'text-input': 'outer-default'
        },
        show: function() {
          console.log('outer show');
          $(this).slideDown();
        },
        hide: function(deleteElement) {
          console.log('outer delete');
          $(this).slideUp(deleteElement);
        },
        repeaters: [{
          isFirstItemUndeletable: true,
          selector: '.inner-repeater',
          defaultValues: {
            'inner-text-input': 'inner-default'
          },
          show: function() {
            console.log('inner show');
            $(this).slideDown();
          },
          hide: function(deleteElement) {
            console.log('inner delete');
            $(this).slideUp(deleteElement);
          }
        }]
      });
    });
  });
</script>

<div class="container">
  <div class="summernote">
    <p>Hello World!</p>
    This text should be replaced by the dialog. </div>
</div>
<div class="repeater">
  <!-- START -->
  IF(<input type="text" name="text-input" value="A" />) <input type="text" name="text-input" value="A" />
  <div data-repeater-list="group-a">
    <div data-repeater-item>
      <select name="select-input">
        <option value="A" selected>ELSE</option>
        <option value="B">ELSEIF</option>
      </select>
      (<input type="text" name="text-input" value="A" />)
      <input type="text" name="text-input" value="A" />
      <input data-repeater-delete type="button" value="Delete" />
    </div>
  </div>
  <input data-repeater-create type="button" value="Add" />
  <br> @ENDIF
</div>
<!-- START -->