3

What is the best course of action to allow the creation/deletion of matrix fields in front-end. For context this in a Commerce Order Field. JS is mess right now, any underlying Craft JS libraries I can tap into?

Here is the whole mess below, having issues with users getting stuck here if they accidentally leave of the matrix fields empty.

{#
 # 2016 Apply-3
 # ---------------
 #}

{% extends '_layouts/_event-checkout' %}

{% block content_inside %}
<section>
    <div class="col-md-8 col-md-offset-2 col-sm-10 col-sm-offset-1">
        <div class="feature boxed bg-secondary">
        <form method="post">
      <input type="hidden" name="action" value="commerce/cart/updateCart">
      <input type="hidden" name="redirect" value="apply/review"/>
            <input type="hidden" name="paymentMethodId" value="2">
            <input type="hidden" name="fields[applicationProgress]" value="2">
            <input type="hidden" name="fields[applicationSchemaVersion]" value="{{ switchboard.applicationSchemaVersion }}">

      <div class="text-center">
            <h4 class="uppercase mt48 mt-xs-0">Great, Now we know you {{ application.shippingAddress.firstName }}!</h4>
            <p class="lead mb64 mb-xs-24">
                Now let's see what you know!
            </p>
      </div>

      {{ getCsrfInput() }}

            <hr class="mt16">
            <div class="row mt64">
                <div class="col-sm-12 input-with-label">
                    <span>Tell me something that's true, that almost nobody agrees with you on.</span>
                    <textarea name="fields[thielQuestion]"
                        placeholder="Some may recognize this famous question, we want some original answers though, really think about this one.">{{ application.thielQuestion }}</textarea>
                </div>
                <div class="col-sm-12 mt48 input-with-label">
                    <span>What are YOU passionate about?</span>
                    <textarea name="fields[whatAreYouPassionateAbout]"
                        placeholder="What are YOU passionate about?">{{ application.whatAreYouPassionateAbout }}</textarea>
                </div>
                <div class="col-sm-12 mt48 input-with-label">
                    <span>What songs are included on the soundtrack to your life?</span>
                    <textarea name="fields[lifeSoundtrack]"
                        placeholder="One of the most important question on this application.">{{ application.lifeSoundtrack }}</textarea>
                </div>
            </div>

            <hr class="mt16">
      <div class="row mt16">
        <div class="skills col-sm-12 col-md-7 col-lg-9">
          <div class="skills__list twelve columns">
            <h3 class="skills__list-title">Add some skills by clicking the options on the right!</h3>
                        {% for bloc in application.skills %}
            <div class="skills input-with-label text-left">
              {% if bloc.type == "programmingLanguage" %}
                <p>+ Programming Language</p>
                                <input type="hidden" name="fields[skills][new{{ loop.index }}][type]" value="{{ bloc.type }}">
                                <span>Language Name:</span>
                <input type="text" name="fields[skills][new{{ loop.index }}][fields][languageName]" value="{{ bloc.languageName }}" placeholder="Language Name">
                                <span>Years Of Proficiency:</span>
                <input type="text" name="fields[skills][new{{ loop.index }}][fields][yearsOfProficiency]" value="{{ bloc.yearsOfProficiency }}" placeholder="Years Of Proficiency">
              {% elseif bloc.type == "programmingFramework"%}
                <p>+ Programming Framework</p>
                                <input type='hidden' name='fields[skills][new{{ loop.index }}][type]' value="{{ bloc.type }}">
                <span>Framework Name:</span>
                <input type="text" name="fields[skills][new{{ loop.index }}][fields][frameworkName]" value="{{ bloc.frameworkName }}" placeholder="Framework Name">
                <span>Coolest Thing You Have Built:</span>
                <textarea name="fields[skills][new{{ loop.index }}][fields][coolestThing]" placeholder="What is the coolest thing you have built with it?">{{ bloc.coolestThing }}</textarea>
                            {% elseif bloc.type == "knowledge"%}
                <p>+ Knowledge</p>
                                <input type='hidden' name='fields[skills][new{{ loop.index }}][type]' value="{{ bloc.type }}">
                                <span>Programmers alone can&apos;t solve these great challenges we need people like you with that special know-how, maybe it&apos;s in environmental sciences, maybe it&apos;s in Politics? Whatever you know something extra about:</span>
                <textarea name="fields[skills][new{{ loop.index }}][fields][knowledge]" placeholder="Programmers alone can&apos;t solve these great challenges we need people like you with that special know-how, maybe it&apos;s in environmental sciences, maybe it&apos;s in Politics? Whatever you know something extra about!">{{ bloc.knowledge }}</textarea>
                            {% elseif bloc.type == "expirience"%}
                <p>+ Expirience</p>
                                <input type='hidden' name='fields[skills][new{{ loop.index }}][type]' value="{{ bloc.type }}">
                                <span>Worked at one of those cool tech companies, or maybe been involved in hackathons in past?</span>
                <textarea name="fields[skills][new{{ loop.index }}][fields][expirience]" placeholder="Worked at one of those cool tech companies, or maybe been involved in hackathons in past?">{{ bloc.knowledge }}</textarea>

                            {% endif %}
            </div>
                        <hr>
            {% endfor %}
          </div>
        </div>
        <div class="col-sm-12 col-md-5 col-lg-3">

          <div class="skills-buttons">
            <a href="#" class="btn btn-sm skills__button" data-type="programmingLanguage">+ Programming Language</a>
            <a href="#" class="btn btn-sm skills__button" data-type="programmingFramework">+ Programming Framework</a>
            <a href="#" class="btn btn-sm skills__button" data-type="knowledge">+ Knowledge</a>
            <a href="#" class="btn btn-sm skills__button" data-type="experience">+ Experience</a>
            <!-- <a href="#" class="btn skills__button" data-type="proveYourAwesomness">+ Prove Your Awesomness</a>
            <a href="#" class="btn skills__button" data-type="showYourThinkingProwess">+ Show Your Thinking Prowess</a> -->
          </div>

        </div>
      </div>

      <input type="submit" class="button button-primary" value="Continue">
        </form>
        </div>
    </div>
</section>
{% includejs %}
var $skills = $('.skills__button');

{% if cart.skills %}
    var $iter = {{ cart.skills|length }};
{% else %}
    var $iter = 0;
{% endif %}

$skills.on('click', function() {
  var $type = $(this).attr("data-type");
  var $humanType = $(this).text();
  $iter++;

  console.log('Adding Block');
  if($type == "programmingLanguage") {
    $('.skills__list').append("\
    <div class='skills'> \
      <p>"+$humanType+"</p> \
      <input type='hidden' name='fields[skills][new"+$iter+"][type]' value='"+$type+"'> \
      <input type='text' name='fields[skills][new"+$iter+"][fields][languageName]' placeholder='Language Name'> \
      <input type='text' name='fields[skills][new"+$iter+"][fields][yearsOfProficiency]' placeholder='Years Of Proficiency'> \
    </div>");
  }else if($type == "programmingFramework") {
    $('.skills__list').append("\
    <div class='skills'> \
      <p>"+$humanType+"</p> \
      <input type='hidden' name='fields[skills][new"+$iter+"][type]' value='"+$type+"'> \
      <input type='text' name='fields[skills][new"+$iter+"][fields][frameworkName]' placeholder='Framework Name'> \
      <textarea name='fields[skills][new"+$iter+"][fields][coolestThing]' placeholder='What is the coolest thing you have built with it?'></textarea> \
    </div>");
  }else if($type == "knowledge") {
    $('.skills__list').append("\
    <div class='skills'> \
      <p>"+$humanType+"</p> \
      <input type='hidden' name='fields[skills][new"+$iter+"][type]' value='"+$type+"'> \
      <textarea name='fields[skills][new"+$iter+"][fields][knowledge]' placeholder='Programmers alone can&apos;t solve these great challenges we need people like you with that special know-how, maybe it&apos;s in environmental sciences, maybe it&apos;s in Politics? Whatever you know something extra about!'></textarea> \
    </div>");
  }else if($type == "expirience") {
    $('.skills__list').append("\
    <div class='skills'> \
      <p>"+$humanType+"</p> \
      <input type='hidden' name='fields[skills][new"+$iter+"][type]' value='"+$type+"'> \
      <textarea name='fields[skills][new"+$iter+"][fields][expirience]' placeholder='Worked at one of those cool tech companies, or maybe been involved in hackathons in past?'></textarea> \
    </div>");
  }
});

{% endincludejs %}
{% endblock %}

1 Answer 1

6

I'll be flat-out honest; I didn't read through your code. Secondly, I think to set expectations when you say a matrix field on the front-end you'll need to understand it isn't going to look like what you think it looks like within the CP.

With that said, on a site I am working on, I have a page where a user can update their profile on the front-end. There is a section they can add multiple contact info - which links to a matrix filed in the CP.

twig

<input type="hidden" name="fields[contactInfo]">

<h3><label for="contactInfo">Contact Info</label></h3>
{% for block in currentUser.contactInfo.type('contact') %}
    <div class="matrixblock">
        <input type="hidden" name="fields[contactInfo][{{ block.id }}][type]" value="contact">
        <input type="hidden" name="fields[contactInfo][{{ block.id }}][enabled]" value="1">

        <input type="text" name="fields[contactInfo][{{ block.id }}][fields][firstName]" value="{{ block.firstName }}">
        <input type="text" name="fields[contactInfo][{{ block.id }}][fields][lastName]" value="{{ block.lastName }}">
        <input type="tel" name="fields[contactInfo][{{ block.id }}][fields][phoneNumber]" value="{{ block.phoneNumber }}">
        <input type="email" name="fields[contactInfo][{{ block.id }}][fields][email]" value="{{ block.email }}">
        <span><a href="javascript:void(0);" class="remove-contact"><i class="fa fa-times"></i></a></span>
    </div>
{% endfor %}
<button type="button" id="addPerson">Add Person</button>

The above is what defines what I'll call a row in your matrix field. It will pull in and render any existing matrix blocks that have been created. You'll need to update the type to whatever you have called it in your project.

To add a new "row" I used this thread and this thread to get me in the right direction. I highly recommend reading them. I think I read them both 20+ times.

Where I know my code falls short is in the javascript. I am currently hard-coding the html. Ideally I'd like to have it clone the existing markup so I don't have to update it multiple places in the event it changes.

jquery

/**
 * Event listener to add additional primary guardian(s).
 *
 * @param {object} event | The click event.
 *
 */
var blocknum = 1;

$('#addPerson').click(function (event) {
    var markup = '<div class="matrixblock"><input type="hidden" name="fields[contactInfo][new' + blocknum + '][type]" value="contact"><input type="hidden" name="fields[contactInfo][new' + blocknum + '][enabled]" value="1"><input type="text" name="fields[contactInfo][new' + blocknum + '][fields][firstName]" value=""><input type="text" name="fields[contactInfo][new' + blocknum + '][fields][lastName]" value=""><input type="tel" name="fields[contactInfo][new' + blocknum + '][fields][phoneNumber]" value=""><input type="email" name="fields[contactInfo][new' + blocknum + '][fields][email]" value=""><span><a href="javascript:void(0);" class="remove-contact"><i class="fa fa-times"></i></a></span></div>';
    $('div.matrixblock:last').after(markup);
    blocknum++;
});

As you will notice, you will also need a way to delete the given row. This is my example, you will need to update the logic to match your current html structure.

jquery

/**
 * Event listener to removing additional primary guardian(s).
 *
 * @param {object} event | The click event.
 *
 */
$('body').on('click', '.remove-contact', function (event) {
    $(this).parent().parent().fadeOut('fast').remove();
    event.preventDefault();
});

Note: Because we are dynamically creating rows, we need to register/bind the click event in this manor so that all future clicks will be attached to this event.

I have not styled my form at all, but in the end, this is what the above markup renders:

enter image description here

(first name, last name, email, phone)

Hope this helps!

1
  • I was surprised there were not more posts addressing this question. Thank you so much for posting the solution. I was able to adapt it for my own uses. I am not savvy enough to clone the first div, but I still got it to work.
    – 4midori
    Jul 17, 2020 at 20:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.