Widget Tricks - Dates

‘Last three months’ default selection

The default start date is set to now minus 3 months, and the default end date is set to now. (MM/DD/YYYY format)

<ods-dataset-context context="ds"
                     ds-dataset="evenements-publics-openagenda"
                     ds-domain="userclub"
                     ds-parameters="{'sort':'date_start'}">
    <div ods-datetime="now">
        <ods-timerange display-time="false" date-format="MM/DD/YYYY" context="ds" default-from="{{ now | momentadd : 'months' : -3 }}" default-to="{{ now }}"></ods-timerange>
    </div>
    <br/>
    <ods-table context="ds"></ods-table>
</ods-dataset-context>

Compute the range date size (in days or hours etc…)

Get the number of hours, days, months or any units you’d like between the from and to selection.

<ods-dataset-context context="ds"
                     ds-dataset="evenements-publics-openagenda"
                     ds-domain="userclub"
                     ds-parameters="{'sort':'date_start'}"
                     ng-init="dates = {'to': '', 'from': ''}">
    <div ods-datetime="now">
        <ods-timerange display-time="false"
                       date-format="MM/DD/YYYY"
                       context="ds"
                       default-from="{{ now | momentadd : 'months' : -1 }}"
                       default-to="{{ now }}"
                       to="dates.to"
                       from="dates.from">
        </ods-timerange>
        {{ nbdays = (dates.to | momentdiff : dates.from : 'days') ; "" }}
        <h4>Current selection : {{ nbdays }} day{{nbdays>1?'s':''}} range</h4>
        <p>This value can then help to have different behavior/displays in your dashboard depending on the date range.
            <br/>The typical usecase is to display a chart with hour precision in case of a small range of a few days, and switch to a day chart precision in case of a several week range selection. The dashboard can then adapt the chart precision to make it more readable.
        </p>
        <h4 ng-if="nbdays <= 90">Less or equals than 3 months range</h4>
        <h4 ng-if="nbdays >90">More than 3 months range</h4>
        <br/>
        <h3>Other date comparators can detect if a date if before or after another one</h3>
        <h4 ng-if="dates.from | isAfter : now">'From' selection is in the futur</h4>
        <h4 ng-if="dates.from | isBefore : (now | momentadd : 'days' : -1)">'From' selection is in the past</h4>
        <h4 ng-if="!(dates.from | isBefore : (now | momentadd : 'days' : -1)) && !(dates.from | isAfter : now)">'From' selection is today !</h4>
        <h4 ng-if="dates.to | isAfter : (now | momentadd : 'days' : 1)">'To' selection is in the futur</h4>
        <h4 ng-if="dates.to | isBefore : now">'To' selection is in the past</h4>
        <h4 ng-if="!(dates.to | isAfter : (now | momentadd : 'days' : 1)) && !(dates.to | isBefore : now)">'To' selection is today !</h4>
    </div>
</ods-dataset-context>

Filter on two different date fields (start and end date)

This example uses the ods-timerange widget to set from and to date, and then filters an events dataset on two different fields.

This is particuarily useful for events datasets with a beginning date of the event and an end date. The timerange selection helps the user to provide his availability, and the result displays events available on this selected period.

<ods-dataset-context context="ds"
                     ds-dataset="evenements-publics-openagenda"
                     ds-domain="userclub"
                     ds-parameters="{'sort':'date_start'}">
    <div ods-datetime="now">
        <ods-timerange display-time="false"
                       date-format="MM/DD/YYYY"
                       context="ds"
                       default-from="{{ now | momentadd : 'days' : -2 }}"
                       default-to="{{ now }}"
                       to="dates.to"
                       from="dates.from"></ods-timerange>
        {{ ds.parameters['q'] = 'date_start:[1900-01-01 TO ' + (dates.to?dates.to:'2099-01-01') + '] AND date_end >= ' + (dates.from?dates.from:'1900-01-01') ; "" }}
        <br/>
        <ods-table context="ds"></ods-table>
    </div>
</ods-dataset-context>

Select current, past or next week

This is an extract of the school canteen menu app

<ods-dataset-context context="menuscantines"
                     menuscantines-dataset="menus-cantines"
                     menuscantines-domain="rennes-metropole"
                     menuscantines-parameters="{'sort':'-date','refine.secteur':'1'}"
                     ng-init="weekselect = 0">
    <div class="container app">
        {{ menuscantines.parameters['q'] = "date:[#now(weeks=" + (weekselect - 1) + ",weekday=6) TO #now(weeks=" +
        (weekselect) + ",weekday=4)]"; ""
        }}
        <div class="weeks-selects">
            <div class="week-select" ng-class="{'week-selected': weekselect == -1}"
                 ng-click="weekselect = -1">
                Past week
            </div>
            <div class="week-select" ng-class="{'week-selected': weekselect == 0}"
                 ng-click="weekselect = 0">
                Current week
            </div>
            <div class="week-select" ng-class="{'week-selected': weekselect == 1}"
                 ng-click="weekselect = 1">
                Next week
            </div>
        </div>
        <ul class="menus">
            <li>
                <div class="ods-box canteenmenu-ods-box header">
                    <div class="logos">
                        <img src="https://discovery.opendatasoft.com/assets/theme_image/rennes_logo.png"/>
                    </div>
                    <h1>
                        School canteen menu <br/>
                        City of Rennes
                    </h1>
                    <div class="row items">
                        <div class="item type">
                            <h2>
                                Starter
                            </h2>
                        </div>
                        <div class="item type">
                            <h2>
                                Main course
                            </h2>
                        </div>
                        <div class="item type">
                            <h2>
                                Vegetables
                            </h2>
                        </div>
                        <div class="item type">
                            <h2>
                                Dairy
                            </h2>
                        </div>
                        <div class="item type">
                            <h2>
                                Desert
                            </h2>
                        </div>
                        <div class="item type">
                            <h2>
                                Snack
                            </h2>
                        </div>
                    </div>
                </div>
            </li>
            <li ng-repeat="menu in menus"
                ods-results="menus"
                ods-results-context="menuscantines"
                class="menu">
                <div class="ods-box">
                    <h2>
                        <span class="stronger">{{ menu.fields.date | date : 'fullDate' | capitalize }}</span>
                    </h2>
                    <div class="row items">
                        <div class="item plat">
                            <h3>
                                {{ menu.fields.entree }}
                            </h3>
                            <div class="item logo">
                                <!--h3>
                {{ menu.fields.code_entree }}
            </h3-->
                                <img ng-if="menu.fields.code_entree.indexOf('AB') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/ab.png"/>
                                <img ng-if="menu.fields.code_entree.indexOf('VBF') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/vbf.png"/>
                                <img ng-if="menu.fields.code_entree.indexOf('VPF') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/vpf.png"/>
                                <img ng-if="menu.fields.code_entree.indexOf('EBR') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/ebr.jpg"/>
                            </div>
                        </div>
                        <div class="item plat">
                            <h3>
                                {{ menu.fields.plat }}
                            </h3>
                            <div class="item logo">
                                <!--h3>
                {{ menu.fields.code_plat }}
            </h3-->
                                <img ng-if="menu.fields.code_plat.indexOf('AB') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/ab.png"/>
                                <img ng-if="menu.fields.code_plat.indexOf('VBF') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/vbf.png"/>
                                <img ng-if="menu.fields.code_plat.indexOf('VPF') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/vpf.png"/>
                                <img ng-if="menu.fields.code_plat.indexOf('EBR') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/ebr.jpg"/>
                            </div>
                        </div>
                        <div class="item plat">
                            <h3>
                                {{ menu.fields.legumes }}
                            </h3>
                            <div class="item logo">
                                <!--h3>
                {{ menu.fields.code_legumes }}
            </h3-->
                                <img ng-if="menu.fields.code_legumes.indexOf('AB') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/ab.png"/>
                                <img ng-if="menu.fields.code_legumes.indexOf('VBF') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/vbf.png"/>
                                <img ng-if="menu.fields.code_legumes.indexOf('VPF') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/vpf.png"/>
                                <img ng-if="menu.fields.code_legumes.indexOf('EBR') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/ebr.jpg"/>
                            </div>
                        </div>
                        <div class="item plat">
                            <h3>
                                {{ menu.fields.laitage }}
                            </h3>
                            <div class="item logo">
                                <!--h3>
                {{ menu.fields.code_laitage }}
            </h3-->
                                <img ng-if="menu.fields.code_laitage.indexOf('AB') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/ab.png"/>
                                <img ng-if="menu.fields.code_laitage.indexOf('VBF') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/vbf.png"/>
                                <img ng-if="menu.fields.code_laitage.indexOf('VPF') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/vpf.png"/>
                                <img ng-if="menu.fields.code_laitage.indexOf('EBR') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/ebr.jpg"/>
                            </div>
                        </div>
                        <div class="item plat">
                            <h3>
                                {{ menu.fields.dessert }}
                            </h3>
                            <div class="item logo">
                                <!--h3>
                {{ menu.fields.code_dessert }}
            </h3-->
                                <img ng-if="menu.fields.code_dessert.indexOf('AB') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/ab.png"/>
                                <img ng-if="menu.fields.code_dessert.indexOf('VBF') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/vbf.png"/>
                                <img ng-if="menu.fields.code_dessert.indexOf('VPF') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/vpf.png"/>
                                <img ng-if="menu.fields.code_dessert.indexOf('EBR') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/ebr.jpg"/>
                            </div>
                        </div>
                        <div class="item plat">
                            <h3>
                                {{ menu.fields.gouter }}
                            </h3>
                            <div class="item logo">
                                <!--h3>
                {{ menu.fields.code_gouter }}
            </h3-->
                                <img ng-if="menu.fields.code_gouter.indexOf('AB') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/ab.png"/>
                                <img ng-if="menu.fields.code_gouter.indexOf('VBF') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/vbf.png"/>
                                <img ng-if="menu.fields.code_gouter.indexOf('VPF') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/vpf.png"/>
                                <img ng-if="menu.fields.code_gouter.indexOf('EBR') >= 0"
                                     src="https://discovery.opendatasoft.com/assets/theme_image/ebr.jpg"/>
                            </div>
                        </div>
                    </div>
                    <div class="row items">
                    </div>
                </div>
            </li>
        </ul>
    </div>
</ods-dataset-context>
.app {
    max-width: 760px;
}
ul {
    list-style: none;
}
.header {
    position: relative;
}
.logos {
    position: absolute;
    right: 15px;
}
.logos img:nth-child(1) {
    height: 50px;
}
/*.logos img:nth-child(2) {
position: absolute;
right: 0;
width: 149px;
height: 49px;
}*/
.menus {
    text-align: center;
}
h1 {
    margin: 0px 0px 30px 15px;
    text-align: left;
    margin-top: 3px;
}
.menu h2 {
    margin-bottom: 15px;
    font-size: 1.2em;
}
.items {
    display: flex;
    justify-content: space-around;
    flex-wrap: wrap;
}
.type {
    margin: 0px 5px;
}
.logo {
    width: 110px;
}
.logo img {
    max-height: 30px;
    max-width: 30px;
}
.item.type h2 {
    width: 105px;
}
.item.plat h3 {
    width: 110px;
    font-size: 1.1em;
}
.stronger {
    font-weight: 400;
}
.weeks-selects {
    display: flex;
    justify-content: center;
}
.week-select {
    padding: 5px 0;
    margin: 5px;
    border: 1px solid #1f0d3340;
    width: 201px;
    text-align: center;
}
.week-select:hover {
    border-bottom: 1px solid #1f0d33;
}
.week-selected {
    border-color: #1f0d33;
    font-weight: 500;
    background-color: white;
}
@media print {
    header, footer, #debug, .weeks-selects, .external-links {
        display: none;
    }
    .ods-box.header {
        border: none;
    }
}

Single date picker + range slider

This example highlights two usages. The ability to have a single date picker instead of two usually. But also the use of sliders to play with date ranges.

This combination is pretty useful to facilitate range selection for the end user.

<ods-dataset-context context="ds,timerange"
                     ds-dataset="evenements-publics-openagenda"
                     ds-domain="userclub"
                     timerange-dataset="evenements-publics-openagenda"
                     timerange-domain="userclub"
                     ds-parameters="{'sort':'date_start'}"
                     ng-init="dates = {'to': '', 'from': ''};
                                              dayrange = 7">
    <p style="text-align: center">
        <strong>Current selection:</strong> events starting from the <strong>{{ dates.from | date }}</strong> to the
        <strong>{{ (dates.from | momentadd : 'days' : dayrange) | date }}</strong> (<strong>+{{ dayrange }}
        day{{dayrange>1?'s':''}}</strong>)
    </p>
    <div class="controlers">
        <div class="controler control-date">
            <h2>
                Starting date
            </h2>
            <div ods-datetime="now">
                <ods-timerange display-time="false"
                               date-format="MM/DD/YYYY"
                               context="timerange"
                               default-from="{{ now }}"
                               default-to="{{ now }}"
                               to="dates.to"
                               from="dates.from">
                </ods-timerange>
            </div>
        </div>
        <div class="controler control-range">
            <h2>
                Day range
            </h2>
            <div class="input-range-with-button">
                <input type="range" min="0" max="14" step="1"
                       ng-model="dayrange"
                       ng-model-options="{ debounce: { 'default': 300 }}"/>
            </div>
        </div>
    </div>
    <pre>{{ ds.parameters['q'] = 'date_start:[' + dates.from + ' TO ' + (dates.from | momentadd : 'days' : 1*dayrange + 1) + ']' ;  }}</pre>
    <br/>
    <ods-chart>
        <ods-chart-query context="ds" field-x="date_start" maxpoints="0" timescale="day">
            <ods-chart-serie chart-type="column" function-y="COUNT" label-y="Number of events" color="#2C3F56"
                             scientific-display="true">
            </ods-chart-serie>
        </ods-chart-query>
    </ods-chart>
</ods-dataset-context>
/* TIMERANGE */
.odswidget-timerange__to {
    display: none;
}
.odswidget-timerange {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
}
.input-holder, .odswidget-timerange__from {
    border: 1px solid #e4e4e4;
    padding: 10px;
    border-radius: 3px;
    box-shadow: 1px 1px 5px gainsboro;
    background-color: white;
    z-index: 2;
    margin-bottom: 10px;
    display: flex;
    align-items: center;
    margin: 3px;
}
.odswidget-timerange__from {
    margin-right: 10px;
}
.input-holder input, .odswidget-timerange__from input {
    border: none;
    width: 110px;
    margin-left: 8px;
}
.input-holder input:focus, .odswidget-timerange__from input:focus {
    outline: 0;
}
/* SLIDER / RANGE */
.controler {
    display: flex;
    margin: 2px 15px;
}
.controler h2 {
    line-height: 2em;
    margin-right: 10px;
}
.control-hour input[type=range] {
    width: 150px;
}
.control-range input[type=range] {
    width: 200px;
}
.input-range-with-button {
    line-height: 3.5em;
}
.controler .ods-button i.fa {
    vertical-align: middle;
}