Added row of sub dropdown values is being based on the dropdown of first row

49 views Asked by At

I am using livewire to add and remove a row and also to do a dynamic dropdown. My problem is that when I add a new row, the values of the sub commodity of that added row is being based on the selected commodity in the first row not in the commodity of its same added row.

e-linkage-components.blade.php

<div class="row">
    <div class="col-md-4 mb-2">
        <label for="associationName" class="form-label label-style">Association Name</label>
        <input wire:model="associationName.0" type="text" class="form-control" name="associationName[]"
            id="associationName" autocomplete="off" value="{{ $associationProfile->association }}" readonly>
    </div>

    <div class="col-md-4 mb-2">
        <label for="commodity" class="form-label label-style">Commodity</label>
        <select wire:model.live="commoditiesID.0" id="commodity" name="commodity[]" class="form-select">
            <option value="">Select Commodity</option>
            @foreach ($commodities as $commodity)
                <option value="{{ $commodity->id }}">{{ $commodity->commodity }}</option>
            @endforeach
        </select>
    </div>

    <div class="col-md-4 mb-2">
        <label for="subCommodity" class="form-label label-style">Sub Commodity</label>
        <select wire:model.live="subCommoditiesID.0" id="subCommodity" name="subCommodity[]" class="form-select">
            <option value="">Select Sub Commodity</option>
            @foreach ($subCommodities as $subCommodity)
                <option value="{{ $subCommodity->id }}">{{ $subCommodity->subCommodities }}</option>
            @endforeach
        </select>
    </div>

    <div class="row">
        <div class="col-md-2 mb-2">
            <label for="variety" class="form-label label-style">Variety</label>
            <input wire:model="variety.0" type="text" class="form-control" name="variety[]" id="variety"
                autocomplete="off">
        </div>

        <div class="col-md-2 mb-2">
            <label for="volume" class="form-label label-style">Volume (Kg)</label>
            <input wire:model="volume.0" type="text" class="form-control" name="volume[]" id="volume"
                autocomplete="off">
        </div>
    </div>

    <div class="row">
        <div class="col-md-3 mb-2">
            <label for="startDate" class="form-label label-style">Estimated
                Time of Harvest <br> (Start Date)</label>
            <input wire:model="startDate.0" type="date" class="form-control" name="startDate[]" id="startDate">
        </div>

        <div class="col-md-3 mb-2">
            <label for="endDate" class="form-label label-style">Estimated
                Time of Harvest <br> (End Date)</label>
            <input wire:model="endDate.0" type="Date" class="form-control" name="endDate[]" id="endDate">
        </div>
    </div>
    <hr>

    @foreach ($inputs as $key => $value)
        <div class="col-md-4 mb-2">
            <label for="associationName" class="form-label label-style">Association Name</label>
            <input wire:model="associationName.{{ $value }}" type="text" class="form-control"
                name="associationName[]" id="associationName" autocomplete="off"
                value="{{ $associationProfile->association }}" readonly>
        </div>

        <div class="col-md-4 mb-2">
            <label for="commodity" class="form-label label-style">Commodity</label>
            <select wire:model="commoditiesID.{{ $value }}" id="commodity" name="commodity[]"
                class="form-select">
                <option value="">Select Commodity</option>
                @foreach ($commodities as $commodity)
                    <option value="{{ $commodity->id }}">{{ $commodity->commodity }}</option>
                @endforeach
            </select>
        </div>

        <div class="col-md-4 mb-2">
            <label for="subCommodity" class="form-label label-style">Sub Commodity</label>
            <select wire:model="subCommoditiesID.{{ $value }}" id="subCommodity" name="subCommodity[]"
                class="form-select">
                <option value="">Select Sub Commodity</option>
                @foreach ($subCommodities as $subCommodity)
                    <option value="{{ $subCommodity->id }}">{{ $subCommodity->subCommodities }}</option>
                @endforeach
            </select>
        </div>

        <div class="row">
            <div class="col-md-2 mb-2">
                <label for="variety" class="form-label label-style">Variety</label>
                <input wire:model="variety.{{ $value }}" type="text" class="form-control" name="variety[]"
                    id="variety" autocomplete="off">
            </div>

            <div class="col-md-2 mb-2">
                <label for="volume" class="form-label label-style">Volume (Kg)</label>
                <input wire:model="volume.{{ $value }}" type="text" class="form-control" name="volume[]"
                    id="volume" autocomplete="off">
            </div>
        </div>

        <div class="row">
            <div class="col-md-3 mb-2">
                <label for="startDate" class="form-label label-style">Estimated
                    Time of Harvest <br> (Start Date)</label>
                <input wire:model="startDate.{{ $value }}" type="date" class="form-control"
                    name="startDate[]" id="startDate">
            </div>

            <div class="col-md-3 mb-2">
                <label for="endDate" class="form-label label-style">Estimated
                    Time of Harvest <br> (End Date)</label>
                <input wire:model="endDate.{{ $value }}" type="Date" class="form-control"
                    name="endDate[]" id="endDate">
            </div>

            <div class="col-md-2 mb-2 d-flex mt-auto">
                <button type="button" wire:click="remove({{ $key }})"
                    class="btn btn-danger">Remove</button>
            </div>
        </div>
        <hr>
    @endforeach

    <div class="col-md-2 mb-2 d-flex mt-auto">
        <button type="button" wire:click="add({{ $i }})" class="btn btn-success">Add more</button>
    </div>

</div>

ELinkageComponents.php

<?php

namespace App\Livewire;

use App\Models\AssociationProfile;
use App\Models\BuyerLinkage;
use App\Models\Commodities;
use App\Models\SubCommodities;
use Illuminate\Support\Facades\Auth;
use Livewire\Component;

class ELinkageComponents extends Component
{
    public $user;
    public $associationProfile;
    public $ELinkages;
    public $commoditiesID;
    public $subCommoditiesID;
    public $inputs;
    public $i;
    public $associationName;
    public $variety;
    public $volume;
    public $startDate;
    public $endDate;

    public function mount()
    {
        $this->user = Auth::user();
        $this->associationProfile = AssociationProfile::where('userId', $this->user->id)->first();
        $this->ELinkages = BuyerLinkage::join('commodities', 'buyer_e_linkages.commodity', '=', 'commodities.id')->select('buyer_e_linkages.*', 'commodities.commodity as commodity_name')->get();
        $this->inputs = [];
        $this->commoditiesID = [];
        $this->subCommoditiesID = [];
        $this->associationName = [$this->associationProfile->association];
        $this->variety = [];
        $this->volume = [];
        $this->startDate = [];
        $this->endDate = [];
        $this->i = 1;
    }

    public function add($i)
    {
        $this->i = $i + 1;
        array_push($this->inputs, $i);
        $this->associationName[$i] = $this->associationProfile->association;
    }

    public function remove($key)
    {
        unset($this->inputs[$key]);
    }

    public function render()
    {
        return view('livewire.e-linkage-components', [
            'commodities' => Commodities::all(),
            'subCommodities' => SubCommodities::where('commodityId', $this->commoditiesID)->get(),
            'user' => $this->user,
            'associationProfile' => $this->associationProfile,
            'ELinkages' => $this->ELinkages,
        ]);
    }
}

I did some testing where I didn't selected any commodity in the first row and I added a new row then I selected a commodity in that row but when I checked the sub commodity, it doesn't have any values. It's like the values of sub commodity dropdown are only being based on the commodity of first row.

1

There are 1 answers

0
Pippo On

I suggest you this refactoring:

  1. Store all fields in a single array where each row represents a "subform"
  2. The index $i is no more needed, we will use the array index
  3. Add a row:key to each "subform", the array index is a good option since we will remove the rows using unset() so the indexes will not be compacted
  4. To retrieve the subcommodities we will use a method with a parameter (the selected commodity)
  5. Rather than repeating the same HTML code for the first fixed "subform" and then for the variable ones, we will put everything in the @foreach() loop and add some controls

Here I have also added a button to show the changes of the data

The class:

class ELinkageComponents extends Component
{
    protected $user;
    protected $associationProfile;
    public $associationName
    public $inputData = [];


    public function mount()
    {
        $this->user = Auth::user();
        $this->associationProfile = AssociationProfile::where('userId', $this->user->id)->first();

        $this->associationName = [$this->associationProfile->association];
        $this->add();
    }


    public function getsubCommodities($commoditiyId)
    {
        return SubCommodities::where('commodityId', $commoditiyId)->orderBy('subCommodities')->get();
    }


    // this method resets subcommoditu on commodity change
    public function updatedInputData($value, $key)
    {
       [$rowId, $fieldName] = explode('.', $key);

       if ($fieldName == 'commodityId') {
            $this->inputData[$rowId]['subCommodityId'] = null;
       }
    }


    public function add()
    {
        $this->inputData[] = [ 'associationName' => $this->associationProfile->association,
                               'commodityId' => null,
                               'subCommodityId' => null,
                               'variety' => null,
                               'volume' => null,
                               'startDate' => null,
                               'endDate' => null,
        ];
    }


    public function remove($rowId)
    {
        if ($rowId != array_key_first($this->inputData))
            unset($this->inputData[$rowId]);
    }


    public function showData()
    {
        dd ($this->inputData);
    }


    public function render()
    {
        return view('livewire.comm', [
            'commodities' => Commodities::all(),
            'associationProfile' => $this->associationProfile,
        ]);
    }
}

The view:

<div class="row">

    @foreach ($inputData as $rowId => $row)

        <div class="row" wire:key="form-{{ $rowId }}">

            <div class="col-md-4 mb-2">

                <label for="associationName" class="form-label label-style">
                    Association Name
                </label>

                <input wire:model="inputData.{{ $rowId }}.associationName"
                       type="text"
                       id="associationName"
                       class="form-control"
                       autocomplete="off"
                       value="{{ $associationProfile->association }}"
                       readonly
                >

            </div>

            <div class="col-md-4 mb-2">

                <label for="commodity" class="form-label label-style">
                    Commodity
                </label>

                <select wire:model.live="inputData.{{ $rowId }}.commodityId"
                        id="commodity"
                        class="form-select"
                >

                    <option value="">Select Commodity</option>

                    @foreach ($commodities as $commodity)

                        <option value="{{ $commodity->id }}" wire:key="comm-{{$commodity->id}}">
                            {{ $commodity->commodity }}
                        </option>

                    @endforeach

                </select>

            </div>

            <div class="col-md-4 mb-2">

                <label for="subCommodity" class="form-label label-style">
                    Sub Commodity
                </label>

                <select wire:model="inputData.{{ $rowId }}.subCommodityId"
                        id="subCommodity"
                        class="form-select"
                >

                    <option value="">Select Sub Commodity</option>

                    @foreach ($this->getSubCommodities($row['commodityId']) as $subCommodity)

                        <option value="{{ $subCommodity->id }}"  wire:key="subComm-{{$subCommodity->id}}">
                            {{ $subCommodity->subCommodities }}
                        </option>

                    @endforeach

                </select>

            </div>

            <div class="row">

                <div class="col-md-2 mb-2">

                    <label for="variety" class="form-label label-style">
                        Variety
                    </label>

                    <input wire:model="inputData.{{ $rowId }}.variety"
                           type="text"
                           class="form-control"
                           id="variety"
                           autocomplete="off"
                    >

                </div>

                <div class="col-md-2 mb-2">

                    <label for="volume" class="form-label label-style">
                        Volume (Kg)
                    </label>

                    <input wire:model="inputData.{{ $rowId }}.volume"
                           type="text"
                           class="form-control"
                           id="volume"
                           autocomplete="off"
                    >

                </div>

            </div>

            <div class="row">

                <div class="col-md-3 mb-2">

                    <label for="startDate" class="form-label label-style">
                        Estimated Time of Harvest <br>
                        (Start Date)
                    </label>

                    <input wire:model="inputData.{{ $rowId }}.startDate"
                           type="date"
                           class="form-control"
                           id="startDate"
                    >

                </div>

                <div class="col-md-3 mb-2">

                    <label for="endDate" class="form-label label-style">
                        Estimated Time of Harvest <br>
                        (End Date)
                    </label>

                    <input wire:model="inputData.{{ $rowId }}.endDate"
                           type="Date"
                           class="form-control"
                           id="endDate"
                    >

                </div>

                @if($rowId != array_key_first($inputData)) {{--count($inputData) > 1--}}

                    <div class="col-md-2 mb-2 d-flex mt-auto">

                        <button type="button"
                                wire:click="remove({{ $rowId }})"
                                class="btn btn-danger"
                        >
                            Remove
                        </button>

                    </div>

                @endif

            </div>

            <hr>

        </div>

    @endforeach

    <div class="col-md-2 mb-2 d-flex mt-auto">

        <button type="button"
                wire:click="add()"
                class="btn btn-success"
        >
            Add more
        </button>

    </div>

    <div class="col-md-2 mb-2 d-flex mt-auto">

        <button type="button"
                wire:click="showData()"
                class="btn btn-primary"
        >
            Show data
        </button>

    </div>
    
</div>