rblade 0.5.0 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -1
- data/README.md +107 -502
- data/lib/rblade/compiler/compiles_components.rb +1 -1
- data/lib/rblade/compiler/compiles_ruby.rb +1 -1
- data/lib/rblade/compiler/compiles_statements.rb +21 -13
- data/lib/rblade/compiler/statements/compiles_conditionals.rb +40 -0
- data/lib/rblade/compiler/statements/compiles_form.rb +48 -0
- data/lib/rblade/compiler/statements/compiles_stacks.rb +39 -15
- data/lib/rblade/compiler/tokenizes_statements.rb +13 -2
- data/lib/rblade/component_store.rb +1 -1
- data/lib/rblade/helpers/attributes_manager.rb +30 -6
- data/lib/rblade/helpers/slot_manager.rb +8 -0
- data/rblade.gemspec +1 -1
- metadata +7 -7
- data/TODO.md +0 -2
data/README.md
CHANGED
@@ -1,12 +1,41 @@
|
|
1
1
|
# RBlade Templates
|
2
2
|
|
3
|
-
- [
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
3
|
+
- [RBlade Templates](#rblade-templates)
|
4
|
+
* [Introduction](#introduction)
|
5
|
+
* [Displaying Data](#displaying-data)
|
6
|
+
+ [HTML Entity Encoding](#html-entity-encoding)
|
7
|
+
+ [Blade and JavaScript Frameworks](#blade-and-javascript-frameworks)
|
8
|
+
- [The `@verbatim` Directive](#the-at-verbatim-directive)
|
9
|
+
* [RBlade Directives](#rblade-directives)
|
10
|
+
+ [If Statements](#if-statements)
|
11
|
+
- [Environment Directives](#environment-directives)
|
12
|
+
+ [Case Statements](#case-statements)
|
13
|
+
+ [Loops](#loops)
|
14
|
+
+ [Conditional Classes & Styles](#conditional-classes-and-styles)
|
15
|
+
+ [Additional Attributes](#additional-attributes)
|
16
|
+
+ [The `@once` Directive](#the-once-directive)
|
17
|
+
+ [Raw Ruby](#raw-ruby)
|
18
|
+
+ [Comments](#comments)
|
19
|
+
* [Components](#components)
|
20
|
+
+ [Rendering Components](#rendering-components)
|
21
|
+
- [Namespaces](#namespaces)
|
22
|
+
+ [Passing Data to Components](#passing-data-to-components)
|
23
|
+
- [Component Properties](#component-properties)
|
24
|
+
- [Short Attribute Syntax](#short-attribute-syntax)
|
25
|
+
- [Escaping Attribute Rendering](#escaping-attribute-rendering)
|
26
|
+
+ [Component Attributes](#component-attributes)
|
27
|
+
- [Default & Merged Attributes](#default-and-merged-attributes)
|
28
|
+
- [Non-Class Attribute Merging](#non-class-attribute-merging)
|
29
|
+
- [Conditionally Merge Classes](#conditionally-merge-classes)
|
30
|
+
- [Retrieving and Filtering Attributes](#retrieving-and-filtering-attributes)
|
31
|
+
+ [Slots](#slots)
|
32
|
+
- [Slot Attributes](#slot-attributes)
|
33
|
+
+ [Registering Additional Component Directories](#registering-additional-component-directories)
|
34
|
+
+ [Index Components](#index-components)
|
35
|
+
* [Forms](#forms)
|
36
|
+
+ [Old Input](#old-input)
|
37
|
+
+ [Method Field](#method-field)
|
38
|
+
* [Stacks](#stacks)
|
10
39
|
|
11
40
|
<a name="introduction"></a>
|
12
41
|
## Introduction
|
@@ -98,57 +127,28 @@ In addition to template inheritance and displaying data, RBlade also provides co
|
|
98
127
|
<a name="if-statements"></a>
|
99
128
|
### If Statements
|
100
129
|
|
101
|
-
You may construct `if` statements using the `@if`, `@
|
130
|
+
You may construct `if` statements using the `@if`, `@elseIf`, `@else`, `@endIf`, `@unless`, and `@endUnless` directives. These directives function identically to their Ruby counterparts:
|
102
131
|
|
103
132
|
```rblade
|
104
133
|
@unless(records.nil?)
|
105
134
|
@if (records.count === 1)
|
106
135
|
I have one record!
|
107
|
-
@
|
136
|
+
@elseIf (records.count > 1)
|
108
137
|
I have multiple records!
|
109
138
|
@else
|
110
139
|
I don't have any records!
|
111
|
-
@
|
112
|
-
@
|
140
|
+
@endIf
|
141
|
+
@endUnless
|
113
142
|
```
|
114
143
|
|
115
|
-
In addition to the conditional directives
|
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
|
140
|
-
```
|
141
|
-
|
142
|
-
If needed, you may specify the authentication guard that should be checked when using the `@auth` and `@guest` directives:
|
144
|
+
In addition to the conditional directives above, the `@blank?`, `defined?`, `@empty?`, `@nil?` and `@present` directives may be used as convenient shortcuts:
|
143
145
|
|
144
146
|
```rblade
|
145
|
-
@
|
146
|
-
//
|
147
|
-
@
|
148
|
-
|
149
|
-
@
|
150
|
-
// The user is not authenticated...
|
151
|
-
@endguest
|
147
|
+
@present?(records)
|
148
|
+
// records is defined and is not nil
|
149
|
+
@else
|
150
|
+
// These directives are shortcuts to if statements so @else may be used
|
151
|
+
@endempty?
|
152
152
|
```
|
153
153
|
|
154
154
|
<a name="environment-directives"></a>
|
@@ -159,7 +159,7 @@ You may check if the application is running in the production environment using
|
|
159
159
|
```rblade
|
160
160
|
@production
|
161
161
|
// Production specific content...
|
162
|
-
@
|
162
|
+
@endProduction
|
163
163
|
```
|
164
164
|
|
165
165
|
Or, you may determine if the application is running in a specific environment using the `@env` directive:
|
@@ -167,30 +167,17 @@ Or, you may determine if the application is running in a specific environment us
|
|
167
167
|
```rblade
|
168
168
|
@env('staging')
|
169
169
|
// The application is running in "staging"...
|
170
|
-
@
|
170
|
+
@endEnv
|
171
171
|
|
172
172
|
@env(['staging', 'production'])
|
173
173
|
// 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
|
174
|
+
@endEnv
|
188
175
|
```
|
189
176
|
|
190
177
|
<a name="switch-statements"></a>
|
191
178
|
### Case Statements
|
192
179
|
|
193
|
-
Case statements can be constructed using the `@case`, `@when`, `@else` and `@
|
180
|
+
Case statements can be constructed using the `@case`, `@when`, `@else` and `@endCase` directives:
|
194
181
|
|
195
182
|
```rblade
|
196
183
|
@case(i)
|
@@ -200,7 +187,7 @@ Case statements can be constructed using the `@case`, `@when`, `@else` and `@end
|
|
200
187
|
Second case...
|
201
188
|
@else
|
202
189
|
Default case...
|
203
|
-
@
|
190
|
+
@endCase
|
204
191
|
```
|
205
192
|
|
206
193
|
<a name="loops"></a>
|
@@ -211,28 +198,28 @@ In addition to conditional statements, RBlade provides simple directives for wor
|
|
211
198
|
```rblade
|
212
199
|
@for (i in 0...10)
|
213
200
|
The current value is {{ i }}
|
214
|
-
@
|
201
|
+
@endFor
|
215
202
|
|
216
203
|
{{-- Compiles to users.each do |user| ... --}}
|
217
204
|
@each (user in users)
|
218
205
|
<p>This is user {{ user.id }}</p>
|
219
|
-
@
|
206
|
+
@endEach
|
220
207
|
|
221
|
-
@
|
208
|
+
@forElse (name in [])
|
222
209
|
<li>{{ name }}</li>
|
223
210
|
@empty
|
224
211
|
<p>No names</p>
|
225
|
-
@
|
212
|
+
@endForElse
|
226
213
|
|
227
|
-
@
|
214
|
+
@eachElse (user in users)
|
228
215
|
<li>{{ user.name }}</li>
|
229
216
|
@empty
|
230
217
|
<p>No users</p>
|
231
|
-
@
|
218
|
+
@endEachElse
|
232
219
|
|
233
220
|
@while (true)
|
234
221
|
<p>I'm looping forever.</p>
|
235
|
-
@
|
222
|
+
@endWhile
|
236
223
|
```
|
237
224
|
|
238
225
|
When using loops you can also skip the current iteration or end the loop using the `@next` and `@break` directives:
|
@@ -241,79 +228,29 @@ When using loops you can also skip the current iteration or end the loop using t
|
|
241
228
|
for (user in users)
|
242
229
|
@if (user.type == 1)
|
243
230
|
@next
|
244
|
-
@
|
231
|
+
@endIf
|
245
232
|
|
246
233
|
<li>{{ user.name }}</li>
|
247
234
|
|
248
235
|
@if (user.number == 5)
|
249
236
|
@break
|
250
|
-
@
|
251
|
-
@
|
237
|
+
@endIf
|
238
|
+
@endFor
|
252
239
|
```
|
253
240
|
|
254
241
|
You may also include the continuation or break condition within the directive declaration:
|
255
242
|
|
256
243
|
```rblade
|
257
|
-
@
|
244
|
+
@for (user in users)
|
258
245
|
@next(user.type == 1)
|
259
246
|
|
260
|
-
<li>{{
|
247
|
+
<li>{{ user.name }}</li>
|
261
248
|
|
262
249
|
@break(user.number == 5)
|
263
|
-
@
|
250
|
+
@endFor
|
264
251
|
```
|
265
252
|
|
266
|
-
<a name="
|
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
|
295
|
-
```
|
296
|
-
|
297
|
-
The `$loop` variable also contains a variety of other useful properties:
|
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>
|
253
|
+
<a name="conditional-classes-and-styles"></a>
|
317
254
|
### Conditional Classes & Styles
|
318
255
|
|
319
256
|
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:
|
@@ -322,7 +259,7 @@ The `@class` directive conditionally adds CSS classes. The directive accepts a `
|
|
322
259
|
@ruby
|
323
260
|
isActive = false;
|
324
261
|
hasError = true;
|
325
|
-
@
|
262
|
+
@endRuby
|
326
263
|
|
327
264
|
<span @class({
|
328
265
|
"p-4": true,
|
@@ -339,7 +276,7 @@ Likewise, the `@style` directive may be used to conditionally add inline CSS sty
|
|
339
276
|
```rblade
|
340
277
|
@ruby
|
341
278
|
isActive = true;
|
342
|
-
@
|
279
|
+
@endRuby
|
343
280
|
|
344
281
|
<span @style({
|
345
282
|
"background-color: red": true,
|
@@ -369,7 +306,7 @@ Likewise, the `@selected` directive may be used to indicate if a given select op
|
|
369
306
|
<option value="{{ version }}" @selected(version == selectedVersion)>
|
370
307
|
{{ version }}
|
371
308
|
</option>
|
372
|
-
@
|
309
|
+
@endEach
|
373
310
|
</select>
|
374
311
|
```
|
375
312
|
|
@@ -446,20 +383,7 @@ In some situations, it's useful to embed Ruby code into your views. You can use
|
|
446
383
|
```rblade
|
447
384
|
@ruby
|
448
385
|
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')
|
386
|
+
@endRuby
|
463
387
|
```
|
464
388
|
|
465
389
|
<a name="comments"></a>
|
@@ -568,7 +492,7 @@ When passing attributes to components, you may also use a "short attribute" synt
|
|
568
492
|
<a name="escaping-attribute-rendering"></a>
|
569
493
|
#### Escaping Attribute Rendering
|
570
494
|
|
571
|
-
Since some JavaScript frameworks such as Alpine.js also use colon-prefixed attributes, you may use a double colon (`::`) prefix to inform RBlade that the attribute is not a
|
495
|
+
Since some JavaScript frameworks such as Alpine.js also use colon-prefixed attributes, you may use a double colon (`::`) prefix to inform RBlade that the attribute is not a Ruby expression. For example, given the following component:
|
572
496
|
|
573
497
|
```rblade
|
574
498
|
<x-button ::class="{ danger: isDeleting }">
|
@@ -593,8 +517,6 @@ We've already examined how to pass data attributes to a component; however, some
|
|
593
517
|
<x-alert type="error" :message class="mt-4"/>
|
594
518
|
```
|
595
519
|
|
596
|
-
**TODO remove from attributes bag using @props? Rename to attributes bag?**
|
597
|
-
|
598
520
|
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:
|
599
521
|
|
600
522
|
```rblade
|
@@ -603,8 +525,8 @@ All of the attributes that are not part of the component's constructor will auto
|
|
603
525
|
</div>
|
604
526
|
```
|
605
527
|
|
606
|
-
<a name="default-merged-attributes"></a>
|
607
|
-
#### Default
|
528
|
+
<a name="default-and-merged-attributes"></a>
|
529
|
+
#### Default & Merged Attributes
|
608
530
|
|
609
531
|
Sometimes you may need to specify default values for attributes or merge additional values into some of the component's attributes. To accomplish this, you may 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
532
|
|
@@ -657,31 +579,22 @@ The rendered HTML of the `button` component in this example would be:
|
|
657
579
|
</button>
|
658
580
|
```
|
659
581
|
|
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
582
|
<a name="conditionally-merge-classes"></a>
|
670
583
|
#### 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
|
584
|
+
|
585
|
+
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
586
|
|
674
587
|
```rblade
|
675
|
-
<div {{
|
676
|
-
{{
|
588
|
+
<div {{ attributes.class({'p-4': true, 'bg-red': hasError}) }}>
|
589
|
+
{{ message }}
|
677
590
|
</div>
|
678
591
|
```
|
679
592
|
|
680
593
|
If you need to merge other attributes onto your component, you can chain the `merge` method onto the `class` method:
|
681
594
|
|
682
595
|
```rblade
|
683
|
-
<button {{
|
684
|
-
{{
|
596
|
+
<button {{ attributes.class({'bg-red': hasError}).merge({type: 'button'}) }}>
|
597
|
+
{{ slot }}
|
685
598
|
</button>
|
686
599
|
```
|
687
600
|
|
@@ -691,62 +604,39 @@ If you need to merge other attributes onto your component, you can chain the `me
|
|
691
604
|
<a name="filtering-attributes"></a>
|
692
605
|
#### Retrieving and Filtering Attributes
|
693
606
|
|
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
|
-
```
|
607
|
+
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.
|
701
608
|
|
702
|
-
|
609
|
+
You may 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.
|
703
610
|
|
704
611
|
```rblade
|
705
|
-
{{
|
612
|
+
{{ attributes.filter { |k, v| k == 'foo'} }}
|
613
|
+
{{ attributes.slice :foo }}
|
706
614
|
```
|
707
615
|
|
708
|
-
|
616
|
+
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:
|
709
617
|
|
710
618
|
```rblade
|
711
|
-
|
712
|
-
```
|
713
|
-
|
714
|
-
Using the `first` method, you may render the first attribute in a given attribute bag:
|
715
|
-
|
716
|
-
```rblade
|
717
|
-
{{ $attributes->whereStartsWith('wire:model')->first() }}
|
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'))
|
619
|
+
@if (attributes.has?(:class))
|
724
620
|
<div>Class attribute is present</div>
|
725
621
|
@endif
|
726
622
|
```
|
727
623
|
|
728
|
-
If
|
624
|
+
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
625
|
|
730
626
|
```rblade
|
731
|
-
@if (
|
627
|
+
@if (attributes.has?('name', 'class'))
|
732
628
|
<div>All of the attributes are present</div>
|
733
629
|
@endif
|
734
630
|
```
|
735
631
|
|
736
|
-
The `
|
632
|
+
The `has_any?` method may be used to determine if any of the given attributes are present on the component:
|
737
633
|
|
738
634
|
```rblade
|
739
|
-
@if (
|
635
|
+
@if (attributes.has_any?('href', ':href', 'v-bind:href'))
|
740
636
|
<div>One of the attributes is present</div>
|
741
637
|
@endif
|
742
638
|
```
|
743
639
|
|
744
|
-
You may retrieve a specific attribute's value using the `get` method:
|
745
|
-
|
746
|
-
```rblade
|
747
|
-
{{ $attributes->get('class') }}
|
748
|
-
```
|
749
|
-
|
750
640
|
<a name="slots"></a>
|
751
641
|
### Slots
|
752
642
|
|
@@ -789,14 +679,14 @@ You may define the content of the named slot using the `x-slot` tag. Any content
|
|
789
679
|
<strong>Whoops!</strong> Something went wrong!
|
790
680
|
</x-alert>
|
791
681
|
```
|
792
|
-
|
793
|
-
|
682
|
+
|
683
|
+
The slot object extends the String interface, so you can invoke a slot's `empty?` method to determine if the slot contains content:
|
794
684
|
|
795
685
|
```rblade
|
796
686
|
<span class="alert-title">{{ title }}</span>
|
797
687
|
|
798
688
|
<div class="alert alert-danger">
|
799
|
-
@if (slot.
|
689
|
+
@if (slot.empty?)
|
800
690
|
This is default content if the slot is empty.
|
801
691
|
@else
|
802
692
|
{{ slot }}
|
@@ -804,29 +694,6 @@ You may invoke a slot's `isEmpty` method to determine if the slot contains conte
|
|
804
694
|
</div>
|
805
695
|
```
|
806
696
|
|
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
697
|
<a name="slot-attributes"></a>
|
831
698
|
#### Slot Attributes
|
832
699
|
|
@@ -848,15 +715,14 @@ Like RBlade components, you may assign additional [attributes](#component-attrib
|
|
848
715
|
|
849
716
|
To interact with slot attributes, you may 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
717
|
|
851
|
-
**TODO allow class to contain a string**
|
852
718
|
```rblade
|
853
719
|
@props({
|
854
720
|
"heading": _required,
|
855
721
|
"footer": _required,
|
856
722
|
})
|
857
723
|
|
858
|
-
<div {{ attributes.class(
|
859
|
-
<h1 {{ heading.attributes
|
724
|
+
<div {{ attributes.class('border') }}>
|
725
|
+
<h1 {{ heading.attributes.class('text-lg') }}>
|
860
726
|
{{ heading }}
|
861
727
|
</h1>
|
862
728
|
|
@@ -868,16 +734,6 @@ To interact with slot attributes, you may access the `attributes` property of th
|
|
868
734
|
</div>
|
869
735
|
```
|
870
736
|
|
871
|
-
<a name="dynamic-components"></a>
|
872
|
-
### Dynamic Components
|
873
|
-
**TODO add this**
|
874
|
-
Sometimes you may need to render a component without knowing which component should be rendered until runtime. In this situation, you may use RBlade's built-in `dynamic-component` component to render the component based on a runtime value or variable:
|
875
|
-
|
876
|
-
```rblade
|
877
|
-
@ruby(componentName = "secondary-button")
|
878
|
-
<x-dynamic-component :component="componentName" class="mt-4" />
|
879
|
-
```
|
880
|
-
|
881
737
|
<a name="registering-additional-component-directories"></a>
|
882
738
|
### Registering Additional Component Directories
|
883
739
|
|
@@ -895,33 +751,6 @@ RBlade::ComponentStore.add_path(Rails.root.join("app", "views", "partials"), "pa
|
|
895
751
|
|
896
752
|
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
753
|
|
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
754
|
<a name="anonymous-index-components"></a>
|
926
755
|
### Index Components
|
927
756
|
|
@@ -949,64 +778,26 @@ However, when an `index.rblade` template exists in a directory, it will be rende
|
|
949
778
|
/app/views/components/accordion/item.rblade
|
950
779
|
```
|
951
780
|
|
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>`:
|
781
|
+
<a name="forms"></a>
|
782
|
+
## Forms
|
956
783
|
|
957
|
-
|
958
|
-
<x-menu color="purple">
|
959
|
-
<x-menu.item>...</x-menu.item>
|
960
|
-
<x-menu.item>...</x-menu.item>
|
961
|
-
</x-menu>
|
962
|
-
```
|
784
|
+
### Old Input
|
963
785
|
|
964
|
-
The
|
786
|
+
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:
|
965
787
|
|
966
788
|
```rblade
|
967
|
-
|
968
|
-
|
969
|
-
@props(['color' => 'gray'])
|
970
|
-
|
971
|
-
<ul {{ $attributes->merge(['class' => 'bg-'.$color.'-200']) }}>
|
972
|
-
{{ $slot }}
|
973
|
-
</ul>
|
789
|
+
<input type="text" name="email" value="@old('email', user.email)">
|
974
790
|
```
|
975
791
|
|
976
|
-
|
792
|
+
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`:
|
977
793
|
|
978
794
|
```rblade
|
979
|
-
|
980
|
-
|
981
|
-
@aware(['color' => 'gray'])
|
982
|
-
|
983
|
-
<li {{ $attributes->merge(['class' => 'text-'.$color.'-800']) }}>
|
984
|
-
{{ $slot }}
|
985
|
-
</li>
|
986
|
-
```
|
987
|
-
|
988
|
-
> [!WARNING]
|
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:
|
998
|
-
|
999
|
-
```rblade
|
1000
|
-
<form method="POST" action="/profile">
|
1001
|
-
@csrf
|
1002
|
-
|
1003
|
-
...
|
1004
|
-
</form>
|
795
|
+
<input type="text" name="email" value="{{ params.fetch(:email, user.email) }}">
|
1005
796
|
```
|
1006
797
|
|
1007
798
|
<a name="method-field"></a>
|
1008
799
|
### Method Field
|
1009
|
-
|
800
|
+
|
1010
801
|
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
802
|
|
1012
803
|
```rblade
|
@@ -1017,52 +808,8 @@ Since HTML forms can't make `PUT`, `PATCH`, or `DELETE` requests, you will need
|
|
1017
808
|
</form>
|
1018
809
|
```
|
1019
810
|
|
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>
|
811
|
+
Alternatively, you can use the dedicated directives for each method: `@put`, `@patch`, or `@delete`.
|
1057
812
|
|
1058
|
-
<input id="email"
|
1059
|
-
type="email"
|
1060
|
-
class="@error('email', 'login') is-invalid @enderror">
|
1061
|
-
|
1062
|
-
@error('email', 'login')
|
1063
|
-
<div class="alert alert-danger">{{ $message }}</div>
|
1064
|
-
@enderror
|
1065
|
-
```
|
1066
813
|
|
1067
814
|
<a name="stacks"></a>
|
1068
815
|
## Stacks
|
@@ -1072,11 +819,11 @@ RBlade allows you to push to named stacks which can be rendered elsewhere in ano
|
|
1072
819
|
```rblade
|
1073
820
|
@push('scripts')
|
1074
821
|
<script src="/example.js"></script>
|
1075
|
-
@
|
822
|
+
@endPush
|
1076
823
|
```
|
1077
824
|
|
1078
|
-
If you would like to `@push` content if a given boolean expression evaluates to `true`, you may use the `@
|
1079
|
-
|
825
|
+
If you would like to `@push` content if a given boolean expression evaluates to `true`, you may use the `@pushif` directive:
|
826
|
+
|
1080
827
|
```rblade
|
1081
828
|
@pushIf(shouldPush, 'scripts')
|
1082
829
|
<script src="/example.js"></script>
|
@@ -1098,153 +845,11 @@ If you would like to prepend content onto the beginning of a stack, you should u
|
|
1098
845
|
```rblade
|
1099
846
|
@push('scripts')
|
1100
847
|
This will be second...
|
1101
|
-
@
|
848
|
+
@endPush
|
1102
849
|
|
1103
850
|
// Later...
|
1104
851
|
|
1105
852
|
@prepend('scripts')
|
1106
853
|
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
|
854
|
+
@endPrepend
|
1250
855
|
```
|