GrapesJS / grapesjs

Free and Open source Web Builder Framework. Next generation tool for building templates without coding
https://grapesjs.com
BSD 3-Clause "New" or "Revised" License
22.36k stars 4.05k forks source link

[Question] how to append a button inside default modal and catch it's click event #2129

Closed nikitha12 closed 5 years ago

nikitha12 commented 5 years ago

I want a form inside a modal (using the default modal ), on click of the submit button make an ajax call. I have created a new component which pops a modal on drag and drop.

I am able to insert the form in the modal

It looks like this :

Screen Shot 2019-07-11 at 10 22 17 AM

but I don't know how to track the submit button click. I tried writing OnSubmit() in a Script and added to the modal content, its not working.

`const domc = editor.DomComponents;
  const defaultType = domc.getType('default');
domc.addType("test-type", {
    model: defaultModel.extend(
      {
        defaults: Object.assign({}, defaultModel.prototype.defaults, {
          type      : "test-type",
          id:"popup",
          droppable : false,
          resizable : true,
          tagName:"popup",
        }),
        getAttrToHTML: function() {
          var attr = defaultType.model.prototype.getAttrToHTML.apply(this, arguments);
          delete attr.onmousedown;
          var testmarker = this.get("testmarker");
          if(testmarker)
            attr.testmarker = testmarker;
          return attr;
        }
      }),
    view: defaultType.view.extend({
      tagName: "button",
      events: {
        "dblclick": "openModal",
      },
      initialize: function(o) {
        defaultType.view.prototype.initialize.apply(this, arguments);
        this.listenTo(this.model, "change:testmarker", this.updateMarker);
        this.listenTo(this.model, "dblclick active", this.openModal);
      },

      updateMarker: function() {
        var testmarker = this.model.get("testmarker");
        this.$el.attr("testmarker", testmarker);
      },

      openModal: function(e) {
        const modal = editor.Modal;
        modal.open({
          title: '<br>Create Identity<br>',
          content: `
          <div class="container"> 
          <br>
              <form onsubmit="formSubmit()">
              <div class="form-group">
              <label>URL</label>
               <input type="text" class="form-control" id="url" placeholder="http://test-data/" name="url">
              </div>
              <br>
              <div class="form-group">
              <label>Identity </label>
               <input type="text" class="form-control" id="address" placeholder="Enter Identity Address" name="address">
              </div>
              <br>
              <button type="submit" class="btn btn-danger">Submit</button>
              </form>
              <br>
              </div>`,
        });
      },
    })
  });`

Is my approach correct? Should I do it in a different way ?

giorgiosjames commented 5 years ago

Doing away with the form and just using javascript to create/interface with the modal contents works well. modal.setContent() for appending the button and myButton.onclick = () => {} to catch its click event. Here's some example code:

function openModal() {
  const pfx = editor.getConfig().stylePrefix;
  const modal = editor.Modal;

  const container = document.createElement('div');

  const inputHtml = `<div class="form-group">
  <label>URL</label>
   <input type="text" class="form-control" placeholder="http://test-data/" name="url" id="urlInput">
  </div>
  <br>
  <div class="form-group">
  <label>Identity </label>
   <input type="text" class="form-control" placeholder="Enter Identity Address" name="address" id="idInput">
  </div>`;

  const btnEdit = document.createElement('button');
  btnEdit.innerHTML = 'Submit';
  btnEdit.className = pfx + 'btn-prim ' + pfx + 'btn-import';
  btnEdit.onclick = function() {
    const urlInputElement = document.getElementById('urlInput');
    const idInputElement = document.getElementById('idInput');

    const urlVal = urlInputElement.value;
    const idVal = idInputElement.value;

    // here is where you put your ajax logic
    myAjaxCallFunction(urlVal, idVal);

    modal.close();
  };

  modal.setTitle('Create Identity');
  container.innerHTML = inputHtml;
  container.appendChild(btnEdit);
  modal.setContent(container);
  modal.open();
};
nikitha12 commented 5 years ago

Thank you @giorgiosjames . I have one more doubt. To style the default modal I am using bootstrap. I tried adding the stylesheet to the innerHTML of container div but its changing the style of all the blocks.

Screen Shot 2019-07-12 at 10 53 31 AM

Should I use selector for adding and removing style to the Modal?

nikitha12 commented 5 years ago

Found the solution. Thank you

artf commented 5 years ago

The point here is that content accepts HTML nodes (not only HTML strings) so you can have any HTMLElement with any attached event (forms with submits, buttons with clicks, etc.) so the approach proposed is totally valid (thanks @giorgiosjames)

AkibDeraiya123 commented 4 years ago

@giorgiosjames Thanks, man this(https://github.com/artf/grapesjs/issues/2129#issuecomment-510734779) is work like a charm in my case.

Your answer save a lot of my time :)

Drishya12345 commented 4 years ago

Thankyou @giorgiosjames .Code really works good in my usecase.

niveth09 commented 3 years ago

Doing away with the form and just using javascript to create/interface with the modal contents works well. modal.setContent() for appending the button and myButton.onclick = () => {} to catch its click event. Here's some example code:

function openModal() {
  const pfx = editor.getConfig().stylePrefix;
  const modal = editor.Modal;

  const container = document.createElement('div');

  const inputHtml = `<div class="form-group">
  <label>URL</label>
   <input type="text" class="form-control" placeholder="http://test-data/" name="url" id="urlInput">
  </div>
  <br>
  <div class="form-group">
  <label>Identity </label>
   <input type="text" class="form-control" placeholder="Enter Identity Address" name="address" id="idInput">
  </div>`;

  const btnEdit = document.createElement('button');
  btnEdit.innerHTML = 'Submit';
  btnEdit.className = pfx + 'btn-prim ' + pfx + 'btn-import';
  btnEdit.onclick = function() {
    const urlInputElement = document.getElementById('urlInput');
    const idInputElement = document.getElementById('idInput');

    const urlVal = urlInputElement.value;
    const idVal = idInputElement.value;

    // here is where you put your ajax logic
    myAjaxCallFunction(urlVal, idVal);

    modal.close();
  };

  modal.setTitle('Create Identity');
  container.innerHTML = inputHtml;
  container.appendChild(btnEdit);
  modal.setContent(container);
  modal.open();
};

I tried this code in angular. It set content as [object HTMLDivElement] . Can someone help me?

giorgiosjames commented 3 years ago

Hey @niveth09,

If I had to guess, I'd say you may be accidently assigning an element object to container.innerHTML rather than a string, as in the example. I would recommend changing your implementation to ensure the inputHtml variable is a string representing valid HTML or you can use container.appendChild(inputHtml) like we do with the button if it's already an element object in your code.

You may also want to use Angular/JQuery API to interact with the DOM instead, depending on your implementation. Something like in this fiddle.

If the above doesn't answer your question, I would recommend posting a question thread on Stack Overflow with a reproducible example and Angular+GrapesJS tags as you're unlikely to get further support on this closed Github Issue.