2
\$\begingroup\$

I'm iterating through a list to generate calendars, the issue is each generator function has the same this.addToCalendar() function. I'd like to know how I could condense this into only being called once, rather than in each function.

The main generators I'd like to look at are the ones within method, where I am generating calendar links as well as calling an external api.

CSS has been removed to focus on JS logic.

<template>
<div>
  <ul>
    <li v-for="(calendar, i) in calendars" :key="i">
      <button @click="calendar.generator">
        <span>{{ calendar.type }}</span>
      </button>
    </li>
  </ul>
  <button @click="setExpanded(false)">
    Close
  </button>
</div>
</template>

<script>
import {
  ICalendar,
  GoogleCalendar,
  OutlookCalendar,
  YahooCalendar,
} from 'datebook'
import { mapActions, mapGetters } from 'vuex'

export default {
  data() {
    return {
      calendars: [
        { type: 'Apple', generator: this.generateAppleCalendar },
        { type: 'Google', generator: this.generateGoogleCalendar },
        { type: 'Outlook', generator: this.generateOutlookCalendar },
        { type: 'Yahoo', generator: this.generateYahooCalendar },
      ],
      success: null,
      error: null,
    }
  },
  computed: {
    ...mapGetters({
      event: 'event/event',
      expanded: 'calendar/expanded',
      userType: 'user/userType',
    }),
    eventLocation() {
      return `${this.event.location_street}, ${this.event.location_city}, ${this.event.location_state}. ${this.event.location_zip}`
    },
    generateConfig() {
      const config = {
        title: this.event.title,
        location: this.eventLocation,
        description: this.event.description,
        start: new Date(this.event.start_time),
        end: new Date(this.event.end_time),
      }
      return config
    },
  },
  methods: {
    ...mapActions({
      setExpanded: 'calendar/setExpanded',
    }),
    generateAppleCalendar() {
      const icalendar = new ICalendar(this.generateConfig)
      icalendar.download()
      this.addToCalendar()
    },
    generateGoogleCalendar() {
      const googleCalendar = new GoogleCalendar(this.generateConfig)
      this.openInNewTab(googleCalendar.render())
      this.addToCalendar()
    },

    generateOutlookCalendar() {
      const outlookCalendar = new OutlookCalendar(this.generateConfig)
      this.openInNewTab(outlookCalendar.render())
      this.addToCalendar()
    },
    generateYahooCalendar() {
      const yahooCalendar = new YahooCalendar(this.generateConfig)
      this.openInNewTab(yahooCalendar.render())
      this.addToCalendar()
    },
    openInNewTab(url) {
      const win = window.open(url, '_blank') // Opening a new tab with the url
      win.focus() // Focussing the users browser to new tab
    },
    async addToCalendar() {
      let url

      if (this.userType === 'guest') {
        url = `event/${this.event.id}/add-guest-user`
      } else {
        url = `event/${this.event.id}/guest`
      }

      const response = await this.$axios.post(url)
      if (response.status === 201) {
        this.success = true
      } else {
        this.error = true
      }
    },
  },
}
</script>
\$\endgroup\$
1
  • 1
    \$\begingroup\$ There is not enough information in your question to answer. Required is... How the calendars relate? How does instantiating a calendar effect the state and thus behavior of addToCalendar? Do all calendars require the call? Are you expecting only one valid calendar, some calendars to be successful, or all calendars to set this.success = true? What should happen to calendars that result in this.error = true? Do you know ahead of instantiating calendars which you want? \$\endgroup\$
    – Blindman67
    Mar 17 at 10:58
1
\$\begingroup\$

I'd like to know how I could condense this into only being called once, rather than in each function.

One option would be to make a method that accepted the calendar or type and called the associated method

methods:
    generateCalendar(type) {
        const generateMethod = `generate${type}Calendar`
        if (typeof this[generateMethod] === 'function')
          this[generateMethod]()
          this.addToCalendar()
        }
      }

Then pass the type:

<button @click="generateCalendar(calendar.type)">

If the only property being accessed in the loop was that type property then destructuring assignment would allow it to be simplified:

 <li v-for="({ type }, i) in calendars" :key="i">
   <button @click="generateCalendar(type)"> 
      <span>{{ calendar.type }}</span>
    </button
     

Alternatively, with using the method above the generator properties of the calendars could be removed, and calendars could perhaps just be an array of strings.

Also, it seems that three of the four calendar methods are nearly identical other than the class of calendar object created: generateGoogleCalendar, generateOutlookCalendar and generateYahooCalendar. A generic method could be used to replace those three methods (and accept the calendar type as a parameter to be used to dynamically invoke a constructor) or create them dynamically in a loop.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.