peanut-square / ux-twig-component
Symfony的Twig组件
Requires
- php: >=7.2.5
- symfony/dependency-injection: ^4.4|^5.0
- symfony/property-access: ^4.4|^5.0
- twig/twig: ^2.0|^3.0
Requires (Dev)
- symfony/framework-bundle: ^4.4|^5.0
- symfony/phpunit-bridge: ^5.2
- symfony/twig-bundle: ^4.4|^5.0
Conflicts
- symfony/dependency-injection: <4.4.18,<5.1.10,<5.2.1
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!' }) }}
享受你的新组件!
这把客户端框架中熟悉的“组件”系统引入了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
组件可重用,我们需要使消息和类型(例如success
、danger
等)可配置。要做到这一点,为每个创建一个公共属性
// 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>
我们如何填充message
和type
属性?通过在渲染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。
祝您玩得开心!