rept
rept

Reputation: 2236

Modifying value of observable values in observableArray

I have an array containing 7 days (week) and a currentDate. When the currentDate goes below the lowest value of the week array, I want to change the dates of every element in the array and shift them by 1 week.

This is the code I have. The subscribe works fine, but the shifting of 1 week doesn't seem to work at all. What am I doing wrong?

Fiddle here: https://jsfiddle.net/rhsr0m9L/

function CalDay(date, total) {
  var self = this;
  this.date = ko.observable(date);
  this.total = ko.observable(total);
  this.dateFormatted = ko.computed(function() {
    return this.date().format('ddd Do');
  }, this);
}

var TimesheetViewModel = function() {
  var self = this;
  this.currentDate = ko.observable(moment().startOf('day'));
  this.week = ko.observableArray([]);

  self.currentDate.subscribe(function () {
    if (self.week()[0] != undefined) {
     if (self.currentDate() < self.week()[0].date()) {
      self.week().forEach(function(d) {
        console.log(d.date())
        d.date(d.date().add(-7, 'days'))
      })
     }
    }
  });

  // fill array to start
  [0,1,2,3,4,5,6].forEach(function(i) {
    self.week.push(new CalDay(self.currentDate().startOf('isoWeek').startOf('day').add(i, 'days'), i));
  });

Upvotes: 0

Views: 261

Answers (1)

jgasiorowski
jgasiorowski

Reputation: 1033

Your ko view model is totally correct. You have only two issues with using momentjs library. Based on documentation moment object is mutable which means that when you initializing your week array you are really modifying currentDate each time and pushing its reference into week array (dates are shown correctly I believe because ko is caching views for performance reasons). Shat's why on start all your dates has grey background because all dates in you week are the same object instance. Then when you change your date it is just big mess because your forEach loop which adds -7 days does that on same moment object 7 times :) that's why you will get seven same days of week separated by week time gap.

Another issue is checking if date is before or equal. Based on documentation you should use .isBefore(..) or .isSame(..) methods instead of < or ==.

I updated your fiddle which should work as you expect https://jsfiddle.net/rhsr0m9L/1/

  function CalDay(date, total) {
    var self = this;
    this.date = ko.observable(date);
    this.total = ko.observable(total);
    this.dateFormatted = ko.computed(function() {
      return this.date().format('ddd Do');
    }, this);
  }

  var TimesheetViewModel = function() {
    var self = this;
    this.currentDate = ko.observable(moment().startOf('day'));
    this.week = ko.observableArray([]);

    this.incDay = function() {
      this.currentDate(moment(this.currentDate()).add(1, 'days'));
    };

    this.decDay = function() {
      this.currentDate(moment(this.currentDate()).add(-1, 'days'));
    };

    this.gotoToday = function() {
      this.currentDate(moment().startOf('day'));
    };

    this.isToday = ko.pureComputed(function() {
      return !this.currentDate().isSame(moment().startOf('day'));
    }, this);

    self.currentDate.subscribe(function() {
      if (self.week()[0] != undefined) {
        if (self.currentDate().isBefore(self.week()[0].date())) {
          self.week().forEach(function(d) {
            console.log(d.date())
            d.date(d.date().add(-7, 'days'))
          })
        }
      }
    });

    [0, 1, 2, 3, 4, 5, 6].forEach(function(i) {
      var temp = moment(self.currentDate());
      self.week.push(new CalDay(temp.startOf('isoWeek').startOf('day').add(i, 'days'), i));
    });
  };

  ko.applyBindings(new TimesheetViewModel());
body {
  padding: 20px;
  font-family: Helvetica;
}

button {
  background: #0084ff;
  border: none;
  border-radius: 5px;
  padding: 8px 14px;
  font-size: 15px;
  color: #fff;
}

.lightgray-back {
  background-color: lightgray;
}

#timesheets_list>tr>th {
  padding-left: 30px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div class="block" id="day-table">
  <div class="infoline mt20">

    <div class="date-sel">
      <div class="input-group narrow-input">
        <span class="input-group-addon" id="calendar-link">
                      <i class="fa fa-calendar"></i>
                    </span>
        <label class="form-control" data-bind="text: currentDate" id="list_date" readonly="true" type="text"></label>
        <button data-bind="click: gotoToday, visible: isToday" id="calendar_today">
        Jump to Today
        </button>
      </div>
    </div>

  </div>
  <div class="weekline mt20">
    <button data-bind="click: decDay">
                Back
                </button>
    <button data-bind="click: incDay">
                Forward
                </button>
  </div>

  <div class="row">
    <div class="col-xs-8 col-xs-offset-2">
      <div class="table-responsive">
        <table class="table">
          <thead id="timesheets_list">
            <tr>
              <th data-bind="text: week()[0].dateFormatted(), css: { 'lightgray-back': currentDate().isSame(week()[0].date()) }"></th>
              <th data-bind="text: week()[1].dateFormatted(), css: { 'lightgray-back': currentDate().isSame(week()[1].date()) }"></th>
              <th data-bind="text: week()[2].dateFormatted(), css: { 'lightgray-back': currentDate().isSame(week()[2].date()) }"></th>
              <th data-bind="text: week()[3].dateFormatted(), css: { 'lightgray-back': currentDate().isSame(week()[3].date()) }"></th>
              <th data-bind="text: week()[4].dateFormatted(), css: { 'lightgray-back': currentDate().isSame(week()[4].date()) }"></th>
              <th data-bind="text: week()[5].dateFormatted(), css: { 'lightgray-back': currentDate().isSame(week()[5].date()) }"></th>
              <th data-bind="text: week()[6].dateFormatted(), css: { 'lightgray-back': currentDate().isSame(week()[6].date()) }"></th>
              <th>Total</th>
            </tr>
          </thead>
          <tbody>
            <tr></tr>
          </tbody>
        </table>
      </div>
    </div>
  </div>

</div>

Upvotes: 1

Related Questions