Basic Usage – Reusing Simple Component Field

Scenario

You are developing a custom wp theme, and you are using acf pro to allow your client to easily edit various content on the site. However, this theme has a special requirement, that is, any button can be either a) Internal page link selected from a dropdown, b) Internal page link, but a input field so the client is allow to append query string to the url, c) External link to other site, but might or might no open in a new tab. And the buttons are all over the place, in the header, sidebar, content, footer, call out area…


Normal solution

If you were to do this with plain ACF Pro plugin, you’d probably be doing something similar to this:

For every button, might be in the same field group or different ones, might be inside a repeater or a flexible content block, you’d create the following fields for every single button instance:

  1. a select field for the client to choose the button type, either “Internal Pages” or “Custom”.
  2. a input box toe hold the text in the button.
  3. a conditional page link field, only visible when the button type is “internal Pages”, for the client to select a page link.
  4. a conditional input field, only visible when the button type is “Custom”, for client to enter any URL link.
  5. a conditional true/false field, only visible when the button type is “Custom”, for client to decide if the button should be opened in new tab or not.

You’d create something like this for one button:
example-1_1

However, some template has buttons on different places, so you’d probably start giving prefixes to those fields, something along the line of “sidebar_button_type”, “sidebar_button_text”… and “callout_button_type”, “callout_button_text”…

Even more, some of the buttons could be optional, such as, the client might not always need a button in the sidebar. Then you’d probably start creating new conditional field to every single button’s fields.

And the next thing you knew, now the client wanted to have the freedom to control the color as well as the text color of the button. As you can see, you’d be repeatedly adding new fields to every single button instance you have, and the pain could escalate really easily.


Using the ACF Component Field Plugin

Now we know what the problem is, but how can ACF Component Field plugin solves this issue?

  1. First, install ACF Pro and ACF Component Field Plugin and activate them.
  2. Yes, we will still have to create the button fields, “button_type”, “button_text”, “button_style”, “button_***”… But this time, we are creating the basic foundation of the button.
  3. Before we save the field group, we check the checkbox “Used as ACF Component Field” to tell ACF that we want this field group to be treated as a “component”, meaning, we will be reusing this sets of fields in many places.
    example-1_2
  4. After we saved the “Button Component” group, it will be appearing under the “component” on ACF field groups page.
    example-1_3
  5. Now, we can go ahead and create our normal field group. Whenever we want to use a button, we just need to select the field type “component field”, and choose the component group we want to use. In our case, it’s the “Button Component” group.
    example-1_4
  6. Like in the scenario, the buttons can be many different places. Sure, we just need to create those, and use the “Button Component”.
    example-1_5
  7. We can also use whatever provided by acf, such as, layouts, column width, column classes… to make your back-end fields layout prettier and easier for the client to edit.
  8. If we go to the page you assign this field group, the button’s fields are now pulled, and nicely displayed on the screen:
    example-1_6
  9. Isn’t the beautiful? Now, if the client wants a new button color to choose from, or any crazy requests they want, you only need to go back to your “Button Component” field group, and add whatever is needed. In our case, a new color select field “button_style”. It’ll now be available on everywhere this component is used.
    example-1_7

Outputting in the template files

As the plugin described on the homepage, this plugin is using the same logic as a repeater field. So we don’t have to learn any new functions or new syntax to putout our data. We simply treat it just like a repeater field.

<div class="site-content">
    <?php while( have_rows('top_button') ) : the_row(); ?>
     
        <?php 
            $button_link = get_sub_field('button_type') == 'internal'  
                ? get_sub_field('page_link')  
                : get_sub_field('custom_link');  
            $button_external = get_sub_field('new_tab')? ' target="_blank"' : '';
        ?>

        <a href="<?php echo $button_link;?>" class="button <?php the_sub_field('button_style'); ?>" <?php echo $button_txternal; ?>>
            <?php the_sub_field('button_text'); ?>
        </a>

    <?php endwhile; ?>
</div>

However, since we have this concept of component, this button component is going to be used everywhere. For better practice, we can extract the output into a function, and call that function whenever we have a button.

function my_button_component() {
    $button_text = get_sub_field('button_text');  
    $button_class = get_sub_field('button_style');  
    $button_link = get_sub_field('button_type') == 'internal'  
        ? get_sub_field('page_link')  
        : get_sub_field('custom_link');  
    $button_external = get_sub_field('new_tab')? ' target="_blank"' : '';
    
    return sprintf( '<a href="%s" class="button %s" %s>%s</a>',
        $button_link, $button_class, $button_external, $button_text );
}
<div class="site-content">
    <?php while( have_rows('top_button') ) : the_row(); ?>
         <?php echo my_button_component(); ?>
    <?php endwhile; ?>

    <h1><?php the_title(); ?>
    <?php the_content(); ?>

    <?php while( have_rows('after_content_botton') ) : the_row(); ?>
         <?php echo my_button_component(); ?>
    <?php endwhile; ?>
</div>

<aside class="site-sidebar">    
    <?php while( have_rows('sidebar_botton') ) : the_row(); ?>
         <?php echo my_button_component(); ?>
    <?php endwhile; ?>
</aside>

Further more, if you think the while loop is bothering you, (this is purely person preference), you can even move the while loop inside you functions. One thing to notice, if we were to move the while loop inside the function, we need to take account for getting the field values from a acf option page, taxonomy term, or user. So we need to make a little condition check before we output the button, and call the have_rows dynamically.

function my_button_component($field_name) {
    if (is_string($field_name)) {
        $field_name = array($field_name);
    }

    $output = '';

    while (call_user_func_array('have_rows', $field_name)) { 
        the_row();

        $button_text = get_sub_field('button_text');  
        $button_class = get_sub_field('button_style');  
        $button_link = get_sub_field('button_type') == 'internal'  
            ? get_sub_field('page_link')  
            : get_sub_field('custom_link');  
        $button_external = get_sub_field('new_tab')? ' target="_blank"' : '';

        $output .= sprintf( '<a href="%s" class="button %s" %s>%s</a>',
            $button_link, $button_class, $button_external, $button_text );
    }

    return $output;
}
<div class="site-content">
    <?php echo my_button_component('top_button'); ?>

    <h1><?php the_title(); ?>
    <?php the_content(); ?>

    <?php echo my_button_component('after_content_botton'); ?>
</div>

<aside class="site-sidebar">    
    <?php echo my_button_component('sidebar_botton'); ?>
</aside>

The above codes are just example on how you can output the component field’s data. You do whatever make sense for you or your team, there’s no absolute right or wrong.