💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# AJAX & Snippets 现代web应用程序现在在服务器上运行一半,在浏览器中运行一半。 AJAX是重要的联合因素。 Nette Framework提供什么支持? * 发送模板片段(所谓的片段) * 在PHP和JavaScript之间传递变量 * AJAX应用程序调试 可以使用封装HTTP请求$ httpRequest-> isAjax()(基于X请求的HTTP报头进行检测)的服务的方法来检测AJAX请求。 控制器还有一个简写方法:$ this-> isAjax()。 AJAX请求与正常的请求没有区别 - 使用某个视图和参数调用控制器。 它也是由控制器如何反应:它可以使用它的例程返回一段HTML代码(一个片段),一个XML文档,一个JSON对象或一段Javascript代码。 ~~~ 注意:Nette不实现客户端AJAX请求。 我们推荐nets.ajax.js库由VojtěchDobeš或Nittro框架。 ~~~ 有一个预处理对象称为有效负载专用于在JSON中将数据发送到浏览器。 ~~~ public function actionDelete($id) { if ($this->isAjax()) { $this->payload->message = 'Success'; } ... } ~~~ 要完全控制JSON输出,请在控制器中使用JsonResponse。 它立即终止制器,模板不显示: ~~~ $this->sendResponse(new JsonResponse(['key' => 'value', ...])); ~~~ 如果我们要发送HTML,我们可以为AJAX请求设置一个特殊模板: ~~~ public function handleClick($param) { if ($this->isAjax()) { $this->template->setFile('path/to/ajax.phtml'); } ... } ~~~ 但是,有一个更强大的内置AJAX支持工具 - 片段。 使用它们使得有可能将常规应用程序转换为AJAX,而不仅仅是几行代码。 它的一切工作原理在第十五个例子中控制器中,他们的代码也可以在build或者GitHub上访问。 片段的工作方式是,在初始(即非AJAX)请求期间传送整个页面,然后与每个其他AJAX子请求(同一呈现者的相同视图的请求)仅传送改变的部分的代码, 前面提到的仓库称为有效载荷。 有两种机制指定:无效和片段渲染。 ## 无效 Control类的每个后代(Presenter也是)能够记住在需要它重新呈现的子请求期间是否有任何改变。 有一对方法来处理这个:redrawControl()和isControlInvalid()。 一个例子: ~~~ public function handleLogin($user) { // 对象必须在用户登录后重新呈现 $this->redrawControl(); ... } ~~~ 然而,Nette提供了比在组件的水平上甚至更细的区别。 列出的方法可以接受代码段的名称作为参数。 因此,可以在片段级别上使无效(即强制重新呈现)(每个对象可以保存任意数量的片段)。 如果整个组件无效,则每个代码段重新呈现。 组件是“无效的”,即使它的任何子组件也是无效的。 ~~~ echo $this->isControlInvalid(); // -> FALSE $this->redrawControl('header'); // invalidates the snippet named 'header' echo $this->isControlInvalid('header'); // -> TRUE echo $this->isControlInvalid('footer'); // -> FALSE echo $this->isControlInvalid(); // -> TRUE, at least one snippet is invalid $this->redrawControl(); // invalidates the whole component, every snippet echo $this->isControlInvalid('footer'); // -> TRUE ~~~ 接收信号的组件将自动失效。 由于片段无效,我们知道哪些部分的元素将需要重新渲染。 ## {snippet} ... {/ snippet} Nette基于逻辑概念,而不是图形元素,即Control的后代不表示页面上的矩形区域,而是表示甚至可以呈现为各种形式的逻辑组件。 (例如假设组件DataGrid可以具有用于呈现网格的方法,另一个用于呈现“分页器”,等等)。每个元素也可以在单个页面上呈现多次;或有条件地,或每次使用不同的模板等。因此,不可能调用每个无效对象的一些render方法。渲染的方法必须与整个页面被渲染时相同。 页面的呈现非常类似于常规请求的情况进行:加载相同的模板等。然而,重要部分是省略不应当到达输出的部分以及将被关联的部分与标识符,并以JavaScript处理程序的可理解格式发送给用户。 ## 句法 如果模板中有控件或代码段,我们必须使用{snippet} ... {/ snippet}对标记将其包装,这样可确保呈现的代码段将被“剪切”并发送到 浏览器。 它还会将它包含在辅助程序<div>标记中(可以使用不同的标记)。 在下面的示例中,一个片段被命名为header,并且也可以表示控件的模板: ~~~ {snippet header} <h1>Hello .... </h1> {/snippet} ~~~ 如果您想创建一个具有不同于<div>的元素的代码段,或向元素添加自定义属性,则可以使用以下定义: ~~~ <article n:snippet="header" class="foo bar"> <h1>Hello .... </h1> </article> ~~~ ## 动态片段 在Nette中,您还可以使用片段,其名称在运行时创建。 这最适合于各种列表,我们需要更改只有一行,但我们不想传输整个列表通过AJAX。 一个例子是: ~~~ <ul n:snippet="itemsContainer"> {foreach $list as $id => $item} <li n:snippet="item-$id">{$item} <a class="ajax" n:href="update! $id">update</a></li> {/foreach} </ul> ~~~ 有一个名为itemsContainer的静态片段,其中包含几个动态片段:item-0,item-1等。 您不能直接无效动态片段(项目-1的无效没有效果),您必须无效其父代码片段(在此示例itemsContainer)。 这将导致父代码片段的所有代码被执行,但只是其子代码片段被发送到浏览器。 如果要仅发送其中一个子代码段,则必须修改父代码段的输入,以便不生成其他子代码段。 在上面的示例中,您必须确保,对于AJAX请求,只有一个项目将添加到$ list数组,因此foreach循环将只打印一个动态片段。 ~~~ class HomepagePresenter extends \Nette\Application\UI\Presenter { /** * This method returns data for the list. * Usually this would just request the data from a model. * For the purpose of this example, the data is hard-coded. * @return array */ private function getTheWholeList() { return [ 'First', 'Second', 'Third' ]; } public function renderDefault() { if (!isset($this->template->list)) { $this->template->list = $this->getTheWholeList(); } } public function handleUpdate($id) { $this->template->list = $this->isAjax() ? [] : $this->getTheWholeList(); $this->template->list[$id] = 'Updated item'; $this->redrawControl('itemsContainer'); } ~~~ ## 所包含的模板中的代码段 可能发生的情况是,代码段在一个模板中,被包括到不同的模板。 在这种情况下,我们需要在snippetArea宏中包含模板,然后我们使snippetArea和实际代码段失效。 宏代码片段确保代码在里面被执行,但只有包含的模板中的实际代码片段被发送做浏览器。 ~~~ {* parent.latte *} {snippetArea wrapper} {include 'child.latte'} {/snippetArea} ~~~ ~~~ {* child.latte *} {snippet item} ... {/snippet} ~~~ ~~~ $this->redrawControl('wrapper'); $this->redrawControl('item'); ~~~ 您还可以将其与动态片段组合。 ## 添加和删除 如果您向列表中添加一个新项目并使itemsContainer无效,AJAX请求将返回包含新项目的片段,但JavaScript处理程序将无法呈现它。 这是因为没有HTML元素与新创建的ID。 在这种情况下,最简单的方法是将整个列表包装在一个更多的代码段中,并将其全部无效: ~~~ {snippet wholeList} <ul n:snippet="itemsContainer"> {foreach $list as $id => $item} <li n:snippet="item-$id">{$item} <a class="ajax" n:href="update! $id">update</a></li> {/foreach} </ul> {/snippet} <a class="ajax" n:href="add!">Add</a> ~~~ ~~~ public function handleAdd() { $this->template->list = $this->getTheWholeList(); $this->template->list[] = 'New one'; $this->redrawControl('wholeList'); } ~~~ 删除项目也是如此。 可以发送空的代码片段,但是通常列表可以被分页,并且实现删除一个项目并加载另一个项目(其曾经在分页列表的不同页面上)将是复杂的。