yairEO / tagify

🔖 lightweight, efficient Tags input component in Vanilla JS / React / Angular / Vue
https://yaireo.github.io/tagify/
Other
3.55k stars 436 forks source link

Convert any "pending" text in the input to tags on form submit #1375

Closed jzohrab closed 2 months ago

jzohrab commented 2 months ago

Prerequisites

(sorry, I'm not sure how to make a demo that shows the issue)

I have a form that uses tagify with an ajaxed whitelist, with enforceWhitelist: false. For various reasons, I have to use addTagOnBlur: false (other iframes sometimes pull focus, resulting in tags being created prematurely if that was true).

Sometimes users will enter text in the tag input box, and then click away from it, or hit the "Save" button for the form. I'd like an remaining text in the input box to be converted to a full tag object before the form is submitted, but can't figure out how to do that.

I've added an "onsubmit" handler to the form:

  <form id="term-form" name="term_form" method="POST"
        onsubmit="return convert_pending_parent_tags(event);">

with the following method:

  function convert_pending_parent_tags(event) {
    const pending_tag = $('span.tagify__input').text().trim();
    if (pending_tag != '') {
      console.log(`adding: ${pending_tag}`);
      parents_tagify.addTags([pending_tag], true, false);
      console.log('supposed to be added');
    }
    else {
      console.log('no pending tag');
    }
    return true;
  }

however, it's not working as expected ... I can see the tag widget being changed in the UI, but the data isn't actually being submitted. I imagine there must be some sort of async method being used during the addTags call, but I can't figure out how to use that in the form submit.

Cheers and thank you for this great library!

jzohrab commented 2 months ago

I tried to get a bit fancier and use the add event, only calling submit() of the form once that event occurred:

  function convert_pending_parent_tags(event) {
    const pending_tag = $('span.tagify__input').text().trim();
    if (pending_tag != '') {
      console.log(`adding: ${pending_tag}`);

      parents_tagify.on('add', onFinalTagAdded);

      // Add the pending tag
      parents_tagify.addTags([pending_tag], true, false);

      // Prevent form submission while waiting for the tag to be added
      event.preventDefault();
      return false;
    }
    else {
      console.log('no pending tag');
    }
    return true;
  }

  function onFinalTagAdded(e) {
    console.log('Tag added:', e.detail.data.value);

    const f = $('#term-form')[0];
    f.submit();

    parents_tagify.off('add', onFinalTagAdded);
  }

The onFinalTagAdded method is getting called and the form is being submitted, but the POST payload still doesn't have the actual new tag data.

jzohrab commented 2 months ago

Adding a timeout made it work. Hacky, but ok.

  function onFinalTagAdded(e) {
    console.log('Tag added:', e.detail.data.value);

    setTimeout(function() {
      const form = $('#term-form');
      form[0].submit();
    }, 200);  // delay, ensure tag data is (hopefully) fully processed

    parents_tagify.off('add', onFinalTagAdded);
  }

If there's a cleaner/clearer way, LMK, cheers!

yairEO commented 2 months ago

You can delete all this and simply add a small timeout to form "submit" logic, so it only gathers the data it should send after 100ms for example. This should give Tagify enough time to finish I suppose.