Magento 2: Using placeholders in form fields

While the Magento 2 adoption of KnockoutJS has opened up the possibility for modern front-end features, the level of abstraction has raised new challenges. Adding a placeholder to form fields is no longer as simple as opening the template file for the form. Here I will explain the steps for adding placeholders to Magento 2 fields.

The HTML templates used by KnockoutJS for forms can be found mostly in the UI module at /vendor/magento/module-ui/view/frontend/web/templates/form/element. Copy whichever fields you need to your theme files at the path /app/code/design/frontend/Vendor_Name/Theme_Name/Magento_Ui/web/templates/form/element.

The label for most fields is attached to the data KnockoutJS passes to the field. In the template, change the placeholder attribute to use the label information.

<input class="input-text" type="text" data-bind="
    value: value,
    valueUpdate: 'keyup',
    hasFocus: focused,
    attr: {
        name: inputName,
        placeholder: label, <!-- placeholder: placeholder is now placeholder: label -->
        'aria-describedby': noticeId,
        id: uid,
        disabled: disabled
    }" />

Clear your static files in the command line using grunt refresh or in the admin at the cache management area. This should add the HTML templates to your static files and create any necessary symlinks. You may also need to do a hard refresh on the page to bypass the browser cache.

What about fields missing their placeholders?

You may have noticed that this solution does not work for all fields. Any fields not being generated from those input HTML templates will need to be tracked down and the process repeated. There are some fields, such as the street address fields, that are hard-coded in the core to not send a label. To get around this, we will need to use a plug-in to add the data.

If you open the file at /vendor/magento/module-checkout/Block/Checkout/AttributeMerger.php you will see on line 253 a function specifically made for the street address field. Since this is a protected function, we won’t be able to use it for our plug-in. Instead, we will be using the merge function at line 110 since it is the next public function we can tap into to make our changes.

For our plug-in file we will use the following code:

<?php
namespace <Vendor_Name>\ShippingAddress\Plugin\Checkout\Block\Checkout\AttributeMerger;

class Plugin
{
  public function afterMerge(\Magento\Checkout\Block\Checkout\AttributeMerger $subject, $result)
  {
    if (array_key_exists('street', $result)) {
      $result['street']['children'][0]['label'] = __('Street Address');
      $result['street']['children'][1]['label'] = __('Apt, suite, building');
    }

    return $result;
  }
}

This will hard-code a label for the street address and address line 2. Be sure to replace Vendor_Name with your own vendor name.

You can see the rest of the files for this plug-in on github.