|
| 1 | +# Twig Components |
| 2 | + |
| 3 | +You can use Twig in [your Web Components](web-components-api.md). To do this, |
| 4 | +you need to follow a few simple steps. |
| 5 | + |
| 6 | +<procedure title="1. Install Twig"> |
| 7 | +First, you need to install <a href="https://twig.symfony.com/">the Twig</a> |
| 8 | +itself <a href="https://getcomposer.org/">using Composer</a>. |
| 9 | +<code-block lang="bash"> |
| 10 | + composer require twig/twig |
| 11 | +</code-block> |
| 12 | +</procedure> |
| 13 | + |
| 14 | +<procedure title="2. Create Twig Component"> |
| 15 | +After that, you should create a component that supports twig rendering. |
| 16 | +<code-block lang="php"> |
| 17 | +<![CDATA[ |
| 18 | +use Boson\WebView\Api\WebComponents\ReactiveContext; |
| 19 | +use Boson\WebView\Api\WebComponents\WebComponent; |
| 20 | +use Boson\WebView\WebView; |
| 21 | +use Twig\Environment; |
| 22 | +use Twig\TemplateWrapper; |
| 23 | +
|
| 24 | +abstract class TwigComponent extends WebComponent |
| 25 | +{ |
| 26 | + /** |
| 27 | + * In this case, the template will be initialized |
| 28 | + * once during the first render. |
| 29 | + */ |
| 30 | + private TemplateWrapper $template { |
| 31 | + get => $this->template ??= $this->twig->createTemplate( |
| 32 | + template: $this->renderTwig(), |
| 33 | + ); |
| 34 | + } |
| 35 | + |
| 36 | + public function __construct( |
| 37 | + protected readonly Environment $twig, |
| 38 | + ReactiveContext $ctx, |
| 39 | + WebView $webview, |
| 40 | + ) { |
| 41 | + parent::__construct($ctx, $webview); |
| 42 | + } |
| 43 | + |
| 44 | + abstract protected function renderTwig(): string; |
| 45 | + |
| 46 | + /** |
| 47 | + * Override the default render behavior by |
| 48 | + * redirecting it to a Twig template |
| 49 | + */ |
| 50 | + #[\Override] |
| 51 | + final public function render(): string |
| 52 | + { |
| 53 | + return $this->template->render(\get_object_vars($this)); |
| 54 | + } |
| 55 | +} |
| 56 | +]]> |
| 57 | +</code-block> |
| 58 | +</procedure> |
| 59 | + |
| 60 | + |
| 61 | +<procedure title="3. Create Instances"> |
| 62 | +Now we need to define how exactly these components will be created, for |
| 63 | +this we should create our own instantiator, which will return new |
| 64 | +components on demand. |
| 65 | +<code-block lang="php"> |
| 66 | +<![CDATA[ |
| 67 | +
|
| 68 | +use Boson\WebView\Api\WebComponents\Instantiator\WebComponentInstantiatorInterface; |
| 69 | +use Boson\WebView\Api\WebComponents\ReactiveContext; |
| 70 | +use Boson\WebView\WebView; |
| 71 | +use Twig\Environment; |
| 72 | +use Twig\Loader\ArrayLoader; |
| 73 | +
|
| 74 | +final readonly class TwigComponentInstantiator implements |
| 75 | + WebComponentInstantiatorInterface |
| 76 | +{ |
| 77 | + private Environment $twig; |
| 78 | +
|
| 79 | + public function __construct() |
| 80 | + { |
| 81 | + $this->twig = new Environment(new ArrayLoader()); |
| 82 | + } |
| 83 | + |
| 84 | + private function isTwigComponent(string $component): bool |
| 85 | + { |
| 86 | + return \is_subclass_of($component, TwigComponent::class); |
| 87 | + } |
| 88 | + |
| 89 | + public function create(WebView $webview, ReactiveContext $context): object |
| 90 | + { |
| 91 | + $component = $context->component; |
| 92 | + |
| 93 | + // Pass twig as a first argument in case of passed |
| 94 | + // component extends from TwigComponent class |
| 95 | + if ($this->isTwigComponent($component)) { |
| 96 | + return new $component($this->twig, $context, $webview); |
| 97 | + } |
| 98 | + |
| 99 | + return new $component($context, $webview); |
| 100 | + } |
| 101 | +} |
| 102 | +]]> |
| 103 | +</code-block> |
| 104 | +</procedure> |
| 105 | + |
| 106 | +<procedure title="4. Register Instantiator"> |
| 107 | +To determine that a different instantiator should be used, it can |
| 108 | +be specified in the webview configs. |
| 109 | + |
| 110 | +<code-block lang="php"> |
| 111 | +<![CDATA[ |
| 112 | +$webComponentsConfig = new WebComponentsCreateInfo( |
| 113 | + instantiator: new TwigComponentInstantiator(), |
| 114 | +); |
| 115 | +
|
| 116 | +$applicationConfig = new ApplicationCreateInfo( |
| 117 | + window: new WindowCreateInfo( |
| 118 | + webview: new WebViewCreateInfo( |
| 119 | + webComponents: $webComponentsConfig, |
| 120 | + ), |
| 121 | + ), |
| 122 | +); |
| 123 | +
|
| 124 | +$app = new Boson\Application($applicationConfig); |
| 125 | +]]> |
| 126 | +</code-block> |
| 127 | +</procedure> |
| 128 | + |
| 129 | +<procedure title="5. Twig Components"> |
| 130 | +And now we can create custom twig components! |
| 131 | + |
| 132 | +<code-block lang="php"> |
| 133 | +<![CDATA[ |
| 134 | +class MyTwigComponent extends TwigComponent |
| 135 | +{ |
| 136 | + protected array $items = [1, 2, 3]; |
| 137 | +
|
| 138 | + protected function renderTwig(): string |
| 139 | + { |
| 140 | + return <<<'twig' |
| 141 | + <ul> |
| 142 | + {% for item in items %} |
| 143 | + <li>{{ item }}</li> |
| 144 | + {% endfor %} |
| 145 | + </ul> |
| 146 | + twig; |
| 147 | + } |
| 148 | +} |
| 149 | +]]> |
| 150 | +</code-block> |
| 151 | + |
| 152 | +To register and check, just write a couple of lines |
| 153 | + |
| 154 | +<code-block lang="php"> |
| 155 | +<![CDATA[ |
| 156 | +$app->webview->defineComponent('my-list', MyTwigComponent::class); |
| 157 | + |
| 158 | +$app->webview->html = '<my-list />'; |
| 159 | +]]> |
| 160 | +</code-block> |
| 161 | + |
| 162 | +<img src="example-twig-components-result.png" alt="Example Result"/> |
| 163 | + |
| 164 | +</procedure> |
0 commit comments