How to create a component outside of a view file

I’m looking to use the paginate_links function and would like to include a custom icon for the previous and next links.

I’ve tried following the advice here:

But I get an error “Undefined variable: attributes”. I’ve tried the following:

view('components.icon', ['icon' => 'chevron-left'])

$icon = new Icon('chevron-left');
return $icon->render();

What is the correct way to retrieve a component with fully formed HTML?

Are you using sage-svg for icons in Sage?

I do have sage-svg installed but the Icon component doesn’t use it at the moment. Though if there’s a solution using sage svg then I’m definitely open to using it!

Yes, IMHO this is already tested and leverages all the Sage features, you should use this instead.

Thanks @strarsis it does fix the issue. I still think it would be good to know how to include a component outside of a blade file though.

Well, that error indicates that there is no variable attributes, but the blade template that is included as a view tries to use it.

Yes there should be an attributes variable, the reason why I’m getting confused is because calling:

$icon = new Icon('chevron-left');
return $icon->render();

Instantiates a new Icon component which should inject attributes before rendering. When I call <x-icon icon="chevron-left" /> inside a blade file attributes is derived from the attributes of the component and I don’t understand why the same doesn’t apply when instantiating the component as an object.

Hello, I was actually trying to figure this out in Sage 10 and came across this thread. I noticed no one has posted an answer to your question so I will share my discovery. The following worked for me, given your example:

echo \Roots\view('path.to.your.template.icon', ['yourTargetParam', 'chevron-left'])->render();

I can’t really give you an exact answer since I don’t know the paths and params for your component. As a result, I will give you a full example that I am working with:

In [theme]/app/View/Components/FieldsSectionTitle I have defined my component class with:

<?php

namespace App\View\Components;
use Roots\Acorn\View\Component;

/**
 * Fields Section Title
 *
 * Common section title component with a bar above title text which is used
 * in A LOT of custom mfj blocks.
 */
class FieldsSectionTitle extends Component {
    /**
     * Title
     *
     * @var string
     */
    public $title;

    /**
     * Title Text Alignment Attr Class
     *
     * @var string
     */
    public $textAlignAttrClass;

    /**
     * Title Bar Style
     *
     * The bootstrap background css attribute used to set the
     * title's bar color.
     *
     * @var string
     */
    public $titleBarStyle;

    /**
     * Title Attr Classes
     *
     * @var string
     */
    public $titleAttrClasses;

    /**
     * Title Bar Attr Classes
     *
     * CSS Selector classes to add to the "title bar" css attribute.
     *
     * @var string
     */
    public $titleBarAttrClasses;

    /**
     * Create the component instance.
     *
     * @param string $title               Title text string
     * @param string $textAlignAttrClass  Title alignment css selector class
     * @param string $titleBarStyle       Title bar background color bootstrap selector class
     * @param string $titleAttrClasses    Title css selector classes
     * @param string $titleBarAttrClasses Title bar selector css classes
     */
    public function __construct(
        string $title = '',
        string $textAlignAttrClass = '',
        string $titleBarStyle = '',
        string $titleAttrClasses = '',
        string $titleBarAttrClasses = ''
    ) {
        $this->title = $title;
        $this->textAlignAttrClass = $textAlignAttrClass;
        $this->titleBarStyle = $titleBarStyle;
        $this->titleAttrClasses = $titleAttrClasses;
        $this->titleBarAttrClasses = $titleBarAttrClasses;
    }

    /**
     * Get the view / contents that represent the component.
     *
     * @return \Illuminate\View\View|string
     */
    public function render() {
        return $this->view('components.fields-section-title');
    }
}

Now, I can do the following to pass my params to the component view from OUTSIDE of my sage 10 app directory. In my case, I am overwriting plugin page templates that need to live outside of the app directory:

echo \Roots\view('components.fields-section-title', [
    'title' => 'Testing', 
    'titleBarStyle' => Constants::BOOTSTRAP_BG_BLUE_MD
])->render();

One thing to note here is, if you want all component parameters to be optional in the above example usage… then you need to do safety checks in your component’s blade template. Otherwise, you will get missing $var errors because using the Roots view renderer bypasses the component’s class model logic:

$title = !empty($title) ? $title : '';
$textAlignAttrClass = !empty($textAlignAttrClass) ? $textAlignAttrClass : '';
$titleBarStyle = !empty($titleBarStyle) ? $titleBarStyle : '';
$titleAttrClasses = !empty($titleAttrClasses) ? $titleAttrClasses : '';
$titleBarAttrClasses = !empty($titleBarAttrClasses) ? $titleBarAttrClasses : '';

Hopes this helps people in the future!

3 Likes

Hello, I’m new here, so don’t swear too much if something is wrong.)
I was faced with the fact that I needed to render my custom component inside the app folder in a PHP file with my AJAX functions. To do this, I ultimately took the following steps:

  1. Created a method in the component class that returns all variables, as well as a special array of attributes that is expected by the component.
public function getAllVars(): array
{
	return [
		'attributes'	=> new \Illuminate\View\ComponentAttributeBag(),
		'id'			=> $this->id,
		'title'			=> $this->title,
		'department'	=> $this->department,
		'location'		=> $this->location,
		'permalink'		=> $this->permalink
	];
}
  1. In my AJAX function I needed to get component’s HTML structure inside standard WP_Query loop. So I did this (JopPreview is my component class):
while( $jobsQuery->have_posts() ){
	$jobsQuery->the_post();
	$preview	= new JobPreview( get_the_ID() );
	$html		.= view( 'components.job-preview.job-preview', $preview->getAllVars() );
}

Hope this will help someone! Have a nice day!

1 Like