yeti blue
yeti blue

Reputation: 271

Vue loop array objects into form checkbox

I'm attempting to create an 'edit listing' page where someone's submitted information is displayed back. I'm a bit stuck on how to populate checkboxes in a form with a check on the boxes that were selected the first time.

I'm aware that checkboxes look for false/true in order to display a check, but my array of something like: [x,y,z] is displayed as just [true] or [false] which leads to all boxes being checked at once and vice versa when using v-model.

The form

           <input
              type="checkbox"
              id="Set Photographer"
              value="Set Photographer"
              v-model="returnedListing[0].crew_positions"
            />
            <label for="Set Photographer">Set Photographer</label>
            <input
              type="checkbox"
              id="Producer"
              value="Producer"
              v-model="returnedListing[0].crew_positions"
            />
            <label for="Producer">Producer</label>
            <input
              type="checkbox"
              id="Production Designer"
              value="Production Designer"
              v-model="returnedListing[0].crew_positions"
            />

            <label for="Production Designer">Production Designer</label>

returnedListing

 const [actors, returnedListing] = await Promise.all([
        $axios.$get(`/api/v1/actors/`, {
          params: {
            user: body
          }
        }),
        $axios.$get(`/api/v1/listings/`, {
          params: {
            random_public_id: params.id
          }
        })
      ]);

      return { actors, returnedListing };

Dummy API object

{
    "id": 15,
    "title": "NUmber 15",
    "start_date": "2021-03-04",
    "end_date": "2021-02-16",
    "location": "The Bronx",
    "overview": "sdfds",
    "studio": "sdfdsf",
    "poster": null,
    "crew_positions": "Set Photographer, Producer, Production Designer",
    "post_production_positions": "Editing, AD",
    "random_public_id": null,
    "date_submitted": null,
    "user": 1
}

Essentially I'm looking to figure out how to loop through returnedListing[0].crew_positions if it's value is ['Set Photographer', 'Producer'] and have those 2 boxes checked while 'Production Designer' remains unchecked.

Upvotes: 0

Views: 618

Answers (1)

muka.gergely
muka.gergely

Reputation: 8329

The first problem (as mentioned in the comments) that the crew_positions is not an array, but a comma-separated string. Then you can iterate over them & set the checkboxes.

const returnedListingArray = [{
    "id": 15,
    "title": "NUmber 15",
    "start_date": "2021-03-04",
    "end_date": "2021-02-16",
    "location": "The Bronx",
    "overview": "sdfds",
    "studio": "sdfdsf",
    "poster": null,
    "crew_positions": "Set Photographer, Producer, Production Designer",
    "post_production_positions": "Editing, AD",
    "random_public_id": null,
    "date_submitted": null,
    "user": 1
  },
  {
    "id": 16,
    "title": "NUmber 16",
    "start_date": "2021-03-04",
    "end_date": "2021-02-16",
    "location": "The Bronx",
    "overview": "sdfds",
    "studio": "sdfdsf",
    "poster": null,
    "crew_positions": "Set Photographer, Production Designer",
    "post_production_positions": "Editing, AD",
    "random_public_id": null,
    "date_submitted": null,
    "user": 1
  }
]

Vue.component("CrewPositionInput", {
  props: ["id", "crewPosition", "checked"],
  methods: {
    handleCbClick() {
      this.$emit("update:position-status", this.crewPosition)
    },
  },
  template: `
    <label
      :for="id"
    >
      <input
        type="checkbox"
        :id="id"
        :value="crewPosition"
        :checked="checked"
        @click="handleCbClick"
      />
      {{ crewPosition }}
    </label>
  `
})

Vue.component("CrewPositions", {
  props: ["id", "possibleCrewPositions", "crewPositions"],
  methods: {
    toggleCrew({
      crew
    }) {
      const positions = this.crewPositions.includes(crew) ?
        this.crewPositions.filter(item => item !== crew) : [...this.crewPositions, crew]
      this.$emit("update:crewPositions", positions)
    },
  },
  template: `
    <div>
      <crew-position-input
        v-for="position in possibleCrewPositions"
        :key="position + id"
        :id="position + id"
        :crew-position="position"
        :checked="crewPositions.includes(position)"
        @update:position-status="(crew) => toggleCrew({ crew })"
      />
    </div>
  `
})

new Vue({
  el: "#app",
  data() {
    return {
      returnedListings: [],
      possibleCrewPositions: [],
    }
  },
  mounted() {
    this.returnedListings = returnedListingArray.map(({
      crew_positions,
      id,
      // ...rest // commenting out - this is just a snippet, no need for large objects
    }) => ({
      id,
      crew_positions: crew_positions.split(", "), // splitting string to array
    }))

    // just to make it a little more dynamic:
    this.possibleCrewPositions = [...new Set(this.returnedListings.reduce((a, c) => {
      return [...a, ...c["crew_positions"]]
    }, []))]
  },
  template: `
    <div>
      <crew-positions
        v-for="listing in returnedListings"
        :key="listing.id"
        :id="listing.id"
        :possible-crew-positions="possibleCrewPositions"
        :crew-positions.sync="listing['crew_positions']"
      />
      {{ returnedListings }}
    </div>
  `
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>


SHORT EXPLANATION

  1. CrewPositionInput is a stateless component that accepts an id, a crewPosition & a checked prop from its parent. The only thing it does is that on click it emits a custom event with the this.crewPosition as payload.
  2. The CrewPositions component is actually a list of CrewPositionInput components, that passes down props to its children and handles the update:position-status custom event coming from them. On any update:position-status custom event, it re-emits an array (that is actually a crewPositions array) to its parent.
  3. The topmost component handles data processing (like crew_positions splitting) & state management (like updating the crew_positions in the stored array of objects (returnedListings). The update is done via .sync (more on sync here) - this is a handy method of doing it, but it has its constraints (like naming of the variables & events must follow. a certain pattern).

possibleCrewPositions is just a way of creating the available checkboxes based on the data source (dynamically).

Upvotes: 1

Related Questions