761
loading...
This website collects cookies to deliver better user experience
app/FormFields/IngredientsFormField.php
:namespace App\FormFields;
use TCG\Voyager\FormFields\AbstractHandler;
class IngredientsFormField extends AbstractHandler
{
protected $codename = 'ingredient';
public function createContent($row, $dataType, $dataTypeContent, $options)
{
return view('formfields.ingredients', [
'row' => $row,
'options' => $options,
'dataType' => $dataType,
'dataTypeContent' => $dataTypeContent
]);
}
}
register()
-function in AppServiceProvider.php
:use TCG\Voyager\Facades\Voyager;
use App\FormFields\IngredientsFormField;
...
public function register()
{
Voyager::addFormField(IngredientsFormField::class);
}
app/resources/views/formfields/ingredients.blade.php
. app/FormFields/IngredientsFormField.php
. @php
$data = json_decode($dataTypeContent->ingredients);
@endphp
<div id="ingredients-container">
<div class="ingredients-header-row row">
<div class="voyager-header-col col-md-2">
<p class="control-label">
Amount
</p>
</div>
<div class="voyager-header-col col-md-2">
<p class="control-label">
Unit
</p>
</div>
<div class="voyager-header-col col-md-8">
<p class="control-label">
Name
</p>
</div>
</div>
@if ($data)
@foreach ($data as $row)
@include('formfields.parts.ingredient', ['index' => $loop->index, 'row' => $row])
@endforeach
@else
@include('formfields.parts.ingredient')
@endif
</div>
<button type="button" name="add" id="addIngredient" class="btn btn-light">Add Ingredient</button>
app/resources/views/formfields/parts/ingredient.blade.php
we create the markup for a single ingredient view.@php
$default_row = new \stdClass();
$default_row->unit = "";
$default_row->amount = "";
$default_row->ingredient = "";
$row = $row ?? $default_row;
$index = $index ?? 0;
@endphp
<div class="row ingredients-row" id="ingredients-row-{{$index}}" data-index="{{$index}}">
<div class="col-md-2">
<input
type="number"
class="form-control ingredients-amount"
name="ingredients[{{$index}}][amount]"
required
value="{{$row->amount}}"
/>
</div>
<div class="col-md-2">
<input
type="text"
class="form-control ingredients-units"
name="ingredients[{{$index}}][unit]"
required
value="{{$row->unit}}"
/>
</div>
<div class="col-md-7">
<input
type="text"
class="form-control ingredients-ingredie"
name="ingredients[{{$index}}][ingredient]"
required
value="{{$row->ingredient}}"
/>
</div>
<div class="col-md-1 remove-ingredients-col">
<button class="btn btn-danger remove-ingredients-row-btn" data-remove-row={{$index}}>
X
</button>
</div>
</div>
[
{
"amount":"200",
"unit":"ml",
"ingredient":"Milk"
},
{
"amount":"50",
"unit":"ml",
"ingredient":"Water"
}
]
public/js/ingredients.js
. webpack.mix.js
:.js('resources/js/ingredients.js', 'public/js')
additional_js
-array in config/voyager.php
.'additional_js' => [
'js/ingredients.js',
],
package.json
already, compiling should work with npm run watch
or yarn watch
. main-function
is looking for the addIngredient-Button
. If this button exists on the page, it adds the addIngredientRow
-function to the button. It also adds the removeIngredientRow
-function to all the remove-buttons.addIngredientRow
clones the first row, clears it, sets the id to the current row count and appends it as a new, clean row to the container. removeIngredientRow
removes the current row from the DOM by using the remove-row-dataset.const addIngredientRow = (firstRow) => {
const rowsCount = document.querySelector(".ingredients-row:last-child").dataset.index + 1;
const newRow = firstRow.cloneNode(true);
newRow.id = `ingredients-row-${rowsCount}`;
newRow.dataset.index = rowsCount;
newRow.querySelector('.ingredients-amount').setAttribute('name', `ingredients[${rowsCount}][amount]`);
newRow.querySelector('.ingredients-amount').value = "";
newRow.querySelector('.ingredients-units').setAttribute('name', `ingredients[${rowsCount}][unit]`);
newRow.querySelector('.ingredients-units').value = "";
newRow.querySelector('.ingredients-ingredients').setAttribute('name', `ingredients[${rowsCount}][ingredient]`);
newRow.querySelector('.ingredients-ingredients').value = "";
newRow.querySelector('.remove-ingredients-row-btn').dataset.removeRow = rowsCount;
newRow.querySelector('.remove-ingredients-row-btn').addEventListener('click', (e) => {removeIngredientRow(e)}, false);
document.querySelector('#ingredients-container').appendChild(newRow);
}
const removeIngredientRow = (e) => {
e.preventDefault();
document.querySelector(`#ingredients-row-${e.currentTarget.dataset.removeRow}`).remove();
}
const main = () => {
if(document.querySelector("#addIngredient")) {
const firstRow = document.querySelector("#ingredients-row-0");
const addButton = document.querySelector("#addIngredient");
addButton.addEventListener('click', () => {addIngredientRow(firstRow), false});
const removeButtons = document.querySelectorAll(".remove-ingredients-row-btn");
removeButtons.forEach((btn) => {
btn.addEventListener('click', (e) => {removeIngredientRow(e)}, false);
})
}
}
main();
npm run watch
. It could also be necessary to restart php artisan serve
. In some cases you might need to clear your config by running php artisan config:cache
before running php artisan serve
.app
or your app\model
folder. Most of the time, it has the same name as your BREAD. If you can't find it, just create a file in your app-folder that containins the following code.<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Recipe extends Model
{
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Recipe extends Model
{
public function save(array $options = [])
{
// Do whatever you want
$this->ingredients = json_encode(request()->input('ingredients'));
parent::save();
}
}