Set a ReactiveDict var to change date field css class

I’ve a database that has a createdAt field of type date. Now I want to change the background-color of this field in my template according to it duration. That is, if it was create before one hour, it will only present #minutes with a css class of red. If it was created about before 1day #hour(s) with a blue bg-color. If it was created before a week, #days and a yellow bg-color…same for weeks and months.
Here is where I got so far in my template.onRendered

var dt = Date.now();
const results = Todos.find({}, { sort: { createdAt: -1 } });

var MS_IN_A_MONTH = 1000 * 60 * 60 * 24 * 30;
var MS_IN_A_WEEK = 1000 * 60 * 60 * 24 * 7;
var MS_IN_A_DAY = 1000 * 60 * 60 * 24;
var MS_IN_A_HOUR = 1000 * 60 * 60;
var MS_IN_A_MINUTE = 1000 * 60;

    results.forEach((todos) => {
        var elem = `${ todos.createdAt.toString() }`;
        var difference = dt - Date.parse(elem);
        console.log('parse ' + dt + 'Difference ' + difference);
        var monthDifference = Math.floor(difference / MS_IN_A_MONTH);
        var weekDifference = Math.floor(difference / MS_IN_A_WEEK);
        var daysDifference = Math.floor(difference / MS_IN_A_DAY);
        var hourDifference = Math.floor(difference / MS_IN_A_HOUR);
        var minDifference = Math.floor(difference / MS_IN_A_MINUTE);
       
        if (monthDifference >= 1 && weekDifference > 3) {
            return 'label label-default';
        } else if (weekDifference <= 3 && weekDifference >= 1) {
            return 'label label-primary';
        } else if (daysDifference < 7 && daysDifference >= 1) {
            return 'label label-success';
        } else if (hourDifference < 24 && hourDifference >= 1) {
            return 'label label-danger';
        } else if (hourDifference <= 0 && minDifference <= 1) {
            return 'label label-warning';
        }
});

This method is either reactive nor working. It’s simply not returning anything, Is there any better way to achieve this using reactivedict or reactive var?

Hey. I think I know where you are heading with this. So if I understand correctly, you want based on the database ‘createdAt’ field to change the color depending on some business rules. This means its per record right? For this to work and to make it work in Javascript and with Meteor there are a couple of concepts that I would like to share to make your problem a bit more digestable and in the end much easier:

Working with dates is a pain
Luckily there is a package that helps the javascript community! momentjs helps us to do calculations, durations, etc. I recommend you to use it and not try to do it yourself unless you just want to learn how to calculate with dates and vanilla Javascript. But in that case I would not do it in combination with Meteor. The method you might want to look for is moment.diff()

I do want to honour you for your good practice in declaring constants on top with capitals and making statements clear and descriptive.

Regarding Blaze onRendered vs onCreated
Blaze’s hooks expect no return values. Instead you can assign vars to the template instance to make them available to the html / handlebars part. Another thing to keep in mind is that onRendered is triggered after the HTML is rendered. A better practice is to put all data formatting / manipulations / subscriptions on the onCreated hook. It works exactly the same, but on an earlier stage:

Template.something.onCreated(function() {
    this.someVar = 'foo'; //Will assign the var to the template instance!
});

However, you want to just tweak some variables dynamically per document. In that case its much easier to use helpers. A helper is responsible for adding fields. Small and single purpose reactive functions.

Template.something.helpers({
    someField() {
        return 'dynamic val';
    };
});

Parent child relations in Blaze
In your case you just want to tweak some data. This means its better to just create 2 templates. 1 parent and a child template. Parent to load the documents and a child to display it and manipulate on a per record basis. It should look like this:

Parent template displays 1 list with many children

<template name="parentList">
    <div>
        {{#each doc in docs }}
            {{> childDocTemplate doc }}
        {{/each}}
    </div>
</template>

Child template displays 1 doc

<template name="childDocTemplate">
    <p class="{{className}}">
        {{createdAt}}
    </p>
</template>

In your parent helper:

Template.parentList.onCreated(function() {
    this.subscribe('todos'); //Subscribe to data
});

Template.parentList.helpers(function() {
    className() {
        const now = moment();
        //Notice the 'this'. It contains the current doc
        const createdAt = moment(this.createdAt); 
        const diffInMonths = now.diff(createdAt, 'months');
        const diffInWeeks = now.diff(createdAt, 'weeks');
        
       
        if (diffInMonths >= 1 && diffInWeeks > 1) {
            return 'label label-default';
        }  //etc. etc.
    }
/**

Now repeat the above for formatting the actually createdAt timestamp
*/

});
2 Likes

There’s also date-fns which does the same things, but rather than turning your Date into a Moment object, it just provides a library of functions. I’ve been trying it out recently, it seems pretty nice. You lose the chaining, but it’s simpler to use.

@cloudspider You Rock:+1: I could do it using your suggestion. That was a fast great help to me. I already had the moment package installed but didn’t know you could do something like that with it. Thanks again. Look like there is no way to avoid templates helpers like suggested in this video.
For some reason, I had to define the helper in my childTemplate for it to work. Doing this in the parentTemplate does not work. That mean:

Template.parentList.helpers(function() {
    ...
});

Has to be

Template.childList.helpers(function() {
    ...
});

Parent Template - Todos:

{{#if Template.subscriptionsReady}}
    {{#each todos}} 
          {{> Todo}} 
    {{/each}}
          {{#if hasMoreTodos}}
              <ahref="#">Load More</a>
          {{/if}}
    {{else}}
          <div>loading...</div>
{{/if}}

Child Template - Todo:

<span class="text">{{body}}</span>
<small class="{{className}}"><i class="fa fa-clock-o"></i>{{timeAgo createdAt}}</small>

Look like the problem may come from {{#if Template.subscriptionsReady}}.