Skip to content

Commit 9e1de79

Browse files
authored
Merge pull request #4312 from Laravel-Backpack/crud-form-javascript-library
Add JS library for interacting with fields inside a CRUD form
2 parents 74302a7 + 90520be commit 9e1de79

12 files changed

+328
-15
lines changed

src/resources/views/crud/fields/checkbox.blade.php

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
@endphp
66
@include('crud::fields.inc.wrapper_start')
77
@include('crud::fields.inc.translatable_icon')
8-
<div class="checkbox">
98
<input type="hidden" name="{{ $field['name'] }}" value="{{ $field['value'] }}">
109
<input type="checkbox"
1110
data-init-function="bpFieldInitCheckbox"
@@ -20,13 +19,12 @@
2019
@endforeach
2120
@endif
2221
>
23-
<label class="form-check-label font-weight-normal">{!! $field['label'] !!}</label>
22+
<label class="font-weight-normal mb-0">{!! $field['label'] !!}</label>
2423

2524
{{-- HINT --}}
2625
@if (isset($field['hint']))
2726
<p class="help-block">{!! $field['hint'] !!}</p>
2827
@endif
29-
</div>
3028
@include('crud::fields.inc.wrapper_end')
3129

3230
{{-- ########################################## --}}
@@ -42,7 +40,7 @@ function bpFieldInitCheckbox(element) {
4240
var id = 'checkbox_'+Math.floor(Math.random() * 1000000);
4341
4442
// make sure the value is a boolean (so it will pass validation)
45-
if (hidden_element.val() === '') hidden_element.val(0);
43+
if (hidden_element.val() === '') hidden_element.val(0).trigger('change');
4644
4745
// set unique IDs so that labels are correlated with inputs
4846
element.attr('id', id);
@@ -56,13 +54,20 @@ function bpFieldInitCheckbox(element) {
5654
element.prop('checked', false);
5755
}
5856
57+
hidden_element.on('CrudField:disable', function(e) {
58+
element.prop('disabled', true);
59+
});
60+
hidden_element.on('CrudField:enable', function(e) {
61+
element.removeAttr('disabled');
62+
});
63+
5964
// when the checkbox is clicked
6065
// set the correct value on the hidden input
6166
element.change(function() {
6267
if (element.is(":checked")) {
63-
hidden_element.val(1);
68+
hidden_element.val(1).trigger('change');
6469
} else {
65-
hidden_element.val(0);
70+
hidden_element.val(0).trigger('change');
6671
}
6772
})
6873
}

src/resources/views/crud/fields/checklist.blade.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,18 @@ function bpFieldInitChecklist(element) {
8686
}
8787
});
8888
89-
hidden_input.val(JSON.stringify(newValue));
89+
hidden_input.val(JSON.stringify(newValue)).trigger('change');
9090
9191
});
92+
93+
hidden_input.on('CrudField:disable', function(e) {
94+
checkboxes.attr('disabled', 'disabled');
95+
});
96+
97+
hidden_input.on('CrudField:enable', function(e) {
98+
checkboxes.removeAttr('disabled');
99+
});
100+
92101
}
93102
</script>
94103
@endLoadOnce

src/resources/views/crud/fields/checklist_dependency.blade.php

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
<div class="row">
8080

8181
<div class="hidden_fields_primary" data-name = "{{ $primary_dependency['name'] }}">
82-
<input type="hidden" name="{{$primary_dependency['name']}}" value="" />
82+
<input type="hidden" bp-field-name="{{$primary_dependency['name']}}" name="{{$primary_dependency['name']}}" value="" />
8383
@if(isset($field['value']))
8484
@if($old_primary_dependency)
8585
@foreach($old_primary_dependency as $item )
@@ -130,7 +130,7 @@ class = 'primary_list'
130130

131131
<div class="row">
132132
<div class="hidden_fields_secondary" data-name="{{ $secondary_dependency['name'] }}">
133-
<input type="hidden" name="{{$secondary_dependency['name']}}" value="" />
133+
<input type="hidden" bp-field-name="{{$secondary_dependency['name']}}" name="{{$secondary_dependency['name']}}" value="" />
134134
@if(isset($field['value']))
135135
@if($old_secondary_dependency)
136136
@foreach($old_secondary_dependency as $item )
@@ -210,17 +210,44 @@ function bpFieldInitChecklistDependencyElement(element) {
210210
let inputToAdd = $('<input type="hidden" class="primary_hidden" name="'+nameInput+'[]" value="'+idCurrent+'">');
211211
212212
field.find('.hidden_fields_primary').append(inputToAdd);
213+
field.find('.hidden_fields_primary').find('input').first().val(idCurrent).trigger('change');
214+
field.find('.hidden_fields_primary').find('input').first().val()
213215
214216
$.each(dependencyJson[idCurrent], function(key, value){
215217
//check and disable secondies checkbox
216218
field.find('input.secondary_list[value="'+value+'"]').prop( "checked", true );
217219
field.find('input.secondary_list[value="'+value+'"]').prop( "disabled", true );
220+
field.find('input.secondary_list[value="'+value+'"]').attr('forced-select', 'true');
218221
//remove hidden fields with secondary dependency if was set
219222
var hidden = field.find('input.secondary_hidden[value="'+value+'"]');
220223
if(hidden)
221224
hidden.remove();
222225
});
223226
};
227+
228+
thisField.find('div.hidden_fields_primary').children('input').first().on('CrudField:disable', function(e) {
229+
let input = $(e.target);
230+
input.parent().parent().find('input[type=checkbox]').attr('disabled', 'disabled');
231+
input.siblings('input').attr('disabled','disabled');
232+
});
233+
234+
thisField.find('div.hidden_fields_primary').children('input').first().on('CrudField:enable', function(e) {
235+
let input = $(e.target);
236+
input.parent().parent().find('input[type=checkbox]').not('[forced-select]').removeAttr('disabled');
237+
input.siblings('input').removeAttr('disabled');
238+
});
239+
240+
thisField.find('div.hidden_fields_secondary').children('input').first().on('CrudField:disable', function(e) {
241+
let input = $(e.target);
242+
input.parent().parent().find('input[type=checkbox]').attr('disabled', 'disabled');
243+
input.siblings('input').attr('disabled','disabled');
244+
});
245+
246+
thisField.find('div.hidden_fields_secondary').children('input').first().on('CrudField:enable', function(e) {
247+
let input = $(e.target);
248+
input.parent().parent().find('input[type=checkbox]').not('[forced-select]').removeAttr('disabled');
249+
input.siblings('input').removeAttr('disabled');
250+
});
224251
225252
thisField.find('.primary_list').each(function() {
226253
var checkbox = $(this);
@@ -257,6 +284,7 @@ function bpFieldInitChecklistDependencyElement(element) {
257284
if(ok){
258285
thisField.find('input.secondary_list[value="'+secondaryItem+'"]').prop('checked', false);
259286
thisField.find('input.secondary_list[value="'+secondaryItem+'"]').prop('disabled', false);
287+
thisField.find('input.secondary_list[value="'+secondaryItem+'"]').removeAttr('forced-select');
260288
}
261289
});
262290

src/resources/views/crud/fields/inc/wrapper_start.blade.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,13 @@
2222
$field['wrapper']['class'] = $field['wrapper']['class'] ?? "form-group col-sm-12";
2323
$field['wrapper']['class'] = $field['wrapper']['class'].$required;
2424
$field['wrapper']['element'] = $field['wrapper']['element'] ?? 'div';
25+
$field['wrapper']['bp-field-wrapper'] = 'true';
26+
$field['wrapper']['bp-field-name'] = square_brackets_to_dots(implode(',', (array)$field['name']));
27+
$field['wrapper']['bp-field-type'] = $field['type'];
2528
@endphp
2629

2730
<{{ $field['wrapper']['element'] }}
2831
@foreach($field['wrapper'] as $attribute => $value)
2932
{{ $attribute }}="{{ $value }}"
3033
@endforeach
31-
>
34+
>

src/resources/views/crud/fields/radio.blade.php

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@
1818

1919
@include('crud::fields.inc.wrapper_start')
2020

21-
<div>
22-
<label>{!! $field['label'] !!}</label>
21+
22+
<label class="d-block">{!! $field['label'] !!}</label>
2323
@include('crud::fields.inc.translatable_icon')
24-
</div>
24+
2525

2626
<input type="hidden" value="{{ $optionValue }}" name="{{$field['name']}}" />
2727

@@ -64,6 +64,18 @@ function bpFieldInitRadioElement(element) {
6464
$(this).siblings('label').attr('for', id+index);
6565
});
6666
67+
hiddenInput.on('CrudField:disable', function(e) {
68+
element.find('.form-check input[type=radio]').each(function(index, item) {
69+
$(this).prop('disabled', true);
70+
});
71+
});
72+
73+
hiddenInput.on('CrudField:enable', function(e) {
74+
element.find('.form-check input[type=radio]').each(function(index, item) {
75+
$(this).removeAttr('disabled');
76+
});
77+
});
78+
6779
// when one radio input is selected
6880
element.find('input[type=radio]').change(function(event) {
6981
// the value gets updated in the hidden input and the 'change' event is fired

src/resources/views/crud/fields/select_from_array.blade.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<select
1212
name="{{ $field['name'] }}@if ($field['multiple'])[]@endif"
1313
@include('crud::fields.inc.attributes')
14-
@if ($field['multiple'])multiple @endif
14+
@if ($field['multiple'])multiple bp-field-main-input @endif
1515
>
1616

1717
@if ($field['allows_null'] && !$field['multiple'])

src/resources/views/crud/fields/select_multiple.blade.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
class="form-control"
2626
name="{{ $field['name'] }}[]"
2727
@include('crud::fields.inc.attributes')
28+
bp-field-main-input
2829
multiple>
2930

3031
@if (count($options))

src/resources/views/crud/fields/summernote.blade.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
name="{{ $field['name'] }}"
1414
data-init-function="bpFieldInitSummernoteElement"
1515
data-options="{{ json_encode($field['options']) }}"
16+
bp-field-main-input
1617
@include('crud::fields.inc.attributes', ['default_class' => 'form-control summernote'])
1718
>{{ old_empty_or_null($field['name'], '') ?? $field['value'] ?? $field['default'] ?? '' }}</textarea>
1819

@@ -46,7 +47,25 @@
4647
@loadOnce('bpFieldInitSummernoteElement')
4748
<script>
4849
function bpFieldInitSummernoteElement(element) {
49-
element.summernote(element.data('options'));
50+
var summernoteOptions = element.data('options');
51+
52+
let summernotCallbacks = {
53+
onChange: function(contents, $editable) {
54+
element.val(contents).trigger('change');
55+
}
56+
}
57+
58+
element.on('CrudField:disable', function(e) {
59+
element.summernote('disable');
60+
});
61+
62+
element.on('CrudField:enable', function(e) {
63+
element.summernote('enable');
64+
});
65+
66+
summernoteOptions['callbacks'] = summernotCallbacks;
67+
68+
element.summernote(summernoteOptions);
5069
}
5170
</script>
5271
@endLoadOnce

src/resources/views/crud/fields/upload.blade.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,26 @@ function bpFieldInitUploadElement(element) {
167167
$(this).next("input[type=hidden]").remove();
168168
});
169169
170+
element.on('CrudField:disable', function(e) {
171+
element.children('.backstrap-file').find('input').prop('disabled', 'disabled');
172+
173+
let $deleteButton = element.children('.existing-file').children('a.file_clear_button');
174+
175+
if($deleteButton.length > 0) {
176+
$deleteButton.on('click.prevent', function(e) {
177+
e.stopImmediatePropagation();
178+
return false;
179+
});
180+
// make the event we just registered, the first to be triggered
181+
$._data($deleteButton.get(0), "events").click.reverse();
182+
}
183+
});
184+
185+
element.on('CrudField:enable', function(e) {
186+
element.children('.backstrap-file').find('input').removeAttr('disabled');
187+
element.children('.existing-file').children('a.file_clear_button').unbind('click.prevent');
188+
});
189+
170190
}
171191
</script>
172192
@endLoadOnce

src/resources/views/crud/fields/upload_multiple.blade.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,40 @@ function bpFieldInitUploadMultipleElement(element) {
163163
164164
fileInput.change(function() {
165165
inputLabel.html("Files selected. After save, they will show up above.");
166+
let selectedFiles = [];
167+
168+
Array.from($(this)[0].files).forEach(file => {
169+
selectedFiles.push({name: file.name, type: file.type})
170+
});
171+
172+
element.find('input').first().val(JSON.stringify(selectedFiles)).trigger('change');
166173
// remove the hidden input, so that the setXAttribute method is no longer triggered
167174
$(this).next("input[type=hidden]:not([name='clear_"+fieldName+"[]'])").remove();
168175
});
176+
177+
element.find('input').on('CrudField:disable', function(e) {
178+
element.children('.backstrap-file').find('input').prop('disabled', 'disabled');
179+
element.children('.existing-file').find('.file-preview').each(function(i, el) {
180+
181+
let $deleteButton = $(el).find('a.file-clear-button');
182+
183+
if(deleteButton.length > 0) {
184+
$deleteButton.on('click.prevent', function(e) {
185+
e.stopImmediatePropagation();
186+
return false;
187+
});
188+
// make the event we just registered, the first to be triggered
189+
$._data($deleteButton.get(0), "events").click.reverse();
190+
}
191+
});
192+
});
193+
194+
element.on('CrudField:enable', function(e) {
195+
element.children('.backstrap-file').find('input').removeAttr('disabled');
196+
element.children('.existing-file').find('.file-preview').each(function(i, el) {
197+
$(el).find('a.file-clear-button').unbind('click.prevent');
198+
});
199+
});
169200
}
170201
</script>
171202
@endLoadOnce

0 commit comments

Comments
 (0)