0

I am creating a simple form where we have 2 date input and check validity of date object; I am following prototype pattern and create 2 method .

this was working fine so far but when I use Promise inside validate method it gives error "can not read property 'startDate' of undefined". so my question is why this is undefined in this case?

and how to solve it?

below is my snippet.

(function() {
    'use strict';


    let startDate, endDate;

    const _calculate = function() {
        // console.log('inside constructor');
        this.endDate = new Date();
        this.ageFind = true; // for first tab
    };

    _calculate.prototype = {
        //function to get data for calculating the time diff (setting startDate, endDate )
        init: function(formName) {
            // console.log("getDifference");
            if (this.countdown) {
                clearInterval(this.countdown);
            }
            // other form name is 'secondForm'
            this.ageFind = (formName === 'firstForm') ? true : false;
            var fd = new FormData(document.getElementById(formName));
            // Start date
            let startDay = fd.get('start_date');
            let startMonth = fd.get('start_month');
            let startYear = fd.get('start_year');
            this.startDate = new Date(`${startMonth}-${startDay}-${startYear}`);
            // End date
            let endDay = fd.get('end_date');
            let endMonth = fd.get('end_month');
            let endYear = fd.get('end_year');
                this.endDate = new Date(`${endMonth}-${endDay}-${endYear}`);
           
            this.isValidDate().then((res) => {
                console.log('is valid');
            }).catch((err) => {
                console.error('Invalid date.', err);
            });
        },

        isValidDate: function() {
            console.log("validateDate", this.startDate, this.endDate);
            return new Promise( function(resolve, reject) {
                console.log('inside promise');
                if (isFinite(this.startDate) && isFinite(this.endDate)) {
                    var [startTimeStamp, endTimeStamp] = [this.startDate.getTime(), this.endDate.getTime()];
                    // console.log("startTimeStamp, endTimeStamp", startTimeStamp, endTimeStamp);
                    // swap the date if `end date` is smaller than `start date`
                    if (endTimeStamp < startTimeStamp) {
                        [this.startDate, this.endDate] = [this.endDate, this.startDate];
                    }
                    resolve(true);
                } else {
                    reject(false);
                }
            });
        }
    };

    window._cal = new _calculate();
})();
div {
float: left;
}

.start-date,.end-date {
 margin: 4px;
 }
<form name="firstForm" id="firstForm">
    <fieldset class="start-date">
    <legend>Start Date</legend>
    <select id="start_date" name="start_date">
        <option selected disabled>Start Date</option>
        <option value="1">1</option>
    </select>
    <select id="start_month" name="start_month">
        <option selected disabled>Start Month</option>
        <option value="10">October</option>
    </select>
    <select id="start_year" name="start_year">
        <option selected disabled>Start Year</option>
        <option value="2016">2016</option>
        <option value="2017">2017</option>
    </select>
    </fieldset>
    <fieldset class="end-date">
    <legend> End date</legend>
        <select id="end_date" name="end_date">
        <option selected disabled>End Date</option>
        <option value="1">1</option>
    </select>
    <select id="end_month" name="end_month">
        <option selected disabled>End Month</option>
        <option value="10">October</option>
    </select>
    <select id="end_year" name="end_year">
        <option selected disabled>End Year</option>
        <option value="2016">2016</option>
        <option value="2017">2017</option>
    </select>
    </fieldset>
    <br/>
    <button type="button" onclick="_cal.init('firstForm')">Calculate!</button>
</form>
xkeshav
  • 53,360
  • 44
  • 177
  • 245

1 Answers1

2

The context is lost due to function, you need to closure it:

    isValidDate: function() {
         var self = this;
        return new Promise( function(resolve, reject) {
            console.log(self.startDate);
            // ...
        });
    }
dhilt
  • 18,707
  • 8
  • 70
  • 85
  • but I need to swap the dates bind with `this` if the start date is greater than the end date . does that with again bind with this? ad there are other methos which use same variable bind with this. – xkeshav Oct 29 '17 at 13:59
  • You could change all `this` to `self`. It has the same meaning inside function() block. – egon12 Oct 29 '17 at 14:03
  • I understand that changing `self` with `this` change the scope in that method.but there are several methods which use this `startDate` and `endDate` ? that's why added these variables in the global scope of the function. otherwise, I have to send this new start date and end date in each method? – xkeshav Oct 29 '17 at 14:09
  • There's no way around doing this if you're using nested functions *unless* you use arrow functions or `bind`/call`/`apply`. Since you're using `Promise` it is likely you're able to use arrow functions, so this code could change to `isValidDate: function() { return new Promise((resolve, reject) => console.log(this.startDate)) }` – Dan Oct 29 '17 at 14:15
  • unable to use the narrow function in prototype methods like this `isvalidDate: () => { return new Promise() }` I have to write function literal in prototype method. I have tried that already. – xkeshav Oct 29 '17 at 14:21
  • Use the code I suggested. The code you wrote is completely wrong. You use the arrow function within the first function literal, you don't replace the first function literal with it. Please [read up on how arrow functions work](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) – Dan Oct 29 '17 at 14:42