Search code examples
javascriptember.jscalendarember-cli

Ember.js weekly calendar navigation


I'm building a simple weekly calendar component for my app and I'm struggling to find a way for creating the weeks navigation. Here's what I have so far:

/week-view/component.js

import Ember from 'ember';

export default Ember.Component.extend({
  firstDay: moment().startOf('week'),

  days: Ember.computed('firstDay', function() {
    let firstDay = this.get('firstDay').subtract(1, 'days');
    let week = [];
    for (var i = 1; i <= 7; i++) {
      var day = firstDay.add(1, 'days').format('MM-DD-YYYY');
      week.push(day);
    }
    return week;
  }),

  actions: {
    currentWeek() {
      this.set('firstDay', moment().startOf('week'));
    },
    previousWeek() {
      this.set('firstDay', moment().startOf('week').subtract(7, 'days'));
    },
    nextWeek() {
      this.set('firstDay', moment().startOf('week').add(7, 'days'));
    }
  }
});

/week-view/template.hbs

<button {{action "previousWeek"}}>previous</button>
<button {{action "currentWeek"}}>current week</button>
<button {{action "nextWeek"}}>next</button>
<ul>
  {{#each days as |day|}}
    <li>{{day}}</li>
  {{/each}}
</ul>

At the moment it works to navigate one week before and one after the current week only. Any idea on how to make this work for an unlimited number of weeks is greatly appreciated. Thanks in advance.


Solution

  • I think you shouldn't change your firstDay property while prepairing week array (in the computed function). It overrides momentjs state. Computed property in this case should just read firstDay property without affecting changes to it.

    Also in your previous and next week actions you don't have to create new momentjs objects. You can easily operate on previously created firstDay property, for example.

    this.get('firstDay').subtract(7, 'days');
    

    But in this case, state of momentjs have changed, but emberjs doesn't see any changes. That is because your firstDay property doesn't really changed (and computed property is set to check only firstDay, it doesn't work deeply). In fact firstDay property is just reference to momentjs object, and that object has been changed, not the reference. But luckily you can manually force emberjs to reload any computed properties based on any property in this way:

    this.notifyPropertyChange('firstDay');
    

    So little bit refactored working example can looks like:

    import Ember from 'ember';
    
    export default Ember.Component.extend({
      selectedWeek: moment().startOf('week'),
    
      days: Ember.computed('selectedWeek', function() {
        let printDay = this.get('selectedWeek').clone();
        let week = [];
        for (var i = 1; i <= 7; i++) {
          week.push(printDay.format('MM-DD-YYYY'));
          printDay.add(1, 'days');
        }
        return week;
      }),
    
      actions: {
        currentWeek() {
          this.set('selectedWeek', moment().startOf('week'));
        },
        previousWeek() {
          this.get('selectedWeek').subtract(7, 'days');
          this.notifyPropertyChange('selectedWeek');
        },
        nextWeek() {
          this.get('selectedWeek').add(7, 'days');
          this.notifyPropertyChange('selectedWeek');
        }
      }
    });