rblade 0.5.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -2
- data/README.md +180 -543
- data/REFERENCE.md +138 -0
- data/examples/README.md +3 -0
- data/examples/application/home.rblade +23 -0
- data/examples/components/content/alert.rblade +6 -0
- data/examples/components/content/hero.rblade +14 -0
- data/examples/components/h1.rblade +6 -0
- data/examples/components/p.rblade +14 -0
- data/examples/layouts/app.rblade +16 -0
- data/lib/rblade/compiler/compiles_components.rb +3 -3
- data/lib/rblade/compiler/{compiles_echos.rb → compiles_prints.rb} +11 -10
- data/lib/rblade/compiler/compiles_ruby.rb +1 -1
- data/lib/rblade/compiler/compiles_statements.rb +24 -17
- data/lib/rblade/compiler/statements/{compiles_props.rb → compiles_component_helpers.rb} +13 -5
- data/lib/rblade/compiler/statements/compiles_conditionals.rb +45 -13
- data/lib/rblade/compiler/statements/compiles_form.rb +48 -0
- data/lib/rblade/compiler/statements/compiles_loops.rb +8 -4
- data/lib/rblade/compiler/statements/compiles_once.rb +6 -6
- data/lib/rblade/compiler/statements/compiles_stacks.rb +43 -19
- data/lib/rblade/compiler/tokenizes_statements.rb +13 -2
- data/lib/rblade/compiler.rb +3 -3
- data/lib/rblade/component_store.rb +1 -1
- data/lib/rblade/helpers/attributes_manager.rb +28 -8
- data/lib/rblade/helpers/slot_manager.rb +8 -2
- data/rblade.gemspec +1 -1
- metadata +17 -9
- data/TODO.md +0 -2
data/README.md
CHANGED
@@ -1,22 +1,68 @@
|
|
1
|
+
<a name="rblade-templates"></a>
|
1
2
|
# RBlade Templates
|
2
3
|
|
3
|
-
|
4
|
-
- [Supercharging Blade With Livewire](#supercharging-blade-with-livewire)
|
5
|
-
- [Displaying Data](#displaying-data)
|
6
|
-
- [HTML Entity Encoding](#html-entity-encoding)
|
7
|
-
- [Blade and JavaScript Frameworks](#blade-and-javascript-frameworks)
|
8
|
-
|
9
|
-
**TODO redo TOC**
|
4
|
+
RBlade is a simple, yet powerful templating engine for Ruby on Rails, inspired by [Laravel Blade](https://laravel.com/docs/blade). Unlike other Rails templating engines, RBlade prioritises the use of components and partials within templates.
|
10
5
|
|
11
|
-
|
12
|
-
|
6
|
+
RBlade template files use the `.rblade` file extension and are typically stored in the `app/views` directory.
|
7
|
+
|
8
|
+
<a name="displaying-data"></a>
|
9
|
+
## Table of Contents
|
10
|
+
- [RBlade Templates](#rblade-templates)
|
11
|
+
* [Table of Contents](#table-of-contents)
|
12
|
+
* [Getting Started](#getting-started)
|
13
|
+
* [Displaying Data](#displaying-data)
|
14
|
+
+ [HTML Entity Encoding](#html-entity-encoding)
|
15
|
+
+ [RBlade and JavaScript Frameworks](#rblade-and-javascript-frameworks)
|
16
|
+
- [The `@verbatim` Directive](#the-at-verbatim-directive)
|
17
|
+
* [RBlade Directives](#rblade-directives)
|
18
|
+
+ [If Statements](#if-statements)
|
19
|
+
- [Environment Directives](#environment-directives)
|
20
|
+
+ [Case Statements](#case-statements)
|
21
|
+
+ [Loops](#loops)
|
22
|
+
+ [Conditional Classes & Styles](#conditional-classes-and-styles)
|
23
|
+
+ [Additional Attributes](#additional-attributes)
|
24
|
+
+ [The `@once` Directive](#the-once-directive)
|
25
|
+
+ [Raw Ruby](#raw-ruby)
|
26
|
+
+ [Comments](#comments)
|
27
|
+
* [Components](#components)
|
28
|
+
+ [Rendering Components](#rendering-components)
|
29
|
+
- [Namespaces](#namespaces)
|
30
|
+
+ [Passing Data to Components](#passing-data-to-components)
|
31
|
+
- [Component Properties](#component-properties)
|
32
|
+
- [Short Attribute Syntax](#short-attribute-syntax)
|
33
|
+
- [Escaping Attribute Rendering](#escaping-attribute-rendering)
|
34
|
+
+ [Component Attributes](#component-attributes)
|
35
|
+
- [Default & Merged Attributes](#default-and-merged-attributes)
|
36
|
+
- [Non-Class Attribute Merging](#non-class-attribute-merging)
|
37
|
+
- [Conditionally Merge Classes](#conditionally-merge-classes)
|
38
|
+
- [Retrieving and Filtering Attributes](#retrieving-and-filtering-attributes)
|
39
|
+
+ [Slots](#slots)
|
40
|
+
- [Slot Attributes](#slot-attributes)
|
41
|
+
+ [Registering Additional Component Directories](#registering-additional-component-directories)
|
42
|
+
+ [Index Components](#index-components)
|
43
|
+
* [Forms](#forms)
|
44
|
+
+ [Old Input](#old-input)
|
45
|
+
+ [Method Field](#method-field)
|
46
|
+
* [Stacks](#stacks)
|
47
|
+
|
48
|
+
<a name="getting-started"></a>
|
49
|
+
## Getting Started
|
50
|
+
|
51
|
+
Add RBlade to your Rails project by adding it to your Gemfile:
|
52
|
+
|
53
|
+
```
|
54
|
+
bundle add rblade
|
55
|
+
```
|
56
|
+
|
57
|
+
RBlade will automatically be detected and start parsing templates ending in `.rblade`.
|
58
|
+
|
59
|
+
For a quick overview of RBlade's capabilities, refer to the [reference file](REFERENCE.md) or take a look at the [examples](examples/README.md).
|
13
60
|
|
14
|
-
RBlade is a simple, yet powerful templating engine for Ruby on Rails, inspired by [Laravel Blade](https://laravel.com/docs/blade). Unlike some templating engines, RBlade does not restrict you from using plain Ruby code in your templates. Blade template files use the `.rblade` file extension and are typically stored in the `app/views` directory.
|
15
61
|
|
16
62
|
<a name="displaying-data"></a>
|
17
63
|
## Displaying Data
|
18
64
|
|
19
|
-
You
|
65
|
+
You can display data that is passed to your RBlade views by wrapping the variable in curly braces. For example, given the following controller method:
|
20
66
|
|
21
67
|
```ruby
|
22
68
|
def index
|
@@ -24,16 +70,16 @@ def index
|
|
24
70
|
end
|
25
71
|
```
|
26
72
|
|
27
|
-
You
|
73
|
+
You can display the contents of the `name` variable like so:
|
28
74
|
|
29
75
|
```rblade
|
30
76
|
Hello, {{ name }}.
|
31
77
|
```
|
32
78
|
|
33
79
|
> [!NOTE]
|
34
|
-
> RBlade's `{{ }}` print
|
80
|
+
> RBlade's `{{ }}` print directives are automatically sent through Rails' `h` function to prevent XSS attacks.
|
35
81
|
|
36
|
-
You are not limited to displaying the contents of the variables passed to the view. You
|
82
|
+
You are not limited to displaying the contents of the variables passed to the view. You can also print the results of any Ruby function. In fact, you can put any Ruby code you wish inside of a RBlade print directive:
|
37
83
|
|
38
84
|
```rblade
|
39
85
|
The current UNIX timestamp is {{ Time.now.to_i }}.
|
@@ -42,19 +88,19 @@ The current UNIX timestamp is {{ Time.now.to_i }}.
|
|
42
88
|
<a name="html-entity-encoding"></a>
|
43
89
|
### HTML Entity Encoding
|
44
90
|
|
45
|
-
By default, RBlade `{{ }}`
|
91
|
+
By default, RBlade `{{ }}` directives are automatically sent through Rails' `h` function to prevent XSS attacks. If you do not want your data to be escaped, you can use the following syntax:
|
46
92
|
|
47
93
|
```rblade
|
48
94
|
Hello, {!! name !!}.
|
49
95
|
```
|
50
96
|
|
51
97
|
> [!WARNING]
|
52
|
-
> Be very careful when
|
98
|
+
> Be very careful when printing content that is supplied by users of your application. You should typically use the escaped, double curly brace syntax to prevent XSS attacks when displaying user supplied data.
|
53
99
|
|
54
|
-
<a name="
|
55
|
-
###
|
100
|
+
<a name="rblade-and-javascript-frameworks"></a>
|
101
|
+
### RBlade and JavaScript Frameworks
|
56
102
|
|
57
|
-
Since many JavaScript frameworks also use "curly" braces to indicate a given expression should be displayed in the browser, you
|
103
|
+
Since many JavaScript frameworks also use "curly" braces to indicate a given expression should be displayed in the browser, you can use the `@` symbol to inform the RBlade rendering engine an expression should remain untouched. For example:
|
58
104
|
|
59
105
|
```rblade
|
60
106
|
<h1>Laravel</h1>
|
@@ -62,12 +108,12 @@ Since many JavaScript frameworks also use "curly" braces to indicate a given exp
|
|
62
108
|
Hello, @{{ name }}.
|
63
109
|
```
|
64
110
|
|
65
|
-
In this example, the `@` symbol will be removed by
|
111
|
+
In this example, the `@` symbol will be removed by RBlade; however, `{{ name }}` expression will remain untouched by the RBlade engine, allowing it to be rendered by your JavaScript framework.
|
66
112
|
|
67
|
-
The `@` symbol
|
113
|
+
The `@` symbol can also be used to escape RBlade directives:
|
68
114
|
|
69
115
|
```rblade
|
70
|
-
{{--
|
116
|
+
{{-- RBlade template --}}
|
71
117
|
@@if()
|
72
118
|
|
73
119
|
<!-- HTML output -->
|
@@ -77,7 +123,7 @@ The `@` symbol may also be used to escape RBlade directives:
|
|
77
123
|
<a name="the-at-verbatim-directive"></a>
|
78
124
|
#### The `@verbatim` Directive
|
79
125
|
|
80
|
-
If you are displaying JavaScript variables in a large portion of your template, you
|
126
|
+
If you are displaying JavaScript variables in a large portion of your template, you can wrap the HTML in the `@verbatim` directive so that you do not have to prefix each RBlade print directive with an `@` symbol:
|
81
127
|
|
82
128
|
```rblade
|
83
129
|
@verbatim
|
@@ -87,110 +133,68 @@ If you are displaying JavaScript variables in a large portion of your template,
|
|
87
133
|
@endverbatim
|
88
134
|
```
|
89
135
|
|
90
|
-
<a name="
|
136
|
+
<a name="rblade-directives"></a>
|
91
137
|
## RBlade Directives
|
92
138
|
|
93
139
|
In addition to template inheritance and displaying data, RBlade also provides convenient shortcuts for common Ruby control structures, such as conditional statements and loops. These shortcuts provide a very clean, terse way of working with Ruby control structures while also remaining familiar to their ruby counterparts.
|
94
140
|
|
95
141
|
> [!NOTE]
|
96
|
-
> RBlade directives are case insensitive and ignore underscores, so depending on your preference, all of `@
|
142
|
+
> RBlade directives are case insensitive and ignore underscores, so depending on your preference, all of `@endIf`, `@endIf` and `@end_if` are identical.
|
97
143
|
|
98
144
|
<a name="if-statements"></a>
|
99
145
|
### If Statements
|
100
146
|
|
101
|
-
You
|
147
|
+
You can construct `if` statements using the `@if`, `@elseIf`, `@else`, `@endIf`, `@unless`, and `@endUnless` directives. These directives function identically to their Ruby counterparts:
|
102
148
|
|
103
149
|
```rblade
|
104
150
|
@unless(records.nil?)
|
105
151
|
@if (records.count === 1)
|
106
152
|
I have one record!
|
107
|
-
@
|
153
|
+
@elseIf (records.count > 1)
|
108
154
|
I have multiple records!
|
109
155
|
@else
|
110
156
|
I don't have any records!
|
111
|
-
@
|
112
|
-
@
|
113
|
-
```
|
114
|
-
|
115
|
-
In addition to the conditional directives already discussed, the `@isset` and `@empty` directives may be used as convenient shortcuts for their respective PHP functions:
|
116
|
-
**TODO add nil? directive?**
|
117
|
-
```rblade
|
118
|
-
@isset($records)
|
119
|
-
// $records is defined and is not null...
|
120
|
-
@endisset
|
121
|
-
|
122
|
-
@empty($records)
|
123
|
-
// $records is "empty"...
|
124
|
-
@endempty
|
125
|
-
```
|
126
|
-
|
127
|
-
<a name="authentication-directives"></a>
|
128
|
-
#### Authentication Directives
|
129
|
-
**TODO add authentication directives?**
|
130
|
-
The `@auth` and `@guest` directives may be used to quickly determine if the current user is [authenticated](/docs/{{version}}/authentication) or is a guest:
|
131
|
-
|
132
|
-
```rblade
|
133
|
-
@auth
|
134
|
-
// The user is authenticated...
|
135
|
-
@endauth
|
136
|
-
|
137
|
-
@guest
|
138
|
-
// The user is not authenticated...
|
139
|
-
@endguest
|
157
|
+
@endIf
|
158
|
+
@endUnless
|
140
159
|
```
|
141
160
|
|
142
|
-
|
161
|
+
In addition to the conditional directives above, the `@blank?`, `defined?`, `@empty?`, `@nil?` and `@present` directives can be used as convenient shortcuts:
|
143
162
|
|
144
163
|
```rblade
|
145
|
-
@
|
146
|
-
//
|
147
|
-
@
|
148
|
-
|
149
|
-
@
|
150
|
-
// The user is not authenticated...
|
151
|
-
@endguest
|
164
|
+
@present?(records)
|
165
|
+
// records is defined and is not nil
|
166
|
+
@else
|
167
|
+
// Since these directives are compiled to if statements, you can also use the @else directive
|
168
|
+
@endempty?
|
152
169
|
```
|
153
170
|
|
154
171
|
<a name="environment-directives"></a>
|
155
172
|
#### Environment Directives
|
156
173
|
|
157
|
-
You
|
174
|
+
You can check if the application is running in the production environment using the `@production` directive:
|
158
175
|
|
159
176
|
```rblade
|
160
177
|
@production
|
161
178
|
// Production specific content...
|
162
|
-
@
|
179
|
+
@endProduction
|
163
180
|
```
|
164
181
|
|
165
|
-
Or, you
|
182
|
+
Or, you can determine if the application is running in a specific environment using the `@env` directive:
|
166
183
|
|
167
184
|
```rblade
|
168
185
|
@env('staging')
|
169
186
|
// The application is running in "staging"...
|
170
|
-
@
|
187
|
+
@endEnv
|
171
188
|
|
172
189
|
@env(['staging', 'production'])
|
173
190
|
// The application is running in "staging" or "production"...
|
174
|
-
@
|
175
|
-
```
|
176
|
-
|
177
|
-
<a name="session-directives"></a>
|
178
|
-
#### Session Directives
|
179
|
-
**TODO add sessuib directives**
|
180
|
-
The `@session` directive may be used to determine if a [session](/docs/{{version}}/session) value exists. If the session value exists, the template contents within the `@session` and `@endsession` directives will be evaluated. Within the `@session` directive's contents, you may echo the `$value` variable to display the session value:
|
181
|
-
|
182
|
-
```rblade
|
183
|
-
@session('status')
|
184
|
-
<div class="p-4 bg-green-100">
|
185
|
-
{{ $value }}
|
186
|
-
</div>
|
187
|
-
@endsession
|
191
|
+
@endEnv
|
188
192
|
```
|
189
193
|
|
190
194
|
<a name="switch-statements"></a>
|
191
195
|
### Case Statements
|
192
196
|
|
193
|
-
Case statements can be constructed using the `@case`, `@when`, `@else` and `@
|
197
|
+
Case statements can be constructed using the `@case`, `@when`, `@else` and `@endCase` directives:
|
194
198
|
|
195
199
|
```rblade
|
196
200
|
@case(i)
|
@@ -200,7 +204,7 @@ Case statements can be constructed using the `@case`, `@when`, `@else` and `@end
|
|
200
204
|
Second case...
|
201
205
|
@else
|
202
206
|
Default case...
|
203
|
-
@
|
207
|
+
@endCase
|
204
208
|
```
|
205
209
|
|
206
210
|
<a name="loops"></a>
|
@@ -211,28 +215,28 @@ In addition to conditional statements, RBlade provides simple directives for wor
|
|
211
215
|
```rblade
|
212
216
|
@for (i in 0...10)
|
213
217
|
The current value is {{ i }}
|
214
|
-
@
|
218
|
+
@endFor
|
215
219
|
|
216
220
|
{{-- Compiles to users.each do |user| ... --}}
|
217
221
|
@each (user in users)
|
218
222
|
<p>This is user {{ user.id }}</p>
|
219
|
-
@
|
223
|
+
@endEach
|
220
224
|
|
221
|
-
@
|
225
|
+
@forElse (name in [])
|
222
226
|
<li>{{ name }}</li>
|
223
227
|
@empty
|
224
228
|
<p>No names</p>
|
225
|
-
@
|
229
|
+
@endForElse
|
226
230
|
|
227
|
-
@
|
231
|
+
@eachElse (user in users)
|
228
232
|
<li>{{ user.name }}</li>
|
229
233
|
@empty
|
230
234
|
<p>No users</p>
|
231
|
-
@
|
235
|
+
@endEachElse
|
232
236
|
|
233
237
|
@while (true)
|
234
238
|
<p>I'm looping forever.</p>
|
235
|
-
@
|
239
|
+
@endWhile
|
236
240
|
```
|
237
241
|
|
238
242
|
When using loops you can also skip the current iteration or end the loop using the `@next` and `@break` directives:
|
@@ -241,88 +245,38 @@ When using loops you can also skip the current iteration or end the loop using t
|
|
241
245
|
for (user in users)
|
242
246
|
@if (user.type == 1)
|
243
247
|
@next
|
244
|
-
@
|
248
|
+
@endIf
|
245
249
|
|
246
250
|
<li>{{ user.name }}</li>
|
247
251
|
|
248
252
|
@if (user.number == 5)
|
249
253
|
@break
|
250
|
-
@
|
251
|
-
@
|
254
|
+
@endIf
|
255
|
+
@endFor
|
252
256
|
```
|
253
257
|
|
254
|
-
You
|
258
|
+
You can also include the continuation or break condition within the directive declaration:
|
255
259
|
|
256
260
|
```rblade
|
257
|
-
@
|
261
|
+
@for (user in users)
|
258
262
|
@next(user.type == 1)
|
259
263
|
|
260
|
-
<li>{{
|
264
|
+
<li>{{ user.name }}</li>
|
261
265
|
|
262
266
|
@break(user.number == 5)
|
263
|
-
@
|
264
|
-
```
|
265
|
-
|
266
|
-
<a name="the-loop-variable"></a>
|
267
|
-
### The Loop Variable
|
268
|
-
**TODO can/should we add this?**
|
269
|
-
While iterating through a `foreach` loop, a `$loop` variable will be available inside of your loop. This variable provides access to some useful bits of information such as the current loop index and whether this is the first or last iteration through the loop:
|
270
|
-
|
271
|
-
```rblade
|
272
|
-
@foreach ($users as $user)
|
273
|
-
@if ($loop->first)
|
274
|
-
This is the first iteration.
|
275
|
-
@endif
|
276
|
-
|
277
|
-
@if ($loop->last)
|
278
|
-
This is the last iteration.
|
279
|
-
@endif
|
280
|
-
|
281
|
-
<p>This is user {{ $user->id }}</p>
|
282
|
-
@endforeach
|
283
|
-
```
|
284
|
-
|
285
|
-
If you are in a nested loop, you may access the parent loop's `$loop` variable via the `parent` property:
|
286
|
-
|
287
|
-
```rblade
|
288
|
-
@foreach ($users as $user)
|
289
|
-
@foreach ($user->posts as $post)
|
290
|
-
@if ($loop->parent->first)
|
291
|
-
This is the first iteration of the parent loop.
|
292
|
-
@endif
|
293
|
-
@endforeach
|
294
|
-
@endforeach
|
267
|
+
@endFor
|
295
268
|
```
|
296
269
|
|
297
|
-
|
298
|
-
|
299
|
-
<div class="overflow-auto">
|
300
|
-
|
301
|
-
| Property | Description |
|
302
|
-
| ------------------ | ------------------------------------------------------ |
|
303
|
-
| `$loop->index` | The index of the current loop iteration (starts at 0). |
|
304
|
-
| `$loop->iteration` | The current loop iteration (starts at 1). |
|
305
|
-
| `$loop->remaining` | The iterations remaining in the loop. |
|
306
|
-
| `$loop->count` | The total number of items in the array being iterated. |
|
307
|
-
| `$loop->first` | Whether this is the first iteration through the loop. |
|
308
|
-
| `$loop->last` | Whether this is the last iteration through the loop. |
|
309
|
-
| `$loop->even` | Whether this is an even iteration through the loop. |
|
310
|
-
| `$loop->odd` | Whether this is an odd iteration through the loop. |
|
311
|
-
| `$loop->depth` | The nesting level of the current loop. |
|
312
|
-
| `$loop->parent` | When in a nested loop, the parent's loop variable. |
|
313
|
-
|
314
|
-
</div>
|
315
|
-
|
316
|
-
<a name="conditional-classes"></a>
|
270
|
+
<a name="conditional-classes-and-styles"></a>
|
317
271
|
### Conditional Classes & Styles
|
318
272
|
|
319
|
-
The `@class` directive conditionally adds CSS classes. The directive accepts a
|
273
|
+
The `@class` directive conditionally adds CSS classes. The directive accepts a Hash of classes where the key contains the class or classes you wish to add, and the value is a boolean expression:
|
320
274
|
|
321
275
|
```rblade
|
322
276
|
@ruby
|
323
277
|
isActive = false;
|
324
278
|
hasError = true;
|
325
|
-
@
|
279
|
+
@endRuby
|
326
280
|
|
327
281
|
<span @class({
|
328
282
|
"p-4": true,
|
@@ -334,12 +288,12 @@ The `@class` directive conditionally adds CSS classes. The directive accepts a `
|
|
334
288
|
<span class="p-4 text-gray-500 bg-red"></span>
|
335
289
|
```
|
336
290
|
|
337
|
-
Likewise, the `@style` directive
|
291
|
+
Likewise, the `@style` directive can be used to conditionally add inline CSS styles to an HTML element:
|
338
292
|
|
339
293
|
```rblade
|
340
294
|
@ruby
|
341
295
|
isActive = true;
|
342
|
-
@
|
296
|
+
@endRuby
|
343
297
|
|
344
298
|
<span @style({
|
345
299
|
"background-color: red": true,
|
@@ -352,7 +306,7 @@ Likewise, the `@style` directive may be used to conditionally add inline CSS sty
|
|
352
306
|
<a name="additional-attributes"></a>
|
353
307
|
### Additional Attributes
|
354
308
|
|
355
|
-
For convenience, you
|
309
|
+
For convenience, you can use the `@checked` directive to easily indicate if a given HTML checkbox input is "checked". This directive will print `checked` if the provided condition evaluates to `true`:
|
356
310
|
|
357
311
|
```rblade
|
358
312
|
<input type="checkbox"
|
@@ -361,7 +315,7 @@ For convenience, you may use the `@checked` directive to easily indicate if a gi
|
|
361
315
|
@checked(user.active)) />
|
362
316
|
```
|
363
317
|
|
364
|
-
Likewise, the `@selected` directive
|
318
|
+
Likewise, the `@selected` directive can be used to indicate if a given select option should be "selected":
|
365
319
|
|
366
320
|
```rblade
|
367
321
|
<select name="version">
|
@@ -369,17 +323,17 @@ Likewise, the `@selected` directive may be used to indicate if a given select op
|
|
369
323
|
<option value="{{ version }}" @selected(version == selectedVersion)>
|
370
324
|
{{ version }}
|
371
325
|
</option>
|
372
|
-
@
|
326
|
+
@endEach
|
373
327
|
</select>
|
374
328
|
```
|
375
329
|
|
376
|
-
Additionally, the `@disabled` directive
|
330
|
+
Additionally, the `@disabled` directive can be used to indicate if a given element should be "disabled":
|
377
331
|
|
378
332
|
```rblade
|
379
333
|
<button type="submit" @disabled(isDisabled)>Submit</button>
|
380
334
|
```
|
381
335
|
|
382
|
-
Moreover, the `@readonly` directive
|
336
|
+
Moreover, the `@readonly` directive can be used to indicate if a given element should be "readonly":
|
383
337
|
|
384
338
|
```rblade
|
385
339
|
<input type="email"
|
@@ -388,7 +342,7 @@ Moreover, the `@readonly` directive may be used to indicate if a given element s
|
|
388
342
|
@readonly(!user.isAdmin) />
|
389
343
|
```
|
390
344
|
|
391
|
-
In addition, the `@required` directive
|
345
|
+
In addition, the `@required` directive can be used to indicate if a given element should be "required":
|
392
346
|
|
393
347
|
```rblade
|
394
348
|
<input type="text"
|
@@ -400,7 +354,7 @@ In addition, the `@required` directive may be used to indicate if a given elemen
|
|
400
354
|
<a name="the-once-directive"></a>
|
401
355
|
### The `@once` Directive
|
402
356
|
|
403
|
-
The `@once` directive allows you to define a portion of the template that will only be evaluated once per rendering cycle. This
|
357
|
+
The `@once` directive allows you to define a portion of the template that will only be evaluated once per rendering cycle. This can be useful for pushing a given piece of JavaScript into the page's header using [stacks](#stacks). For example, if you are rendering a given [component](#components) within a loop, you may wish to only push the JavaScript to the header the first time the component is rendered:
|
404
358
|
|
405
359
|
```rblade
|
406
360
|
@once
|
@@ -446,20 +400,7 @@ In some situations, it's useful to embed Ruby code into your views. You can use
|
|
446
400
|
```rblade
|
447
401
|
@ruby
|
448
402
|
counter = 1;
|
449
|
-
@
|
450
|
-
```
|
451
|
-
|
452
|
-
**TODO add require?**
|
453
|
-
Or, if you only need to use PHP to import a class, you may use the `@use` directive:
|
454
|
-
|
455
|
-
```rblade
|
456
|
-
@use('App\Models\Flight')
|
457
|
-
```
|
458
|
-
|
459
|
-
A second argument may be provided to the `@use` directive to alias the imported class:
|
460
|
-
|
461
|
-
```php
|
462
|
-
@use('App\Models\Flight', 'FlightModel')
|
403
|
+
@endRuby
|
463
404
|
```
|
464
405
|
|
465
406
|
<a name="comments"></a>
|
@@ -478,7 +419,7 @@ Components are a way of including sub-views into your templates. To illustrate h
|
|
478
419
|
|
479
420
|
First, we will create a new `alert.rblade` file in the `app/views/components/forms` directory. Templates in the `app/views/components` directory and its subdirectories are are automatically discovered as components, so no further registration is required. Both `.rblade` and `.html.rblade` are valid extensions for RBlade components.
|
480
421
|
|
481
|
-
Once your component has been created, it
|
422
|
+
Once your component has been created, it can be rendered using its tag alias:
|
482
423
|
|
483
424
|
```rblade
|
484
425
|
<x-forms.alert/>
|
@@ -487,7 +428,7 @@ Once your component has been created, it may be rendered using its tag alias:
|
|
487
428
|
<a name="rendering-components"></a>
|
488
429
|
### Rendering Components
|
489
430
|
|
490
|
-
To display a component, you
|
431
|
+
To display a component, you can use a RBlade component tag within one of your RBlade templates. RBlade component tags start with the string `x-` followed by the kebab case name of the component class:
|
491
432
|
|
492
433
|
```rblade
|
493
434
|
{{-- Render the `alert` component in app/views/components/ --}}
|
@@ -538,7 +479,7 @@ You can define a component's data properties using a `@props` directive at the t
|
|
538
479
|
<div class="{{ type }}">{{ message }}</div>
|
539
480
|
```
|
540
481
|
|
541
|
-
The `@props` directive accepts a
|
482
|
+
The `@props` directive accepts a Hash where the key is the name of the attribute, and the value is the default value for the property. You can use the special `_required` value to represent a property with no default that must always be defined:
|
542
483
|
|
543
484
|
```rblade
|
544
485
|
{{-- This will give an error because the alert component requires a message propery --}}
|
@@ -555,7 +496,7 @@ All properties in the `@props` directive are automatically removed from `attribu
|
|
555
496
|
<a name="short-attribute-syntax"></a>
|
556
497
|
#### Short Attribute Syntax
|
557
498
|
|
558
|
-
When passing attributes to components, you
|
499
|
+
When passing attributes to components, you can also use a "short attribute" syntax. This is often convenient since attribute names frequently match the variable names they correspond to:
|
559
500
|
|
560
501
|
```rblade
|
561
502
|
{{-- Short attribute syntax... --}}
|
@@ -568,7 +509,7 @@ When passing attributes to components, you may also use a "short attribute" synt
|
|
568
509
|
<a name="escaping-attribute-rendering"></a>
|
569
510
|
#### Escaping Attribute Rendering
|
570
511
|
|
571
|
-
Since some JavaScript frameworks such as Alpine.js also use colon-prefixed attributes, you
|
512
|
+
Since some JavaScript frameworks such as Alpine.js also use colon-prefixed attributes, you can use a double colon (`::`) prefix to inform RBlade that the attribute is not a Ruby expression. For example, given the following component:
|
572
513
|
|
573
514
|
```rblade
|
574
515
|
<x-button ::class="{ danger: isDeleting }">
|
@@ -587,15 +528,13 @@ The following HTML will be rendered by RBlade:
|
|
587
528
|
<a name="component-attributes"></a>
|
588
529
|
### Component Attributes
|
589
530
|
|
590
|
-
We've already examined how to pass data attributes to a component; however, sometimes you
|
531
|
+
We've already examined how to pass data attributes to a component; however, sometimes you can need to specify additional HTML attributes, such as `class`, that are not part of the data required for a component to function. Typically, you want to pass these additional attributes down to the root element of the component template. For example, imagine we want to render an `alert` component like so:
|
591
532
|
|
592
533
|
```rblade
|
593
534
|
<x-alert type="error" :message class="mt-4"/>
|
594
535
|
```
|
595
536
|
|
596
|
-
|
597
|
-
|
598
|
-
All of the attributes that are not part of the component's constructor will automatically be added to the component's "attribute manager". This attribute manager is automatically made available to the component via the `attributes` variable. All of the attributes may be rendered within the component by printing this variable:
|
537
|
+
All of the attributes that are not part of the component's constructor will automatically be added to the component's "attribute manager". This attribute manager is automatically made available to the component via the `attributes` variable. All of the attributes can be rendered within the component by printing this variable:
|
599
538
|
|
600
539
|
```rblade
|
601
540
|
<div {{ attributes }}>
|
@@ -603,10 +542,10 @@ All of the attributes that are not part of the component's constructor will auto
|
|
603
542
|
</div>
|
604
543
|
```
|
605
544
|
|
606
|
-
<a name="default-merged-attributes"></a>
|
607
|
-
#### Default
|
545
|
+
<a name="default-and-merged-attributes"></a>
|
546
|
+
#### Default & Merged Attributes
|
608
547
|
|
609
|
-
Sometimes you
|
548
|
+
Sometimes you can need to specify default values for attributes or merge additional values into some of the component's attributes. To accomplish this, you can use the attribute manager's `merge` method. This method is particularly useful for defining a set of default CSS classes that should always be applied to a component:
|
610
549
|
|
611
550
|
```rblade
|
612
551
|
<div {{ attributes.merge({"class": "alert alert-#{type}"}) }}>
|
@@ -657,31 +596,22 @@ The rendered HTML of the `button` component in this example would be:
|
|
657
596
|
</button>
|
658
597
|
```
|
659
598
|
|
660
|
-
**Todo add prepends**
|
661
|
-
If you would like an attribute other than `class` or `style` to have its default value and injected values joined together, you can use the `prepends` method. In this example, the `data-controller` attribute will always begin with `profile-controller` and any additional injected `data-controller` values will be placed after this default value:
|
662
|
-
|
663
|
-
```rblade
|
664
|
-
<div {{ attributes.merge({"data-controller": attributes.prepends("profile-controller")}) }}>
|
665
|
-
{{ slot }}
|
666
|
-
</div>
|
667
|
-
```
|
668
|
-
|
669
599
|
<a name="conditionally-merge-classes"></a>
|
670
600
|
#### Conditionally Merge Classes
|
671
|
-
|
672
|
-
Sometimes you may wish to merge classes if a given condition is `true`. You can accomplish this via the `class` method, which accepts
|
601
|
+
|
602
|
+
Sometimes you may wish to merge classes if a given condition is `true`. You can accomplish this via the `class` method, which accepts a Hash of classes where the array key contains the class or classes you wish to add, while the value is a boolean expression:
|
673
603
|
|
674
604
|
```rblade
|
675
|
-
<div {{
|
676
|
-
{{
|
605
|
+
<div {{ attributes.class({'p-4': true, 'bg-red': hasError}) }}>
|
606
|
+
{{ message }}
|
677
607
|
</div>
|
678
608
|
```
|
679
609
|
|
680
610
|
If you need to merge other attributes onto your component, you can chain the `merge` method onto the `class` method:
|
681
611
|
|
682
612
|
```rblade
|
683
|
-
<button {{
|
684
|
-
{{
|
613
|
+
<button {{ attributes.class({'bg-red': hasError}).merge({type: 'button'}) }}>
|
614
|
+
{{ slot }}
|
685
615
|
</button>
|
686
616
|
```
|
687
617
|
|
@@ -691,60 +621,37 @@ If you need to merge other attributes onto your component, you can chain the `me
|
|
691
621
|
<a name="filtering-attributes"></a>
|
692
622
|
#### Retrieving and Filtering Attributes
|
693
623
|
|
694
|
-
|
695
|
-
|
696
|
-
You may filter attributes using the `filter` method. This method accepts a closure which should return `true` if you wish to retain the attribute in the attribute bag:
|
697
|
-
|
698
|
-
```rblade
|
699
|
-
{{ $attributes->filter(fn (string $value, string $key) => $key == 'foo') }}
|
700
|
-
```
|
701
|
-
|
702
|
-
For convenience, you may use the `whereStartsWith` method to retrieve all attributes whose keys begin with a given string:
|
624
|
+
The attributes manager is a wrapper around the Ruby Hash class. Unless explicitly overwritten, any methods called on the attributes manager will call that same method on the underlying Hash.
|
703
625
|
|
704
|
-
|
705
|
-
{{ $attributes->whereStartsWith('wire:model') }}
|
706
|
-
```
|
707
|
-
|
708
|
-
Conversely, the `whereDoesntStartWith` method may be used to exclude all attributes whose keys begin with a given string:
|
626
|
+
You can filter attributes using the `filter` and `slice` methods. These methods call `filter` and `slice` on the underlying Hash and return a new attributes manager with the result.
|
709
627
|
|
710
628
|
```rblade
|
711
|
-
{{
|
629
|
+
{{ attributes.filter { |k, v| k == 'foo'} }}
|
630
|
+
{{ attributes.slice :foo }}
|
712
631
|
```
|
713
632
|
|
714
|
-
|
633
|
+
If you would like to check if an attribute is present on the component, you can use the `has?` method. This method accepts the attribute name as its only argument and returns a boolean indicating whether or not the attribute is present:
|
715
634
|
|
716
635
|
```rblade
|
717
|
-
|
718
|
-
```
|
719
|
-
|
720
|
-
If you would like to check if an attribute is present on the component, you may use the `has` method. This method accepts the attribute name as its only argument and returns a boolean indicating whether or not the attribute is present:
|
721
|
-
|
722
|
-
```rblade
|
723
|
-
@if ($attributes->has('class'))
|
636
|
+
@if (attributes.has?(:class))
|
724
637
|
<div>Class attribute is present</div>
|
725
|
-
@
|
638
|
+
@endIf
|
726
639
|
```
|
727
640
|
|
728
|
-
If
|
641
|
+
If multiple parameters are passed to the `has?` method, the method will determine if all of the given attributes are present on the component:
|
729
642
|
|
730
643
|
```rblade
|
731
|
-
@if (
|
644
|
+
@if (attributes.has?('name', 'class'))
|
732
645
|
<div>All of the attributes are present</div>
|
733
|
-
@
|
646
|
+
@endIf
|
734
647
|
```
|
735
648
|
|
736
|
-
The `
|
649
|
+
The `has_any?` method can be used to determine if any of the given attributes are present on the component:
|
737
650
|
|
738
651
|
```rblade
|
739
|
-
@if (
|
652
|
+
@if (attributes.has_any?('href', ':href', 'v-bind:href'))
|
740
653
|
<div>One of the attributes is present</div>
|
741
|
-
@
|
742
|
-
```
|
743
|
-
|
744
|
-
You may retrieve a specific attribute's value using the `get` method:
|
745
|
-
|
746
|
-
```rblade
|
747
|
-
{{ $attributes->get('class') }}
|
654
|
+
@endIf
|
748
655
|
```
|
749
656
|
|
750
657
|
<a name="slots"></a>
|
@@ -759,7 +666,7 @@ You will often need to pass additional content to your component via "slots". Th
|
|
759
666
|
</div>
|
760
667
|
```
|
761
668
|
|
762
|
-
We
|
669
|
+
We can pass content to the `slot` by injecting content into the component:
|
763
670
|
|
764
671
|
```rblade
|
765
672
|
<x-alert>
|
@@ -767,6 +674,9 @@ We may pass content to the `slot` by injecting content into the component:
|
|
767
674
|
</x-alert>
|
768
675
|
```
|
769
676
|
|
677
|
+
> [!NOTE]
|
678
|
+
> You can instead use `<//>` as the closing tag of RBlade components; however, this will bypass some of the template sanity checking that the compiler performs.
|
679
|
+
|
770
680
|
Sometimes a component may need to render multiple different slots in different locations within the component. Let's modify our alert component to allow for the injection of a "title" slot:
|
771
681
|
|
772
682
|
```rblade
|
@@ -778,7 +688,7 @@ Sometimes a component may need to render multiple different slots in different l
|
|
778
688
|
</div>
|
779
689
|
```
|
780
690
|
|
781
|
-
You
|
691
|
+
You can define the content of the named slot using the `x-slot` tag. Any content not within an explicit `x-slot` tag will be passed to the component in the `slot` variable:
|
782
692
|
|
783
693
|
```xml
|
784
694
|
<x-alert>
|
@@ -789,48 +699,25 @@ You may define the content of the named slot using the `x-slot` tag. Any content
|
|
789
699
|
<strong>Whoops!</strong> Something went wrong!
|
790
700
|
</x-alert>
|
791
701
|
```
|
792
|
-
|
793
|
-
|
702
|
+
|
703
|
+
The slot object extends the String interface, so you can invoke a slot's `empty?` method to determine if the slot contains content:
|
794
704
|
|
795
705
|
```rblade
|
796
706
|
<span class="alert-title">{{ title }}</span>
|
797
707
|
|
798
708
|
<div class="alert alert-danger">
|
799
|
-
@if (slot.
|
709
|
+
@if (slot.empty?)
|
800
710
|
This is default content if the slot is empty.
|
801
711
|
@else
|
802
712
|
{{ slot }}
|
803
|
-
@
|
713
|
+
@endIf
|
804
714
|
</div>
|
805
715
|
```
|
806
716
|
|
807
|
-
Additionally, the `hasActualContent` method may be used to determine if the slot contains any "actual" content that is not an HTML comment:
|
808
|
-
|
809
|
-
```rblade
|
810
|
-
@if (slot.hasActualContent)
|
811
|
-
The scope has non-comment content.
|
812
|
-
@endif
|
813
|
-
```
|
814
|
-
|
815
|
-
<a name="scoped-slots"></a>
|
816
|
-
#### Scoped Slots
|
817
|
-
**TODO can we do this easily?**
|
818
|
-
If you have used a JavaScript framework such as Vue, you may be familiar with "scoped slots", which allow you to access data or methods from the component within your slot. You may achieve similar behavior in Laravel by defining public methods or properties on your component and accessing the component within your slot via the `$component` variable. In this example, we will assume that the `x-alert` component has a public `formatAlert` method defined on its component class:
|
819
|
-
|
820
|
-
```rblade
|
821
|
-
<x-alert>
|
822
|
-
<x-slot:title>
|
823
|
-
{{ $component->formatAlert('Server Error') }}
|
824
|
-
</x-slot>
|
825
|
-
|
826
|
-
<strong>Whoops!</strong> Something went wrong!
|
827
|
-
</x-alert>
|
828
|
-
```
|
829
|
-
|
830
717
|
<a name="slot-attributes"></a>
|
831
718
|
#### Slot Attributes
|
832
719
|
|
833
|
-
Like RBlade components, you
|
720
|
+
Like RBlade components, you can assign additional [attributes](#component-attributes) to slots such as CSS class names:
|
834
721
|
|
835
722
|
```xml
|
836
723
|
<x-card class="shadow-sm">
|
@@ -846,17 +733,16 @@ Like RBlade components, you may assign additional [attributes](#component-attrib
|
|
846
733
|
</x-card>
|
847
734
|
```
|
848
735
|
|
849
|
-
To interact with slot attributes, you
|
736
|
+
To interact with slot attributes, you can access the `attributes` property of the slot's variable. For more information on how to interact with attributes, please consult the documentation on [component attributes](#component-attributes):
|
850
737
|
|
851
|
-
**TODO allow class to contain a string**
|
852
738
|
```rblade
|
853
739
|
@props({
|
854
740
|
"heading": _required,
|
855
741
|
"footer": _required,
|
856
742
|
})
|
857
743
|
|
858
|
-
<div {{ attributes.class(
|
859
|
-
<h1 {{ heading.attributes
|
744
|
+
<div {{ attributes.class('border') }}>
|
745
|
+
<h1 {{ heading.attributes.class('text-lg') }}>
|
860
746
|
{{ heading }}
|
861
747
|
</h1>
|
862
748
|
|
@@ -868,14 +754,16 @@ To interact with slot attributes, you may access the `attributes` property of th
|
|
868
754
|
</div>
|
869
755
|
```
|
870
756
|
|
871
|
-
<a name="
|
872
|
-
|
873
|
-
|
874
|
-
Sometimes you may
|
757
|
+
<a name="conditional-rendering"></a>
|
758
|
+
#### Conditional Rendering
|
759
|
+
|
760
|
+
Sometimes, you may wish to return early from a component without printing anything. For example, if you make an error component and no errors are passed in as properties, you might want to skip rendering. You can use the `@shouldRender` directive anywhere within a component to prevent the component from being rendered:
|
875
761
|
|
876
762
|
```rblade
|
877
|
-
|
878
|
-
|
763
|
+
{{-- components/error.rblade --}}
|
764
|
+
@props({errors: []})
|
765
|
+
@shouldRender(errors.present?)
|
766
|
+
...
|
879
767
|
```
|
880
768
|
|
881
769
|
<a name="registering-additional-component-directories"></a>
|
@@ -895,33 +783,6 @@ RBlade::ComponentStore.add_path(Rails.root.join("app", "views", "partials"), "pa
|
|
895
783
|
|
896
784
|
If multiple directories are registered with the same namespace, RBlade will search for components in all the directories in the order they were registered.
|
897
785
|
|
898
|
-
<a name="manually-registering-components"></a>
|
899
|
-
### Manually Registering Components
|
900
|
-
**TODO would this be useful? It'd be useful for testing...**
|
901
|
-
> [!WARNING]
|
902
|
-
> The following documentation on manually registering components is primarily applicable to those who are writing Laravel packages that include view components. If you are not writing a package, this portion of the component documentation may not be relevant to you.
|
903
|
-
|
904
|
-
When writing components for your own application, components are automatically discovered within the `app/View/Components` directory and `resources/views/components` directory.
|
905
|
-
|
906
|
-
However, if you are building a package that utilizes Blade components or placing components in non-conventional directories, you will need to manually register your component class and its HTML tag alias so that Laravel knows where to find the component. You should typically register your components in the `boot` method of your package's service provider:
|
907
|
-
|
908
|
-
use Illuminate\Support\Facades\Blade;
|
909
|
-
use VendorPackage\View\Components\AlertComponent;
|
910
|
-
|
911
|
-
/**
|
912
|
-
* Bootstrap your package's services.
|
913
|
-
*/
|
914
|
-
public function boot(): void
|
915
|
-
{
|
916
|
-
Blade::component('package-alert', AlertComponent::class);
|
917
|
-
}
|
918
|
-
|
919
|
-
Once your component has been registered, it may be rendered using its tag alias:
|
920
|
-
|
921
|
-
```rblade
|
922
|
-
<x-package-alert/>
|
923
|
-
```
|
924
|
-
|
925
786
|
<a name="anonymous-index-components"></a>
|
926
787
|
### Index Components
|
927
788
|
|
@@ -949,64 +810,26 @@ However, when an `index.rblade` template exists in a directory, it will be rende
|
|
949
810
|
/app/views/components/accordion/item.rblade
|
950
811
|
```
|
951
812
|
|
952
|
-
<a name="
|
953
|
-
|
954
|
-
**TODO this? It might be complicated**
|
955
|
-
Sometimes you may want to access data from a parent component inside a child component. In these cases, you may use the `@aware` directive. For example, imagine we are building a complex menu component consisting of a parent `<x-menu>` and child `<x-menu.item>`:
|
956
|
-
|
957
|
-
```rblade
|
958
|
-
<x-menu color="purple">
|
959
|
-
<x-menu.item>...</x-menu.item>
|
960
|
-
<x-menu.item>...</x-menu.item>
|
961
|
-
</x-menu>
|
962
|
-
```
|
963
|
-
|
964
|
-
The `<x-menu>` component may have an implementation like the following:
|
965
|
-
|
966
|
-
```rblade
|
967
|
-
<!-- /resources/views/components/menu/index.rblade -->
|
968
|
-
|
969
|
-
@props(['color' => 'gray'])
|
813
|
+
<a name="forms"></a>
|
814
|
+
## Forms
|
970
815
|
|
971
|
-
|
972
|
-
{{ $slot }}
|
973
|
-
</ul>
|
974
|
-
```
|
816
|
+
### Old Input
|
975
817
|
|
976
|
-
|
818
|
+
The Rails `params` Hash is available in views and components. However, the `@old` directive is a useful shortcut that will output the old input value for a given key:
|
977
819
|
|
978
820
|
```rblade
|
979
|
-
|
980
|
-
|
981
|
-
@aware(['color' => 'gray'])
|
982
|
-
|
983
|
-
<li {{ $attributes->merge(['class' => 'text-'.$color.'-800']) }}>
|
984
|
-
{{ $slot }}
|
985
|
-
</li>
|
821
|
+
<input type="text" name="email" value="@old('email', user.email)">
|
986
822
|
```
|
987
823
|
|
988
|
-
|
989
|
-
> The `@aware` directive can not access parent data that is not explicitly passed to the parent component via HTML attributes. Default `@props` values that are not explicitly passed to the parent component can not be accessed by the `@aware` directive.
|
990
|
-
|
991
|
-
<a name="forms"></a>
|
992
|
-
## Forms
|
993
|
-
|
994
|
-
<a name="csrf-field"></a>
|
995
|
-
### CSRF Field
|
996
|
-
**TODO I don't think we need this?**
|
997
|
-
Anytime you define an HTML form in your application, you should include a hidden CSRF token field in the form so that [the CSRF protection](/docs/{{version}}/csrf) middleware can validate the request. You may use the `@csrf` Blade directive to generate the token field:
|
824
|
+
The first parameter is the name of the previous input, and the second input is the default if the key isn't present in `params`. The previous example is the equivalent of calling `params.fetch`:
|
998
825
|
|
999
826
|
```rblade
|
1000
|
-
<
|
1001
|
-
@csrf
|
1002
|
-
|
1003
|
-
...
|
1004
|
-
</form>
|
827
|
+
<input type="text" name="email" value="{{ params.fetch(:email, user.email) }}">
|
1005
828
|
```
|
1006
829
|
|
1007
830
|
<a name="method-field"></a>
|
1008
831
|
### Method Field
|
1009
|
-
|
832
|
+
|
1010
833
|
Since HTML forms can't make `PUT`, `PATCH`, or `DELETE` requests, you will need to add a hidden `_method` field to spoof these HTTP verbs. The `@method` RBlade directive can create this field for you:
|
1011
834
|
|
1012
835
|
```rblade
|
@@ -1017,52 +840,8 @@ Since HTML forms can't make `PUT`, `PATCH`, or `DELETE` requests, you will need
|
|
1017
840
|
</form>
|
1018
841
|
```
|
1019
842
|
|
1020
|
-
|
1021
|
-
### Validation Errors
|
1022
|
-
**TODO this**
|
1023
|
-
The `@error` directive may be used to quickly check if [validation error messages](/docs/{{version}}/validation#quick-displaying-the-validation-errors) exist for a given attribute. Within an `@error` directive, you may echo the `$message` variable to display the error message:
|
1024
|
-
|
1025
|
-
```rblade
|
1026
|
-
<!-- /resources/views/post/create.rblade -->
|
1027
|
-
|
1028
|
-
<label for="title">Post Title</label>
|
1029
|
-
|
1030
|
-
<input id="title"
|
1031
|
-
type="text"
|
1032
|
-
class="@error('title') is-invalid @enderror">
|
1033
|
-
|
1034
|
-
@error('title')
|
1035
|
-
<div class="alert alert-danger">{{ $message }}</div>
|
1036
|
-
@enderror
|
1037
|
-
```
|
1038
|
-
|
1039
|
-
Since the `@error` directive compiles to an "if" statement, you may use the `@else` directive to render content when there is not an error for an attribute:
|
1040
|
-
|
1041
|
-
```rblade
|
1042
|
-
<!-- /resources/views/auth.rblade -->
|
1043
|
-
|
1044
|
-
<label for="email">Email address</label>
|
1045
|
-
|
1046
|
-
<input id="email"
|
1047
|
-
type="email"
|
1048
|
-
class="@error('email') is-invalid @else is-valid @enderror">
|
1049
|
-
```
|
1050
|
-
|
1051
|
-
You may pass [the name of a specific error bag](/docs/{{version}}/validation#named-error-bags) as the second parameter to the `@error` directive to retrieve validation error messages on pages containing multiple forms:
|
1052
|
-
|
1053
|
-
```rblade
|
1054
|
-
<!-- /resources/views/auth.rblade -->
|
1055
|
-
|
1056
|
-
<label for="email">Email address</label>
|
1057
|
-
|
1058
|
-
<input id="email"
|
1059
|
-
type="email"
|
1060
|
-
class="@error('email', 'login') is-invalid @enderror">
|
843
|
+
Alternatively, you can use the dedicated directives for each method: `@put`, `@patch`, or `@delete`.
|
1061
844
|
|
1062
|
-
@error('email', 'login')
|
1063
|
-
<div class="alert alert-danger">{{ $message }}</div>
|
1064
|
-
@enderror
|
1065
|
-
```
|
1066
845
|
|
1067
846
|
<a name="stacks"></a>
|
1068
847
|
## Stacks
|
@@ -1072,18 +851,18 @@ RBlade allows you to push to named stacks which can be rendered elsewhere in ano
|
|
1072
851
|
```rblade
|
1073
852
|
@push('scripts')
|
1074
853
|
<script src="/example.js"></script>
|
1075
|
-
@
|
854
|
+
@endPush
|
1076
855
|
```
|
1077
856
|
|
1078
|
-
If you would like to `@push` content if a given boolean expression evaluates to `true`, you
|
1079
|
-
|
857
|
+
If you would like to `@push` content if a given boolean expression evaluates to `true`, you can use the `@pushif` directive:
|
858
|
+
|
1080
859
|
```rblade
|
1081
860
|
@pushIf(shouldPush, 'scripts')
|
1082
861
|
<script src="/example.js"></script>
|
1083
862
|
@endPushIf
|
1084
863
|
```
|
1085
864
|
|
1086
|
-
You
|
865
|
+
You can push to a stack as many times as needed. To render the complete stack contents, pass the name of the stack to the `@stack` directive:
|
1087
866
|
|
1088
867
|
```rblade
|
1089
868
|
<head>
|
@@ -1098,153 +877,11 @@ If you would like to prepend content onto the beginning of a stack, you should u
|
|
1098
877
|
```rblade
|
1099
878
|
@push('scripts')
|
1100
879
|
This will be second...
|
1101
|
-
@
|
880
|
+
@endPush
|
1102
881
|
|
1103
882
|
// Later...
|
1104
883
|
|
1105
884
|
@prepend('scripts')
|
1106
885
|
This will be first...
|
1107
|
-
@
|
1108
|
-
```
|
1109
|
-
|
1110
|
-
<a name="rendering-blade-fragments"></a>
|
1111
|
-
## Rendering Blade Fragments
|
1112
|
-
**TODO this?**
|
1113
|
-
When using frontend frameworks such as [Turbo](https://turbo.hotwired.dev/) and [htmx](https://htmx.org/), you may occasionally need to only return a portion of a Blade template within your HTTP response. Blade "fragments" allow you to do just that. To get started, place a portion of your Blade template within `@fragment` and `@endfragment` directives:
|
1114
|
-
|
1115
|
-
```rblade
|
1116
|
-
@fragment('user-list')
|
1117
|
-
<ul>
|
1118
|
-
@foreach ($users as $user)
|
1119
|
-
<li>{{ $user->name }}</li>
|
1120
|
-
@endforeach
|
1121
|
-
</ul>
|
1122
|
-
@endfragment
|
1123
|
-
```
|
1124
|
-
|
1125
|
-
Then, when rendering the view that utilizes this template, you may invoke the `fragment` method to specify that only the specified fragment should be included in the outgoing HTTP response:
|
1126
|
-
|
1127
|
-
```php
|
1128
|
-
return view('dashboard', ['users' => $users])->fragment('user-list');
|
1129
|
-
```
|
1130
|
-
|
1131
|
-
The `fragmentIf` method allows you to conditionally return a fragment of a view based on a given condition. Otherwise, the entire view will be returned:
|
1132
|
-
|
1133
|
-
```php
|
1134
|
-
return view('dashboard', ['users' => $users])
|
1135
|
-
->fragmentIf($request->hasHeader('HX-Request'), 'user-list');
|
1136
|
-
```
|
1137
|
-
|
1138
|
-
The `fragments` and `fragmentsIf` methods allow you to return multiple view fragments in the response. The fragments will be concatenated together:
|
1139
|
-
|
1140
|
-
```php
|
1141
|
-
view('dashboard', ['users' => $users])
|
1142
|
-
->fragments(['user-list', 'comment-list']);
|
1143
|
-
|
1144
|
-
view('dashboard', ['users' => $users])
|
1145
|
-
->fragmentsIf(
|
1146
|
-
$request->hasHeader('HX-Request'),
|
1147
|
-
['user-list', 'comment-list']
|
1148
|
-
);
|
1149
|
-
```
|
1150
|
-
|
1151
|
-
<a name="extending-blade"></a>
|
1152
|
-
## Extending Blade
|
1153
|
-
**TODO this?**
|
1154
|
-
Blade allows you to define your own custom directives using the `directive` method. When the Blade compiler encounters the custom directive, it will call the provided callback with the expression that the directive contains.
|
1155
|
-
|
1156
|
-
The following example creates a `@datetime($var)` directive which formats a given `$var`, which should be an instance of `DateTime`:
|
1157
|
-
|
1158
|
-
<?php
|
1159
|
-
|
1160
|
-
namespace App\Providers;
|
1161
|
-
|
1162
|
-
use Illuminate\Support\Facades\Blade;
|
1163
|
-
use Illuminate\Support\ServiceProvider;
|
1164
|
-
|
1165
|
-
class AppServiceProvider extends ServiceProvider
|
1166
|
-
{
|
1167
|
-
/**
|
1168
|
-
* Register any application services.
|
1169
|
-
*/
|
1170
|
-
public function register(): void
|
1171
|
-
{
|
1172
|
-
// ...
|
1173
|
-
}
|
1174
|
-
|
1175
|
-
/**
|
1176
|
-
* Bootstrap any application services.
|
1177
|
-
*/
|
1178
|
-
public function boot(): void
|
1179
|
-
{
|
1180
|
-
Blade::directive('datetime', function (string $expression) {
|
1181
|
-
return "<?php echo ($expression)->format('m/d/Y H:i'); ?>";
|
1182
|
-
});
|
1183
|
-
}
|
1184
|
-
}
|
1185
|
-
|
1186
|
-
As you can see, we will chain the `format` method onto whatever expression is passed into the directive. So, in this example, the final PHP generated by this directive will be:
|
1187
|
-
|
1188
|
-
<?php echo ($var)->format('m/d/Y H:i'); ?>
|
1189
|
-
|
1190
|
-
> [!WARNING]
|
1191
|
-
> After updating the logic of a Blade directive, you will need to delete all of the cached Blade views. The cached Blade views may be removed using the `view:clear` Artisan command.
|
1192
|
-
|
1193
|
-
<a name="custom-echo-handlers"></a>
|
1194
|
-
### Custom Echo Handlers
|
1195
|
-
**TODO this? Need to do something similar for attribute manager anyway**
|
1196
|
-
If you attempt to "echo" an object using Blade, the object's `__toString` method will be invoked. The [`__toString`](https://www.php.net/manual/en/language.oop5.magic.php#object.tostring) method is one of PHP's built-in "magic methods". However, sometimes you may not have control over the `__toString` method of a given class, such as when the class that you are interacting with belongs to a third-party library.
|
1197
|
-
|
1198
|
-
In these cases, Blade allows you to register a custom echo handler for that particular type of object. To accomplish this, you should invoke Blade's `stringable` method. The `stringable` method accepts a closure. This closure should type-hint the type of object that it is responsible for rendering. Typically, the `stringable` method should be invoked within the `boot` method of your application's `AppServiceProvider` class:
|
1199
|
-
|
1200
|
-
use Illuminate\Support\Facades\Blade;
|
1201
|
-
use Money\Money;
|
1202
|
-
|
1203
|
-
/**
|
1204
|
-
* Bootstrap any application services.
|
1205
|
-
*/
|
1206
|
-
public function boot(): void
|
1207
|
-
{
|
1208
|
-
Blade::stringable(function (Money $money) {
|
1209
|
-
return $money->formatTo('en_GB');
|
1210
|
-
});
|
1211
|
-
}
|
1212
|
-
|
1213
|
-
Once your custom echo handler has been defined, you may simply echo the object in your Blade template:
|
1214
|
-
|
1215
|
-
```rblade
|
1216
|
-
Cost: {{ $money }}
|
1217
|
-
```
|
1218
|
-
|
1219
|
-
<a name="custom-if-statements"></a>
|
1220
|
-
### Custom If Statements
|
1221
|
-
**TODO this**
|
1222
|
-
Programming a custom directive is sometimes more complex than necessary when defining simple, custom conditional statements. For that reason, Blade provides a `Blade::if` method which allows you to quickly define custom conditional directives using closures. For example, let's define a custom conditional that checks the configured default "disk" for the application. We may do this in the `boot` method of our `AppServiceProvider`:
|
1223
|
-
|
1224
|
-
use Illuminate\Support\Facades\Blade;
|
1225
|
-
|
1226
|
-
/**
|
1227
|
-
* Bootstrap any application services.
|
1228
|
-
*/
|
1229
|
-
public function boot(): void
|
1230
|
-
{
|
1231
|
-
Blade::if('disk', function (string $value) {
|
1232
|
-
return config('filesystems.default') === $value;
|
1233
|
-
});
|
1234
|
-
}
|
1235
|
-
|
1236
|
-
Once the custom conditional has been defined, you can use it within your templates:
|
1237
|
-
|
1238
|
-
```rblade
|
1239
|
-
@disk('local')
|
1240
|
-
<!-- The application is using the local disk... -->
|
1241
|
-
@elsedisk('s3')
|
1242
|
-
<!-- The application is using the s3 disk... -->
|
1243
|
-
@else
|
1244
|
-
<!-- The application is using some other disk... -->
|
1245
|
-
@enddisk
|
1246
|
-
|
1247
|
-
@unlessdisk('local')
|
1248
|
-
<!-- The application is not using the local disk... -->
|
1249
|
-
@enddisk
|
886
|
+
@endPrepend
|
1250
887
|
```
|