1

The ultimate goal is to have a widget for Elementor in Wordpress that can create multiple buttons inside of tabs. So I want a repeater inside of a repeater.

I do not have all the functionality or styling yet, just the very basic set up that lists each input on the page. The code I have does not work. The plugin displays correctly, however all the elementor controls disappear.

Screenshot of Wordpress page

Here is the code:

<?php
namespace Elementor;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * Elementor iatabs widget.
 *
 * Elementor widget that displays vertical or horizontal iatabs with different
 * pieces of content.
 *
 * @since 1.0.0
 */
class Widget_AltTabs extends Widget_Base {

    	/**
	 * Get widget name.
	 *
	 * Retrieve iatabs widget name.
	 *
	 * @since 1.0.0
	 * @access public
	 *
	 * @return string Widget name.
	 */
	public function get_name() {
		return 'alttabs';
	}

	/**
	 * Get widget title.
	 *
	 * Retrieve tabs widget title.
	 *
	 * @since 1.0.0
	 * @access public
	 *
	 * @return string Widget title.
	 */
	public function get_title() {
		return __( 'AltTabs', 'elementor' );
	}

	/**
	 * Get widget icon.
	 *
	 * Retrieve tabs widget icon.
	 *
	 * @since 1.0.0
	 * @access public
	 *
	 * @return string Widget icon.
	 */
	public function get_icon() {
		return 'eicon-tabs';
	}

protected function _register_controls() {

$this->start_controls_section(
    'content_section',
    [
        'label' => __( 'Content', 'elementor' ),
        'tab' => Controls_Manager::TAB_CONTENT,
    ]
);

$this->add_control(
    'list',
    [
        'label' => __( 'Repeater List', 'elementor' ),
        'type' => Controls_Manager::REPEATER,
        'fields' => [
            [
                'name' => 'list_title',
                'label' => __( 'Title', 'elementor' ),
                'type' => Controls_Manager::TEXT,
                'default' => __( 'List Title' , 'elementor' ),
                'label_block' => true,
            ],
            [
                'name' => 'list_content',
                'label' => __( 'Content', 'elementor' ),
                'type' => Controls_Manager::WYSIWYG,
                'default' => __( 'List Content' , 'elementor' ),
                'show_label' => false,
            ],
            [
                'name' => 'list_buttons',
                'label' => __( 'List Buttons', 'elementor' ),
                'type' => Controls_Manager::REPEATER,
                'fields' => [
                    [
                        'name' => 'button_text',
                        'label' => __( 'Button Text', 'elementor' ),
                        'type' => Controls_Manager::TEXT,
                        'default' => __( 'Click' , 'elementor' ),
                        'label_block' => true,
                    ],
                ],
                'default' => [
                    [
                        'button_text' => __( 'Button #1', 'elementor' ),
                    ],
                    [
                        'button_text' => __( 'Button #2', 'elementor' ),
                    ],
                ],
            ],  
        ],
        'default' => [
            [
                'list_title' => __( 'Title #1', 'elementor' ),
                'list_content' => __( 'Item content. Click the edit button to change this text.', 'elementor' ),
            ],
            [
                'list_title' => __( 'Title #2', 'elementor' ),
                'list_content' => __( 'Item content. Click the edit button to change this text.', 'elementor' ),
            ],
        ],
        'title_field' => '{{{ list_title }}}',
    ]
);

$this->end_controls_section();

}

protected function render() {
    $settings = $this->get_settings_for_display();
    if ( $list ) {
        echo '<dl>';
        foreach (  $settings['list'] as $item ) {
            echo '<dt class="repeater-' . $item['_id'] . '">' . $item['list_title'] . '</dt>';
            echo '<dd>' . $item['list_content'] . '</dd>';
            foreach ($item['list_buttons'] as $button) {
                echo $button['button_text'];
            }
        }
        echo '</dl>';
    }
}

	/**
	 * Render tabs widget output in the editor.
	 *
	 * Written as a Backbone JavaScript template and used to generate the live preview.
	 *
	 * @since 1.0.0
	 * @access protected
	 */
    protected function _content_template() { ?>
        <# if ( settings.list ) { #>
            <dl>
            <# _.each( settings.list, function( item ) { #>
                <dt class="repeater-{{ item._id }}"> {{ item.list_title }} </dt>
                <dd> {{ item.list_content }} </dd>
                <dd> {{ item.list_buttons[0].button_text }} </dd>

                <# if ( item.list_buttons ) { #>
                    <dl>
                    <# _.each( item.list_buttons, function( i ) { #>
                        <dt class="repeater-{{ i._id }}"> </dt>
                        <dd> {{ i.button_text }} </dd>
                    <# }); #>
                    </dl>
                <# } #>

            <# }); #>
            </dl>
        <# } #>
        <?php
        }

    }
Plugin::instance()->widgets_manager->register_widget_type( new Widget_AltTabs ); ?>

Here is an example of the error messages that I am getting:

Uncaught TypeError: Cannot read property 'each' of undefined at Object. (editor.min.js?ver=2.0.15:2) at Function.each (load-scripts.php?c=0&load[]=underscore,shortcode,utils,jquery-core,jquery-migrate,backbone,wp-util,wp-backbone,media-models,moxiejs,plupload,wp-plupload,jqu&load[]=ery-ui-core,jquery-ui-widget,jquery-ui-mouse,jquery-ui-sortable&ver=4.9:10) at n.getStyleControls (editor.min.js?ver=2.0.15:2) at editor.min.js?ver=2.0.15:2 at Function.m.each.m.forEach (load-scripts.php?c=0&load[]=underscore,shortcode,utils,jquery-core,jquery-migrate,backbone,wp-util,wp-backbone,media-models,moxiejs,plupload,wp-plupload,jqu&load[]=ery-ui-core,jquery-ui-widget,jquery-ui-mouse,jquery-ui-sortable&ver=4.9:5) at e.Collection.each (load-scripts.php?c=0&load[]=underscore,shortcode,utils,jquery-core,jquery-migrate,backbone,wp-util,wp-backbone,media-models,moxiejs,plupload,wp-plupload,jqu&load[]=ery-ui-core,jquery-ui-widget,jquery-ui-mouse,jquery-ui-sortable&ver=4.9:18) at Object. (editor.min.js?ver=2.0.15:2) at Function.each (load-scripts.php?c=0&load[]=underscore,shortcode,utils,jquery-core,jquery-migrate,backbone,wp-util,wp-backbone,media-models,moxiejs,plupload,wp-plupload,jqu&load[]=ery-ui-core,jquery-ui-widget,jquery-ui-mouse,jquery-ui-sortable&ver=4.9:10) at n.getStyleControls (editor.min.js?ver=2.0.15:2) at n.renderStyles (editor.min.js?ver=2.0.15:2)<

I am pretty new to writing Elementor widget/plugins so I am stumped here. I would appreciate any help. Thank you!

4
  • As the error message statues the _ in your call _.each is undefined. So you're probably missing undersore.js or a similar library. Commented Jun 13, 2018 at 15:31
  • Thanks for the comment! Unfortunately that is not the issue because when I remove my inner repeater, everything works just fine. It is something when I nest the repeater that is going wrong. Commented Jun 13, 2018 at 17:20
  • It might be a scoping issue then, that inside the outer loop or some other block the _ is not defined. I once had similar issues with handlebarsjs Commented Jun 14, 2018 at 17:55
  • I ended up using SiteOrigin to create the widget since Elementor is able to use SiteOrigin widgets. I am familiar with those widgets, it went a lot smoother. Thanks to everyone who took a look at this! Commented Jun 19, 2018 at 16:02

2 Answers 2

4

Repeater controls cannot support a nested repeater

take look : https://github.com/pojome/elementor/issues/2955

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you! This is unfortunate, but I am glad I at least know I am not missing something. For anyone curious about how I solved this problem, I actually built a SiteOrigin Widget which I then used in Elementor. It isn't perfect, but my boss and the client were very pleased.
-1

My solution

Create two repeaters and bind them with keys. Good luck

protected function _register_controls() {
    $this->start_controls_section(
        'section_categories',
        [
            'label' => __( 'Categories', 'elementor' ),
        ]
    );

    $repeater = new Repeater();

    $repeater->add_control(
        'category_slug',
        [
            'label'       => __( 'Category Slug', 'elementor' ),
            'type'        => Controls_Manager::TEXT,
            'default'     => __( 'category-name', 'elementor' ),
            'label_block' => true,
        ]
    );

    $repeater->add_control(
        'category_title',
        [
            'label'       => __( 'Category Title', 'elementor' ),
            'type'        => Controls_Manager::TEXT,
            'default'     => __( 'Category Title', 'elementor' ),
            'label_block' => true,
        ]
    );

    $this->add_control(
        'categories',
        [
            'label'       => __( 'Contacts', 'elementor' ),
            'type'        => Controls_Manager::REPEATER,
            'fields'      => $repeater->get_controls(),
            'title_field' => '{{{ category_title }}}',
        ]
    );

    $this->end_controls_section();

    $this->start_controls_section(
        'section_prices',
        [
            'label' => __( 'Prices', 'elementor' ),
        ]
    );

    $serviceRepeater = new Repeater();

    $serviceRepeater->add_control(
        'category_slug',
        [
            'label'       => __( 'Category Slug', 'elementor' ),
            'type'        => Controls_Manager::TEXT,
            'default'     => __( 'category-name', 'elementor' ),
            'label_block' => true,
        ]
    );

    $serviceRepeater->add_control(
        'service_title',
        [
            'label'       => __( 'Service Title', 'elementor' ),
            'type'        => Controls_Manager::TEXT,
            'default'     => __( 'Service Title', 'elementor' ),
            'label_block' => true,
        ]
    );

    $serviceRepeater->add_control(
        'service_price',
        [
            'label'       => __( 'Price', 'elementor' ),
            'type'        => Controls_Manager::TEXT,
            'default'     => __( '40,00', 'elementor' ),
            'label_block' => true,
        ]
    );

    $this->add_control(
        'services',
        [
            'label'       => __( 'Services', 'elementor' ),
            'type'        => Controls_Manager::REPEATER,
            'fields'      => $serviceRepeater->get_controls(),
            'title_field' => '{{{ service_title }}}',
        ]
    );

    $this->end_controls_section();
}

Add to render

foreach ($settings['categories'] as $category) {
    echo $category['category_title'];

    foreach($settings['services' as $service) {
        if ($category['category_slug'] === $service['category_slug'] {
            echo $category['service_title'];
        }
    }
}

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.