# 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');
}
~~~
刪除項目也是如此。 可以發送空的代碼片段,但是通常列表可以被分頁,并且實現刪除一個項目并加載另一個項目(其曾經在分頁列表的不同頁面上)將是復雜的。
- Nette簡介
- 快速開始
- 入門
- 主頁
- 顯示文章詳細頁
- 文章評論
- 創建和編輯帖子
- 權限驗證
- 程序員指南
- MVC應用程序和控制器
- URL路由
- Tracy - PHP調試器
- 調試器擴展
- 增強PHP語言
- HTTP請求和響應
- 數據庫
- 數據庫:ActiveRow
- 數據庫和表
- Sessions
- 用戶授權和權限
- 配置
- 依賴注入
- 獲取依賴關系
- DI容器擴展
- 組件
- 字符串處理
- 數組處理
- HTML元素
- 使用URL
- 表單
- 驗證器
- 模板
- AJAX & Snippets
- 發送電子郵件
- 圖像操作
- 緩存
- 本土化
- Nette Tester - 單元測試
- 與Travis CI的持續集成
- 分頁
- 自動加載
- 文件搜索:Finder
- 原子操作