-
-
Notifications
You must be signed in to change notification settings - Fork 276
Expand file tree
/
Copy pathajax.texy
More file actions
249 lines (176 loc) · 13.4 KB
/
ajax.texy
File metadata and controls
249 lines (176 loc) · 13.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
AJAX и фрагменти
****************
<div class=perex>
В ерата на съвременните уеб приложения, където функционалността често се разпростира между сървъра и браузъра, AJAX е важен свързващ елемент. Какви възможности предлага Nette Framework в тази област?
- Изпращане на части от шаблона, т.нар. фрагменти
- предаване на променливи между PHP и JavaScript
- инструменти за отстраняване на грешки при AJAX заявките
</div>
Заявка AJAX .[#toc-ajax-request]
================================
Заявката AJAX не се различава съществено от класическата HTTP заявка. Извиква се презентатор с определени параметри. От водещия зависи как да отговори на заявката - той може да върне данни във формат JSON, да изпрати част от HTML код, XML документ и т.н.
От страна на браузъра инициираме AJAX заявка, като използваме функцията `fetch()`:
```js
fetch(url, {
headers: {'X-Requested-With': 'XMLHttpRequest'},
})
.then(response => response.json())
.then(payload => {
// обработка на отговора
});
```
От страна на сървъра AJAX заявката се разпознава чрез метода `$httpRequest->isAjax()` на услугата, която [капсулира HTTP заявката |http:request]. Той използва HTTP заглавието `X-Requested-With`, така че е от съществено значение да го изпратите. В рамките на презентатора можете да използвате метода `$this->isAjax()`.
Ако искате да изпратите данни във формат JSON, използвайте метода [`sendJson()` |presenters#Sending a response] метод. Методът също така прекратява дейността на презентатора.
```php
public function actionExport(): void
{
$this->sendJson($this->model->getData);
}
```
Ако планирате да отговорите със специален шаблон, предназначен за AJAX, можете да го направите по следния начин:
```php
public function handleClick($param): void
{
if ($this->isAjax()) {
$this->template->setFile('path/to/ajax.latte');
}
//...
}
```
Извадки .[#toc-snippets]
========================
Най-мощният инструмент, предлаган от Nette за свързване на сървъра с клиента, са фрагментите. С тях можете да превърнете едно обикновено приложение в AJAX приложение с минимални усилия и няколко реда код. Примерът Fifteen демонстрира как работи всичко това, а кодът му може да бъде намерен в [GitHub |https://github.com/nette-examples/fifteen].
Извадките или изрезките ви позволяват да актуализирате само части от страницата, вместо да презареждате цялата страница. Това е по-бързо и по-ефективно, а също така осигурява по-удобно потребителско изживяване. Snippets може да ви напомнят за Hotwire за Ruby on Rails или Symfony UX Turbo. Интересното е, че Nette въвежда фрагментите 14 години по-рано.
Как работят отрязъците? Когато страницата се зарежда за първи път (заявка, която не е свързана с AJAX), се зарежда цялата страница, включително всички фрагменти. Когато потребителят взаимодейства със страницата (напр. щракне върху бутон, изпрати формуляр и т.н.), вместо да се зареди цялата страница, се прави AJAX заявка. Кодът в презентатора извършва действието и решава кои фрагменти се нуждаят от актуализиране. Nette визуализира тези фрагменти и ги изпраща под формата на JSON масив. След това кодът за обработка в браузъра вмъква получените фрагменти обратно в страницата. Следователно се прехвърля само кодът на променените фрагменти, което спестява честотна лента и ускорява зареждането в сравнение с прехвърлянето на цялото съдържание на страницата.
Naja .[#toc-naja]
-----------------
За обработка на фрагменти от страна на браузъра се използва [библиотеката Naja |https://naja.js.org]. [Инсталирайте я |https://naja.js.org/#/guide/01-install-setup-naja] като пакет за node.js (за използване с приложения като Webpack, Rollup, Vite, Parcel и други):
```shell
npm install naja
```
... или я вмъкнете директно в шаблона на страницата:
```html
<script src="https://unpkg.com/naja@2/dist/Naja.min.js"></script>
```
Първо трябва да [инициализирате |https://naja.js.org/#/guide/01-install-setup-naja?id=initialization] библиотеката:
```js
naja.initialize();
```
За да превърнете обикновена връзка (сигнал) или подаване на форма в AJAX заявка, просто маркирайте съответната връзка, форма или бутон с класа `ajax`:
```html
<a n:href="go!" class="ajax">Go</a>
<form n:name="form" class="ajax">
<input n:name="submit">
</form>
or
<form n:name="form">
<input n:name="submit" class="ajax">
</form>
```
Прерисуване на фрагменти .[#toc-redrawing-snippets]
---------------------------------------------------
Всеки обект от класа [Control |components] (включително самият Presenter) запазва запис дали са настъпили промени, които налагат прерисуването му. За тази цел се използва методът `redrawControl()`.
```php
public function handleLogin(string $user): void
{
// след като влезете в системата, е необходимо да прерисувате съответната част
$this->redrawControl();
//...
}
```
Nette също така позволява по-фино управление на това, което трябва да се прерисува. Гореспоменатият метод може да приема името на фрагмента като аргумент. По този начин е възможно да се обезсили (което означава: да се наложи прерисуване) на ниво част от шаблона. Ако целият компонент бъде обезсилен, всеки фрагмент от него също ще бъде прерисуван:
```php
// обезсилва фрагмента 'header'
$this->redrawControl('header');
```
Извадки в Latte .[#toc-snippets-in-latte]
-----------------------------------------
Използването на фрагменти в Latte е изключително лесно. За да определите част от шаблона като фрагмент, просто я обвийте в тагове `{snippet}` и `{/snippet}`:
```latte
{snippet header}
<h1>Hello ... </h1>
{/snippet}
```
Извадката създава елемент `<div>` в HTML страницата със специално генериран `id`. При прерисуване на фрагмент съдържанието на този елемент се актуализира. Следователно при първоначалното визуализиране на страницата трябва да се визуализират и всички фрагменти, дори ако първоначално те могат да бъдат празни.
Можете също така да създадете фрагмент с елемент, различен от `<div>` като използвате атрибут n::
```latte
<article n:snippet="header" class="foo bar">
<h1>Hello ... </h1>
</article>
```
Области на извадките .[#toc-snippet-areas]
------------------------------------------
Имената на фрагментите могат да бъдат и изрази:
```latte
{foreach $items as $id => $item}
<li n:snippet="item-{$id}">{$item}</li>
{/foreach}
```
По този начин ще получим няколко фрагмента като `item-0`, `item-1` и т.н. Ако директно обезсилим динамичен фрагмент (например `item-1`), нищо няма да бъде прерисувано. Причината е, че фрагментите функционират като истински откъси и само те самите се визуализират директно. В шаблона обаче технически няма фрагмент с име `item-1`. Той се появява само при изпълнение на заобикалящия го код на фрагмента, в този случай цикъла foreach. Следователно ще маркираме частта от шаблона, която трябва да се изпълни, с тага `{snippetArea}`:
```latte
<ul n:snippetArea="itemsContainer">
{foreach $items as $id => $item}
<li n:snippet="item-{$id}">{$item}</li>
{/foreach}
</ul>
```
И ще прерисуваме както отделния фрагмент, така и цялата обща област:
```php
$this->redrawControl('itemsContainer');
$this->redrawControl('item-1');
```
Също така е важно да се гарантира, че масивът `$items` съдържа само елементите, които трябва да бъдат прерисувани.
При вмъкване на друг шаблон в основния с помощта на тага `{include}`, който има фрагменти, е необходимо отново да се обвие включеният шаблон в `snippetArea` и да се обезсилят заедно и фрагментът, и областта:
```latte
{snippetArea include}
{include 'included.latte'}
{/snippetArea}
```
```latte
{* included.latte *}
{snippet item}
...
{/snippet}
```
```php
$this->redrawControl('include');
$this->redrawControl('item');
```
Извадки в компонентите .[#toc-snippets-in-components]
-----------------------------------------------------
Можете да създавате фрагменти в [компонентите |components] и Nette автоматично ще ги прерисува. Има обаче специфично ограничение: за да прерисува отрязъци, той извиква метода `render()` без никакви параметри. По този начин подаването на параметри в шаблона няма да работи:
```latte
OK
{control productGrid}
will not work:
{control productGrid $arg, $arg}
{control productGrid:paginator}
```
Изпращане на потребителски данни .[#toc-sending-user-data]
----------------------------------------------------------
Заедно с фрагментите можете да изпращате всякакви допълнителни данни на клиента. Просто ги запишете в обекта `payload`:
```php
public function actionDelete(int $id): void
{
//...
if ($this->isAjax()) {
$this->payload->message = 'Success';
}
}
```
Изпращане на параметри .[#toc-sending-parameters]
=================================================
Когато изпращаме параметри към компонента чрез AJAX заявка, независимо дали става въпрос за сигнални или постоянни параметри, трябва да предоставим тяхното глобално име, което съдържа и името на компонента. Пълното име на параметъра се връща от метода `getParameterId()`.
```js
let url = new URL({link //foo!});
url.searchParams.set({$control->getParameterId('bar')}, bar);
fetch(url, {
headers: {'X-Requested-With': 'XMLHttpRequest'},
})
```
Метод за обработка със съответните параметри в компонента:
```php
public function handleFoo(int $bar): void
{
}
```