olveneer / twig-components-bundle
一个轻量级的symfony扩展包,它提供了一种简单的方法将模块化、组件结构应用到您的twig模板中。
Requires
- php: >=5.5.0
- symfony/framework-bundle: ~2.7|~3.0|~4.0
- symfony/options-resolver: ^4.1
- twig/twig: ^2.5
- dev-master
- v3.2.0
- v3.1.1
- v3.1.0
- v3.0.0
- v2.6.1
- v2.6.0
- v2.5.0
- v2.4.1
- v2.4.0
- v2.3.3
- v2.3.2
- v2.3.1
- v2.3.0
- v2.2.0
- v2.1.1
- v2.1.0
- 2.0.0
- v1.7.0
- v1.6.6
- v1.6.4
- v1.6.3
- v1.6.0
- v1.5.8
- v1.5.7
- v1.5.4
- v1.5.3
- v1.5.1
- v1.4.5
- v1.4
- v1.3.5
- v1.3.1
- v1.3
- v1.2.5
- v1.2.0
- v1.1.2
- v1.1.1
- v1.1
- v1.0.3
- v1.0.2
- v1.0.1
- v1.0.0
- dev-dependabot/composer/twig/twig-2.15.3
- dev-dependabot/composer/symfony/framework-bundle-4.4.37
- dev-dependabot/composer/symfony/http-foundation-4.4.4
- dev-dependabot/composer/symfony/cache-4.3.9
- dev-dependabot/composer/symfony/dependency-injection-4.1.12
This package is not auto-updated.
Last update: 2024-09-28 18:10:51 UTC
README
twig-components-bundle
一个轻量级的symfony扩展包,它提供了一种简单的方法将模块化、组件结构应用到您的twig模板中。
关于
当开发前端时,我发现我很快地使用Vue和React等框架。最近我更多地关注于使用Symfony的后端编程,很快我就发现我缺少Vue提供的组件结构。当然,你有了块和所有这些,但它们仍然缺乏某些Vue以其自身方式提供的核心功能。
因此,我制作了这个包来恢复这些功能,我认为它工作得很好。总是欢迎建议或反馈! olvenmage@live.nl
安装
composer require olveneer/twig-components-bundle
使用
类
每个组件都需要一个特殊的服务,该服务扩展了
/**
* Returns the parameters to be used when rendering the template.
* Props can be provided when rendering the component to make it more dynamic.
*
* @param array $props
* @return array
*/
public function getParameters(array $props)
{
return [
'test' => 123
];
}
一个返回您twig组件参数的函数。你问这些$props
是什么?嗯,组件的主要优势是它们可以被重复使用多次。然而,你很少需要完全相同的组件,所以props作为选项来使参数更加动态。困惑?不用担心,稍后一切都会变得更加清晰。
/**
* Configures the props using the Symfony OptionResolver
*
* @param OptionsResolver $resolver
*/
public function configureProps(OptionsResolver $resolver)
{
$resolver->setDefaults(['someProp' => 'someValue']);
}
可以使用configureProps
方法确保组件始终按预期运行。如果你一直使用symfony,这应该不会是什么新鲜事。
让我们确定我们的组件名称:假设你的组件上的get_class()
结果是App/Component/TestComponent
。然后我们取类名而不带命名空间,所以在这个例子中是TestComponent
,接下来,我们将其转换为snake_case,最终名称结果是test_component
如果你想自定义名称,使用
/**
* Returns the string to use as a name for the component.
*
* @return String
*/
public function getName()
{
return 'another_name';
}
现在,这些服务不是自动注册为Twig组件,你需要使用额外的olveneer.component
标签来注册服务。示例
app.test_component:
class: App\Component\TestComponent
tags: ['olveneer.component']
如果你使用autoconfigure,这会自动为扩展
这会将组件目录中的每个服务标记为Twig组件。
渲染
当你已经配置了组件类,是时候创建模板了。模板的位置应该在templates/components/<component_name>.html.twig
中。(你也会得到一个错误消息说这个)。
'/components'
部分可以在配置文件中进行配置,但稍后我们会谈到这一点。
文件应该叫什么名字?当然是组件名称!所以在这个例子中是test_component.html.twig
。
在这里,你可以访问你发送回的所有参数。
太好了!现在你怎么把模板放在屏幕上呢?很简单!
有两种方法可以做到这一点。
方法A:在你的模板中使用twig函数渲染它。
方法B:从你的控制器/服务中渲染它。
我们先看看方法A。
在任何模板中,你都可以使用以下语法
{% get name %} {% endget %}
这会渲染组件而不传递props,要这样做,使用这个
{% get name with { someProp: 'someValue' } %} {% endget %}
方法B是将Component
注入到您的服务或控制器中,如果您只想获取HTML,则可以调用renderComponent( $props = [])
函数,或者调用render($props = [])
以立即获取浏览器响应。
示例
/**
* @Route(path="/")
* @param TestComponent $testComponent
* @return \Symfony\Component\HttpFoundation\Response
* @throws \Twig_Error_Loader
* @throws \Twig_Error_Runtime
* @throws \Twig_Error_Syntax
*/
public function test(TestComponent $testComponent)
{
return $testComponent->render();
}
$component->render($props = []);
插槽系统
TwigComponents还内置了传递HTML而不是仅传递变量的功能。
有两种类型的{% slot %}标签。首先,那些存在于{% get %}标签内部的标签。这些标签之间的HTML将被插入到正在渲染的组件中。其次,那些不
存在于{% get %}标签中的{% slot %}标签。这些标签定义了一个HTML/Twig内容可以插入的位置,中间的值是默认值。
假设您有一个名为'card'的组件,使用card.html.twig
模板文件渲染bootstrap卡片。
// card.html.twig
<div class="card">
<div class="card-header">
{% slot header %}
<h1> default header value </h1>
{% endslot %}
</div>
<div class="card-body">
{% slot body %}
<h1> default body value </h1>
{% endslot %}
</div>
</div>
如果您熟悉Vue或React,您可能已经意识到发生了什么。这个组件正在请求某些标记块。您在插槽块之间看到的值是在没有插槽
的情况下默认值。
现在我们想在我们的模板中使用这个卡片组件
// index.html.twig
<body>
{% get card %}
{% slot header %}
<div> My own html! </div>
{% endslot %}
{% endget %}
</body>
这将导致渲染mainComponent,其中头部插槽将用<div> My own html! </div>
填充,而主体默认为<h1> default body value </h1>
。
当然,我们也可以像这样将我们的主体插槽插入其中
// index.html.twig
<body>
{% get card %}
{% slot header %}
<div> My own html! </div>
{% endslot %}
{% slot body %}
<div> A cool body </div>
{% endslot %}
{% endget %}
</body>
不仅可以从{% get %}传递值到组件中,{% slot %}还可以暴露
某些变量。以下是一个示例
// parent.html.twig
{% for item in items %}
...
{% endfor %}
{% slot body expose { products: items, hello: 'Hi!!!' } %} {% endslot %}
//child.html.twig
{% get parent %}
{% slot body %}
<div> ... </div>
<h1> {{ hello }} </h1>
{% for product in products %}
...
{% endfor %}
{% endslot %}
{% endget %}
如您所见,您暴露的变量可以在{% slot %}标签中使用!请注意,您暴露的变量仅在那些标签之间存在!
混入
有时您有一些您希望在大多数组件中使用的特定属性或参数。为了避免代码重复,您可以使用像这样的混入
// SomeComponent.php
public funciton getParameters()
{
...
}
public function importMixins()
{
return [SomeMixin::class];
}
如您所见,使用非常简单。现在让我们看看一个混入
// SomeMixin.php
class SomeMixin extends TwigComponentMixin
{
/**
* @return array
*
* Merges with the parameters.
*/
public function getParameters()
{
return [];
}
/**
* @return array
*
* Merges with the props.
*/
public function getProps()
{
return [];
}
/**
* @return int
*
* The execution order of all the mixins. Mixins with the same key override the earlier ones.
* Lower goes first.
*/
public function getPriority()
{
return 0;
}
}
混入只是另一个服务,所以在这里您可以注入并使用您想要的任何东西。当混入被解析时,其返回的参数将与使用array_merge
的组件合并。
混入必须在您的services.yaml
中使用olveneer.mixin
标签进行注册。如果您使用自动配置,则任何扩展TwigComponentMixin
类的服务都会自动发生!
最佳实践
以下是一些最佳实践,以获得最佳长期更新支持和整体代码优化。
- 尽可能使用默认的
getName()
(返回短类名)。 - 如果适用,请使用
configureProps()
方法。 - 使用一些特定配置处理边缘情况的同时,使用之前提到的组件自动配置。
- 使用蛇形命名法为自定义组件命名。例如:
some_component_name
- 在/src下为混入和组件创建两个单独的文件夹。
配置
现在我们终于来到了配置部分,我将向您展示一个示例配置文件。
// config/packages/olveneer_twig_components.yaml
olveneer_twig_components:
components_directory: '/components'
关于嵌入呢?一些人可能会想,“为什么不使用twig的嵌入呢?”
嗯,有一些明显的区别
嵌入使用块标签而不是我们酷炫的插槽标签,那里的区别在于,一个块只能被覆盖一次,这是合逻辑的,因为块相当静态。然而,插槽标签可以暴露变量,供覆盖的标签使用,因此它们可以非常动态。因此,这是可行的
// parent.html.twig
<table>
<tr>
<th></th>
<th></th>
</tr>
<tr>
{% for item in items %}
{% slot body expose {item: item} %}
{% endslot %}
{% endfor %}
</tr>
</table>
// child.html.twig
{% get parent %}
{% slot body %}
<td> {{ item.name }} </td>
<td> {{ item.text }} </td>
{% endslot %}
{% endget %}
注意:此示例灵感来自vuetify。他们在组件中大量使用这个。
此外,组件可以非常独立。当使用嵌入式组件时,您必须在{}中传递它们所需的参数。这可能导致您必须将它们注入并传递到每个希望嵌入模板的模板中。使用组件时,它们可以自行处理,只需请求props
即可使用。
即将推出
- 欢迎提出建议!