danmartuszewski/ux-twig-component

为 Symfony 提供的 Twig 组件

dev-main / 1.4.x-dev 2022-01-18 11:22 UTC

This package is auto-updated.

Last update: 2024-09-18 18:01:16 UTC


README

实验性 该组件目前处于实验性阶段,可能会发生变化,甚至发生重大变化。

Twig 组件使您能够将对象绑定到模板,从而更容易渲染和重用小的模板 "单元",例如 "警告",模态的标记,或分类侧边栏

每个组件都由以下内容组成(1)一个类

// src/Components/AlertComponent.php
namespace App\Components;

use Symfony\UX\TwigComponent\AbstractComponent;

class AlertComponent extends AbstractComponent
{
    public string $type = 'success';
    public string $message;

    public static function getComponentName(): string
    {
        return 'alert';
    }

    // optional, specify template name 
    public static function getTemplate() : ?string
    {
        return '@App/components/alert.html.twig';
    }
}

和(2)相应的模板

{# templates/components/alert.html.twig #}
<div class="alert alert-{{ this.type }}">
    {{ this.message }}
</div>

完成!现在在您想要的地方渲染它

{{ component('alert', { message: 'Hello Twig Components!' }) }}

享受您的新组件!

Example of the AlertComponent

这将从客户端框架中引入熟悉的 "组件" 系统,进入 Symfony。结合 Live Components,创建一个具有自动、Ajax 渲染的交互式前端。

安装

让我们开始安装!运行

composer require symfony/ux-twig-component

这就完了!我们准备好了!

创建一个基本组件

让我们创建一个可重用的 "警告" 元素,我们可以在整个网站上使用它来显示成功或错误消息。第一步总是创建一个实现 ComponentInterface 的组件。让我们尽可能简单开始

// src/Components/AlertComponent.php
namespace App\Components;

use Symfony\UX\TwigComponent\ComponentInterface;

class AlertComponent implements ComponentInterface
{
    public static function getComponentName(): string
    {
        return 'alert';
    }
}

第二步是为该组件创建一个模板。模板位于 templates/components/{Component Name}.html.twig,其中 {Component Name} 是您从 getComponentName() 方法返回的内容

{# templates/components/alert.html.twig #}
<div class="alert alert-success">
    Success! You've created a Twig component!
</div>

目前这并不有趣...因为消息是硬编码到模板中的。但这足够了!通过从任何其他 Twig 模板渲染您的组件来庆祝

{{ component('alert') }}

完成!您刚刚渲染了第一个 Twig 组件!花点时间挥舞拳头 - 然后回来!

将数据传递到您的组件中

这是一个好开始:但这并不有趣!为了使我们的 alert 组件可重用,我们需要使消息和类型(例如 successdanger 等)可配置。要做到这一点,为每个创建一个公共属性

// src/Components/AlertComponent.php
// ...

class AlertComponent implements ComponentInterface
{
+    public string $message;

+    public string $type = 'success';

    // ...
}

在模板中,AlertComponent 实例可通过 this 变量访问。使用它来渲染这两个新属性

<div class="alert alert-{{ this.type }}">
    {{ this.message }}
</div>

如何填充 messagetype 属性?通过在渲染 component() 函数时将其作为第二个参数传递

{{ component('alert', { message: 'Successfully created!' }) }}

{{ component('alert', {
    type: 'danger',
    message: 'Danger Will Robinson!'
}) }}

幕后,将实例化一个新的 AlertComponent,并将 message 键(以及传递的 type)设置为对象的 $message 属性。然后,渲染组件!如果属性有一个设置器方法(例如 setMessage()),则将调用该方法而不是直接设置属性。

mount() 方法

如果您出于某种原因不想将选项直接设置为 component() 函数的属性,则可以在组件中创建一个 mount() 方法

// src/Components/AlertComponent.php
// ...

class AlertComponent implements ComponentInterface
{
    public string $message;
    public string $type = 'success';

    public function mount(bool $isSuccess = true)
    {
        $this->type = $isSuccess ? 'success' : 'danger';
    }

    // ...
}

mount() 方法在组件实例化后立即被调用一次。由于方法有一个 $isSuccess 参数,因此可以在渲染组件时传递一个 isSuccess 选项

{{ component('alert', {
    isSuccess: false,
    message: 'Danger Will Robinson!'
}) }}

如果选项名称与 mount() 中的参数名称匹配,则将选项作为该参数传递,组件系统将 不会 尝试直接将其设置为属性。

获取服务

让我们创建一个更复杂的示例:一个“特色产品”组件。你可以选择将一个产品对象数组传递给component()函数,并在$products属性上设置这些。但让我们允许组件执行查询的工作。

如何?组件是服务,这意味着自动装配就像正常一样工作。此示例假设你有一个Product Doctrine实体和ProductRepository

// src/Components/FeaturedProductsComponent.php
namespace App\Components;

use App\Repository\ProductRepository;
use Symfony\UX\TwigComponent\ComponentInterface;

class FeaturedProductsComponent implements ComponentInterface
{
    private ProductRepository $productRepository;

    public function __construct(ProductRepository $productRepository)
    {
        $this->productRepository = $productRepository;
    }

    public function getProducts(): array
    {
        // an example method that returns an array of Products
        return $this->productRepository->findFeatured();
    }

    public static function getComponentName() : string
    {
        return 'featured_products';
    }
}

在模板中,可以通过this.products访问getProducts()方法。

{# templates/components/featured_products.html.twig #}

<div>
    <h3>Featured Products</h3>

    {% for product in this.products %}
        ...
    {% endfor %}
</div>

由于此组件没有任何需要填充的公共属性,你可以用以下方式渲染它:

{{ component('featured_products') }}

注意 因为组件是服务,所以可以使用正常的依赖注入。但是,每个组件服务都注册为shared: false。这意味着你可以安全地多次使用不同的数据渲染相同的组件,因为每个组件都将是一个独立的实例。

计算属性

在上一个示例中,我们没有立即查询特色产品(例如在__construct()中),而是创建了一个getProducts()方法,并通过this.products从模板中调用该方法。

这样做是因为,作为一般规则,你应该尽可能使你的组件延迟执行,并只在其属性上存储你需要的信息(这也帮助你在以后将组件转换为实时组件)。在这个设置中,只有当实际调用getProducts()方法时,才会执行查询。这与Vue等框架中的“计算属性”概念非常相似。

但是,getProducts()方法并没有魔法:如果你在模板中多次调用this.products,查询将会多次执行。

为了使你的getProducts()方法表现得像真正的计算属性(其值只在第一次调用该方法时计算),你可以在私有属性上存储其结果。

// src/Components/FeaturedProductsComponent.php
namespace App\Components;
// ...

class FeaturedProductsComponent implements ComponentInterface
{
    private ProductRepository $productRepository;

+    private ?array $products = null;

    // ...

    public function getProducts(): array
    {
+        if ($this->products === null) {
+            $this->products = $this->productRepository->findFeatured();
+        }

-        return $this->productRepository->findFeatured();
+        return $this->products;
    }
}

贡献

有兴趣贡献吗?请访问此存储库的主源:https://github.com/symfony/ux/tree/main/src/TwigComponent

祝你好运!