9

From working on plugin development, importing (I believe the correct term for this is macro) the 'forms' to quickly generate text areas, selects and etc. is incredibly useful. Adding the import now fails as '_includes' is only accessible from the control panel.

So in my template I could add this line:

{% import "_includes/forms" as forms %}

Then use something similar to this to output my forms fields?

{{ forms.textField({
    label: "First Name"|t,
    id: 'firstName',
    name: 'firstName',
    instructions: "First name (i.e John)",
    placeholder: "John",
    first: true,
    autofocus: true,
    required: true
}) }}

I know when using Symfony and Twig you can specify the namespace on where to grab the template from. Does Craft include a way to access those templates from the front end? Has anyone managed to perform this outside of the control panel?

Edit: if this is not possible, or p&t doesn't plan on making this possible (for security concerns) is there a way for a plugins template path to allow anonymous access? This way the developer could create an '_includes' and allow people to access it in the same manor?

4 Answers 4

4

We've done this on a few sites and you're right it's extremely helpful. The only way I've found to get it to work is to simply copy the "_includes/forms" folder from craft/app/templates to the sites craft/templates

It works but I'd love a less hacks way to do it, either through a plugin or some built-in means.

1
  • I thought symlinking the directory might work, but then subsequent imports get messed up :(
    – Tim Kelty
    Commented Nov 19, 2014 at 20:38
9

I've been able to render CP templates in the front end using some methods in PathService and TemplatesService, which you could use inside your own Template Variable to render the macros:

class YourVariable {

    public function renderFormMacro($macro, array $args)
    {
        // Get the current template path
        $originalPath = craft()->path->getTemplatesPath();

        // Point Twig at the CP templates
        craft()->path->setTemplatesPath(craft()->path->getCpTemplatesPath());

        // Render the macro.
        $html = craft()->templates->renderMacro('_includes/forms', $macro, array($args));

        // Restore the original template path
        craft()->path->setTemplatesPath($originalPath);

        return TemplateHelper::getRaw($html);
    }
}

Which would be used like:

craft.yourVariable.renderFormMacro('textField', { label: 'Name' })

I've not tested it but I think that should be close.

2
  • I'm going to have to give that a try soon, thanks Mike. Commented Nov 20, 2014 at 21:06
  • Confirming that it works. I think this is best answer Commented Feb 26, 2017 at 15:37
2

Here’s a Craft 3-friendly way of achieving what Mike suggested. This assumes your app already has a custom twig extension, if it doesn’t you can easily create one.

Add use craft\web\View; to the top of your TwigExtension, then in getFunctions() add your function definition:

new \Twig\TwigFunction('field', [$this, 'renderFormMacro'], ['is_safe' => ['html']]),

Then the function itself:

public function renderFormMacro(string $fieldType, array $fieldOptions) : string
{
    $oldMode = \Craft::$app->view->getTemplateMode();
    \Craft::$app->view->setTemplateMode(View::TEMPLATE_MODE_CP);
    $html = \Craft::$app->view->renderTemplateMacro('_includes/forms', $fieldType, [$fieldOptions]);
    \Craft::$app->view->setTemplateMode($oldMode);

    return $html;
}

You can then display fields in your frontend templates with code like this:

{{ field('text', {
    name: 'location',
    value: 'Timbuktu'
}) }}
1
  • A heads up that renderTemplateMacro is now deprecated since Craft CMS 3.6 and removed in Craft CMS 4 I think. Commented May 2, 2022 at 18:38
1

For those looking at this in Craft 4, the renderTemplateMacro function in the View class was deprecated in Craft 3 and has been removed in Craft 4, so the Craft 3 example won't work.

https://craftcms.com/docs/4.x/extend/updating-plugins.html#view-rendertemplatemacro-has-been-removed

However, the Cp helper class can provide access to the various form field macros, which you can link up with a Twig function.

https://docs.craftcms.com/api/v4/craft-helpers-cp.html#public-methods.

Using the Cp helper class, means you don't have to do the template mode switching if you call the field in Twig like this:

public function getFunctions(): array
{
    return [
        new TwigFunction('textField', [$this, 'renderTextField'], ['is_safe' => ['html']])
    ];
}

public function renderTextField(array $options)
{
    return Cp::textFieldHtml($options);
}

This could get repetitive for multiple fields, but there are a few ways to retain the same functionality as the renderTemplateMacro example.

Another approach, could be to use a twig function like this, calling the fieldHtml() to render a full field with label.

// Full field with label
public function renderCpFieldHtml(string $fieldType, array $fieldOptions = [])
{
    return Cp::fieldHtml('template:_includes/forms/' . $fieldType, $fieldOptions);
}
// Just the field
public function renderCpField(string $fieldType, array $fieldOptions = [])
{
    return Cp::renderTemplate('_includes/forms/' . $fieldType, $fieldOptions);
}

In both cases fieldType would be a value like text or select.

You can also do this in Craft CMS 3 as well, so if you wanted to avoid having a deprecation issue, it's one less problem when you do upgrade to Craft 4.

Your Answer

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

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