How to use jQuery Tag-it plugin with a Symfony form

This is Part 4 in a series on setting up Tags in a ManyToMany association with an Entity in Symfony. You can read the previous three articles here:

  1. Managing Tags in ManyToMany association with Symfony
  2. Setting up Form Collection for Tags in Symfony
  3. Using a Data Transformer in Symfony to handle duplicate tags

By now, you should have your Product and Tag entities set up, along with your forms, and a data transformer for preventing duplicate tags from being added to the database. At the end of the previous article, we also created a controller which renders the submitProduct.html.twig template that we’ll be using for displaying our form to the world.

As mentioned in the previous article, we are going to need to add some JavaScript to add/remove tag.name  inputs for our form. There are multiple ways you can do this, and the Symfony documention on Allowing “new” Tags with the “Prototype” shows you a good example.

In this article, we will be using the jQuery Tag-it plugin, which allows you to have a tag input box on your page which works something like this:

    You can read Tag-it’s documentation for more information on it. For this tutorial, go ahead and download/unzip Tag-it, and copy these files:

    css/jquery.tagit.css
    css/tagit.ui-zendesk.css
    js/tag-it.min.js

    Into your Symfony web assets folder:

    web/css/jquery.tagit.css
    web/css/tagit.ui-zendesk.css
    web/js/tag-it.min.js

    Make sure you also have jQuery and jQuery-ui included in your template, as they are required by Tag-it.

    Ok, now how to set up this plugin with our Symfony form?

    Remember in the Symfony example using prototype, that you are replacing __name__ with the index of the next item in the array:

    product[tags][__name__][name]

    Using a find replace on __name__ you can create new inputs for your form:

    The way that Tag-it works is to add a hidden form input for each tag. It has a fieldName option which allows you to set the name you want to use for your inputs. You might think of doing something like this there:

    That will actually work to add your tags, but will not play nicely when you later remove a tag from the middle of a group of tags associated with a Product, and add another to it for example. The index of the item in the array needs to be set in the field name, so that it knows exactly which item to remove/add.

    So how to increment the field name’s index in Tag-it?

    In the Tag-it documentation, you will notice there is a beforeTagAdded event. We can use this to increment our index, and reset the Tag-it fieldName value each time a new tag is added. Here is a basic example:

    Test that by typing 3 new tags into your Tag-it input box: car, Japanese, import

    And you’ll see that Tag-it adds the hidden inputs to the form just we as we want them:

    If you prefer not to hand code the field name value in, but want to use Symfony’s form prototype instead, you could do something like this:

    Notice the {% do form.tags.setRendered %} added in the first line there. You will need this if you are using Symfony’s {{ form_end(form) }} to end your form. The reason is that form_end will output anything it thinks is missing from your form. It will believe that 'tags' are missing since we are actually creating the tag inputs ourselves. So we tell it that form.tags  are already rendered to prevent that.

    Another option would be to just close the form yourself:  </form> . But don’t forget to render the CSRF token (or anything else your form requires) in that case.

    The final result of your form might look something like this (using Bootstrap here):

    If you want to edit the tags of an existing Product, you could pass that Product to the view in your controller, and then output all of its tags inside the unordered list like so:

    The final result is a setup which meets the requirements outlined in the first article of this tutorial.

    I hope that this has given you some ideas on ways you can manage tags for your entities in a ManyToMany association in Symfony. If you have any questions or suggestions, please leave them in the comments!