zaxcms / ui
Nette 框架的额外组件行为
Requires
- php: >=5.4.0
- nette/application: >=2.2.0
Requires (Dev)
- nette/nette: ~2.2
- nette/tester: ~1.3
Suggests
- latte/latte: Powerful templating engine
This package is auto-updated.
Last update: 2024-09-13 20:17:01 UTC
README
该库包含了一些有用的 Nette 框架 UI 扩展。
Zax\Application\UI\IAjaxAware
定义了一个方法 enableAjax()
,显然是为了启用 AJAX 功能。
Zax\Application\UI\Control
这个类扩展了标准的 Nette 控件,并添加了一些有趣的功能,包括 IAjaxAware
实现和视图。其主要目的是在我们执行一些常见任务时节省重复的样板代码,并使得组件的 AJAX 化变得更容易,并且结果一致。
示例
一个非常简单的组件可能看起来像这样
class SomeControl extends Zax\Application\UI\Control { public function beforeRender() { // gets called before a component gets rendered } public function viewDefault() { // gets called only if view is "Default" } }
组件也需要一个模板。所以让我们在组件所在的 'templates' 目录中添加一个 'Default.latte'。结构将如下所示
- SomeControl.php
- /templates
- Default.latte
就是这样!
使用视图
视图由 view<View> 方法定义。如果您尝试访问一个未定义的视图,将抛出一个异常。另外,每个视图都有一个同名的模板。
视图内部实际上只是一个持久的参数 $view
,因此创建指向视图的链接 couldn't be any easier。
$this->link('this', ['view' => 'Foo']);
使用渲染
在 Nette 中,我们有时可能想使用类似这样的事物:{control someControl:foo}
。通常,我们会创建一个 renderFoo
方法,但这个组件基于 __call
魔法方法,所以这不会正常工作。相反,让我们调用我们的方法 beforeRenderFoo
。同样,它需要一个单独的模板,假设我们仍在 "Default" 视图中,那么模板将是 'Default.Foo.latte'。
模板命名的模式是 '<View>.latte' 或 '<View>.<Render>.latte'。
我们也可以向渲染传递参数,因此 {control someControl:foo, bar => val}
将调用 beforeRenderFoo
方法并将 "val" 传递给名为 "bar" 的参数。
处理 AJAX
我不会绕弯子。AJAX 是神奇的,它就是为了满足我的需求而设计的,所以你可能会在这里发现一些 WTF 因素。但既然我已经花时间实现了它,我也可以记录它。
首先我们需要做的是在我们的新创建的组件上调用 enableAjax
(在 createComponent*
方法中),并在我们的组件模板中用 {snippet}
(不带名称)包裹我们的组件。从现在起,当我们发送 AJAX 请求时,组件应该自动重新绘制它的片段。请注意,这将启用所有子组件的 AJAX(嗯,对于所有实现 IAjaxAware
的子组件)。
已知限制: Nette 按需创建组件。这意味着,如果一个组件在请求期间没有接收到任何参数,它将在在模板中需要它之后创建,这对于 AJAX 重绘来说太晚了。一个简单的解决方案是在操作中手动创建组件,如下所示
public function actionDefault() { $this->createComponent('someControl'); // or shorter $this['someControl']; }
好的,现在我们有一个了解 AJAX 并且知道 AJAX 是否启用的组件。现在是我们创建一些尊重这些设置的链接的时候了。有两种方法可以做到这一点,一种是尊重默认的 nette.ajax.js 设置,但需要更多的样板代码,另一种是使用 n:ajax
宏。
要使用第一种方法,只需在添加类 "ajax" 到您的链接之前检查 $control->isAjaxEnabled()
(或由于 Nette\Object 魔法 $control->ajaxEnabled
),如下所示
<a n:href="this, view => Foo" n:class="$control->ajaxEnabled ? ajax">link</a>
另一种方法在模板中看起来更加优雅
<a n:href="this, view => Foo" n:ajax>link</a>
这个n:macro在你的组件上执行相同的检查,但它不是添加一个类,而是添加data-zax-ajax
属性。为了使其工作,我们需要在配置中注册一个扩展,并在这个js代码之前添加以下代码:
extensions:
ajax: Zax\DI\AjaxExtension
before calling $.nette.init()
$.nette.ext('init').linkSelector = 'a[data-zax-ajax]';
简单吧?现在,还有一个问题要讨论。如果你曾经尝试过将你的应用AJAX化,你可能已经走过一条路,那就是不断地写if is ajax, redraw, else redirect
,通常你只能在信号中使用AJAX,因为当你设置持久性参数或类似的东西时,你不能调用redrawControl
。
我也走过那条路,真的很糟糕。我们已经有自动片段无效化,这解决了持久性参数部分,但关于if is ajax blahblahblah
呢?好吧,我添加了一个名为go
的方法,它为我们执行这个检查,并确保无论是否是AJAX请求,我们都能到达同一个目的地。
所以调用$this->go('signal!', ['view' => 'Foo']);
将检查是否是AJAX请求,如果是,则执行常规重定向到信号和Foo视图,否则直接将我们转发到相同的目的地,而不会发出额外的请求。这再简单不过了!
Zax\Application\UI\Multiplier
Multiplier是一个小巧的类,允许我们在一个页面上拥有同一组件的多个实例。但是默认的Nette Multiplier会阻止子组件接收ajaxEnabled状态,因为它不是IAjaxAware
。所以我把它变成了IAjaxAware
。
自定义
控制行为被划分为几个特质
TControlForward
forward()
和presenterForward()
方法。
TControlAjax
IAjaxAware
实现+TControlForward
TControlLifeCycle
使用__call
进行生命周期管理,调用view*()
和beforeRender*()
,并添加一个持久参数$view
。此外,如果控制实现IHasControlLifeCycle
,则自动调用run()
方法,我们可以使用该方法来渲染模板或做任何我们想做的事情。
TControlMergeLinkParams
允许我们在特定的组件中指定$defaultLinkParams
,以在使用多个(子)组件和持久参数时尽可能保持URL的清洁。