@mjwarren (Mitch) posted a new topic, subject "How can I improve my select component?". It read:

   Greetings. I'm new to Vue and JS frameworks in general - so I'm struggling quite a bit in to wrap my head around all these new concepts.
    
    **I've been working on a component that finally works** (yay!) but I'm concerned it may not be following best practices. I'd be really grateful to get some advice for improving this code, or to get an opinion on whether this was the best way to write it.
    
    My component is a select input which loads up an array of the last 7 days, with the current day set as the default. The vue instance receives the selected date passed through $emit via a ‘watch'. That's the bit I'm not sure is right - as I assumed v-model would do this.
    
    **TLDR: New to Vue. Wrote a component that works. Would like to get opinions on if it is right.**
    
    * * *
    
    My component on Codepen:  
    CodePen
    
    ### Vue.js - Display Last 7 Days in <select>
    
    ...
    
    HTML:
    [code] 
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
        
        <h1>Last 7 Days</h1>
        
        <div id="app">
           
           <!-- DATE SELECT -->
           <date-select v-model="date"></date-select>
           
           <!-- BROADCAST RESULT -->
           <p v-if="date">VM Date: {{ date }}</p>
           
        </div>
        
    [/code]
    
    JavaScript:
    [code] 
        Vue.component('date-select', {   
           //-----------------------------------------
           // TEMPLATE
           //-----------------------------------------
           
           template: `
                 <select v-model="date">
                    <option v-for="item in lastSevenDays" :value="item">
                       {{ item.title }}
                    </option>
                 </select>
           `,
              
           //-----------------------------------------
           // DATA
           //-----------------------------------------
           
           data: function(){
             return {
                date: {},
                lastSevenDays: [],
             } 
           },
           
           //-----------------------------------------
           // COMPUTED
           //-----------------------------------------
           
           computed: {
              todaysDate: function(){
                 return new Date();
              }
           },
           
           //-----------------------------------------
           // CREATED
           //-----------------------------------------
           
           created: function() {
              this.lastSevenDays = this.getDates(this.todaysDate, 7);
              this.date = this.lastSevenDays[0]; // set initial option as selected
           },
           
           //-----------------------------------------
           // WATCH >> Is this right??
           //-----------------------------------------
           
           watch: {
              date: function() {
                 this.$emit('input', this.date);
              }
           },
            
           //-----------------------------------------
           // METHODS
           //-----------------------------------------
           
           methods: {
                          
              //==================================
              // getDates
              //==================================
              
              getDates: function(startDate, totalDays){
        
                 var dates_arr = [];
        
                 for (var i = 0; i <= totalDays; i++) {
                    
                    var date_obj = {};
                    var currentDate = new Date();      
                    var timeStamp = currentDate.setDate(startDate.getDate() - i);
                    
                    var dateStr = this.dayAsString(currentDate.getDay());
                        dateStr += ", ";
                        dateStr += currentDate.getDate();
                        dateStr += " ";
                        dateStr += this.monthAsString(currentDate.getMonth());
                        dateStr += " ";
                        dateStr += currentDate.getFullYear();
              
                    date_obj.title = dateStr;
                    date_obj.timeStamp = timeStamp;
                    
                    dates_arr.push(date_obj);
                 }
                 return dates_arr;
              },
              
              //==================================
              // monthAsString
              //==================================
              
              monthAsString: function (monthIndex) {
                 var month = new Array(12);
                     month[0]="January";
                     month[1]="February";
                     month[2]="March";
                     month[3]="April";
                     month[4]="May";
                     month[5]="June";
                     month[6]="July";
                     month[7]="August";
                     month[8]="September";
                     month[9]="October";
                     month[10]="November";
                     month[11]="December";
        
                 return month[monthIndex];
              },
              
              //==================================
              // dayAsString
              //==================================
              
              dayAsString: function(dayIndex) {
                 var weekdays = new Array(7);
                     weekdays[0] = "Sunday";
                     weekdays[1] = "Monday";
                     weekdays[2] = "Tuesday";
                     weekdays[3] = "Wednesday";
                     weekdays[4] = "Thursday";
                     weekdays[5] = "Friday";
                     weekdays[6] = "Saturday";
        
                 return weekdays[dayIndex];
              }
           }
        });
        
        //-----------------------------------------
        // VUE
        //-----------------------------------------
        
        new Vue({
           el: '#app',
           data: {
              date: {}
           }
        })
        
    [/code]

After which, @Herteby (Simon Herteby) added

    I would use v-on instead, like:
    [code] 
        <select v-model="date" @input="$emit('input', date)">
        
    [/code]
    
    If you use watch, it would emit an input event even if the value got changed in the parent for example.
    
    Btw, you don't have to use new Array() and specify a length, looks kind of unusual. You can just do like this:
    [code] 
        var weekdays = ['Sunday','Monday','Tuesday']
        
    [/code]

To which @mjwarren (Mitch) replied

    Hi Herteby, thanks for your reply! I've updated the array expression - much simpler I agree.
    
    I tried your v-bind suggestion - but found that by removing ‘watch' the select didn't automatically populate with the first option, which was the main issue I ran into that lead me to use watch. Are there any other tricks for simplicity?
    
    Example:
    
    CodePen
    
    ### Vue.js - Display Last 7 Days in <select> #2
    
    ...

To which @Herteby (Simon Herteby) replied

    It's kind of unusual to have a select emit an input from just being created. But I guess sometimes it's useful. What you can do is emit an event in the created() hook, and maybe add a prop to toggle the behavior:  
    CodePen
    
    ### Vue.js - Display Last 7 Days in <select> #2
    
    ...

To which @mjwarren (Mitch) replied

    That's an interesting approach. Is it common to mix expressions in with the data? Why not use a computed property instead?
    
    I'm not sure I fully understand the ‘this.emitOnCreated' part - won't the emit on the ‘created' perform the same action as without the `this.emitOnCreated` conditional?
    
    One small issue I've run into with the example, (I'm not quite sure how to fix) - is the ‘select' doesn't change the data value on the **second** click, though it is fine on the **third** and subsequent clicks.

Then @Herteby (Simon Herteby) said

    Sure you can use expressions inside data(). They only run once when the component is created, much like created().
    
    A computed property is for when you need it to be recalculated based on reactive data, not necessary here.
    
    The reason I added emitOnCreated is because having a component emit an input event when it's created is not usual behavior, so it's better to have the user of the component specifically turn it on with the prop. Not necessary if you're the only one who's going to be using the component maybe, just an example.
    
    Oh I see. I guess the date model hasn't been updated when the event fires, so it's always one step behind. I fixed it using this.$nextTick:
    
    CodePen
    
    ### Vue.js - Display Last 7 Days in <select> #2
    
    ...

To which @mjwarren (Mitch) replied

    Thanks again Herteby, that makes sense. I could then switch it on from the parent or another component - very handy.
    
    Unfortunately the example still doesn't work for me, clicking the dropdown doesn't change the data until the second click (tested Safari and Chrome).
    
    One more Q with the expression in the data: if the ‘select' wasn't actually a component and its logic was inside the vue instance I wouldn't be able to use an expression - due to ‘data' only accepting an object. In this theoretical example, where would you calculate the date from? Method?

After which, @Herteby (Simon Herteby) added

    Hmm really? That's weird…  
    Works for me in Chrome and Firefox on Linux.
    
    Data should always be a function returning an object. But for some reason, to be friendlier to newbies maybe, `new Vue()` also accepts an object as data.

To which @mjwarren (Mitch) replied

    Okay gotcha. Thanks for all your help!


{'source': 'discourse', 'url': 'https://forum.vuejs.org/t/how-can-i-improve-my-select-component/18800', 'title': 'How can I improve my select component?', 'views': 2065, 'has_accepted_answer': False, 'word_count': 974, 'like_count': 0, 'reply_count': 3, 'participant_count': 2, 'posts': 9, 'tags': '', 'author': '@mjwarren (Mitch)', 'participants': '@mjwarren (Mitch), @Herteby (Simon Herteby)', 'image_url': [None], 'created_at': '2017-10-01T12:34:15.429Z', 'last_posted_at': '2017-10-03T23:39:14.876Z'}