peanut-square/ux-twig-component

Symfony的Twig组件

安装: 301

依赖项: 0

建议者: 0

安全: 0

星星: 0

关注者: 0

分支: 20

类型:symfony-bundle

dev-main / 1.4.x-dev 2022-02-14 05:25 UTC

This package is auto-updated.

Last update: 2024-09-14 11:13:48 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属性上。然后,渲染组件!如果属性有一个setter方法(例如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

祝您玩得开心!