0

I have the following columnDefs

self.columnDefs = [
        { width: 150, field: 'timeReceived', displayName: 'Time Received', cellFilter: function (data) { return moment(data).format('DD/MM/YYYY h:mm a') } },
        { width: 500, field: 'name', displayName: 'Name' },
        { width: 150, field: 'LinkToTimeSent', displayName: 'Time SentX', cellTemplate: '<a data-bind="text:$parent.entity.timeSent, attr: { href: $parent.entity.linkToTimeSent}" ></a>' },
    ];

My problem is with the Time SentX. I'd like this to display the content of entity.timeSent but converted for human consumption using the moment library.

How can I call the function moment($parent.entity.timeSent).format('DD/MM/YYYY h:mm a') from within my columnDefs?

In the following plunk, line 96 needs to contain something like

text:moment($parent.entity.TimeSent, "DD/MM/YYYY h:mm a") but I can't get it to work!

:https://plnkr.co/edit/jNn3knbnCCbBQu9NgKze?p=preview

Rob Bowman
  • 7,632
  • 22
  • 93
  • 200
  • what is the problem with using the cellFilter like in your question: `{ field: 'TimeSent', displayName: 'TimeSent', width: 130, cellFilter: function (data) { return moment(data).format('DD/MM/YYYY h:mm a')} }, ` here it is working fine: https://plnkr.co/edit/sqNyTmm4ZisVg1CzVSIt?p=preview – nemesv May 23 '16 at 17:42
  • Hi @nemesv, I want the text within the cell to be a hyperlink. So, it should display the time sent but when the user clicks this, navigate to a copy of the message that was sent. This is why I'm using cellTemplate rather than cellFilter – Rob Bowman May 23 '16 at 19:07

1 Answers1

1

Edit: My answer was a bit too general. An attempt to be more specific.

Map your WorkflowRules to their own "viewmodels", and you can do anything you like:

this.workflowRules = ko.observableArray(sampleData.WorkflowRules
  .map(function(jsonRule) {

    // Add UI helpers (create a sort of viewmodel)
    var timeSentMoment  = moment(jsonRule.TimeSent);

    // Purely for display purposes:
    jsonRule.timeSentLabel = timeSentMoment.format('DD/MM/YYYY h:mm a');
    // Do what ever you want to create the right url
    jsonRule.href = jsonRule.TimeSent; 

    return jsonRule;
}));

Then, in your template:

<div data-bind="with: $parent.entity">
  <a data-bind="text: timeSentLabel, 
                attr: {href: href}"></a>
</div>';    

Which is defined in js:

var timeSentTemplate = '<div data-bind="with: $parent.entity"><a data-bind="text: timeSentLabel, attr: {href: href}"></a></div>';

var columnDefs = [
  { cellTemplate: timeSentTemplate, /* etc. */ }
];

I hope I finally got your question correctly...

(https://plnkr.co/edit/93ucvDLk5bUFtU4dB1vn?p=preview, moved some stuff around)


Previous, more general answer:

When you create a knockout binding, knockout automatically wraps the second part of the binding in a function. For example:

data-bind="text: myTextObservable"

Is processed as:

text: function (){ return myTextObservable }

Additionally, knockout uses this function to create a computedObservable. This will create a subscription to any observable used inside the function, making sure the data-bind is re-evaluated if any of them changes.

This means that in your case, you can define your format rule inside your data-bind like so (assuming timeSent is an observable`):

data-bind="text: moment($parent.entity.timeSent()).format('DD/MM/YYYY h:mm a') "

Knockout will see that the timeSent observable is called and make sure the whole binding gets updated correctly. Here's an example:

var date = ko.observable(new Date());
var addDay = function() {
  date(moment(date())
    .add(1, "day")
    .toDate()
  );
};

ko.applyBindings({
  date: date,
  addDay: addDay
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.13.0/moment.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<strong>The "raw" observable</strong>
<div data-bind="text: date"></div>
<br/>
<strong>A "computed" text defined in the data-bind</strong>
<div data-bind="text: moment(date()).format('DD-MM-YY')"></div>
<button data-bind="click:addDay">add a day</button>

My advice however is to create a separate computed observable inside your viewmodel. After all, this is what viewmodels are meant to do, and it will help you out a great deal when fixing bugs. I.e.:

// Add to your entity view model:
this.timeSentLabel = ko.computed(function() {
  return moment(this.timeSent())
    .format('DD/MM/YYYY h:mm a');
}, this);
user3297291
  • 22,592
  • 4
  • 29
  • 45
  • Thanks for your answer but I need to call the moment function from within the columnDefs of the koGrid gridOptions function. I have tried to illustrate in the following plunk: https://plnkr.co/edit/jNn3knbnCCbBQu9NgKze?p=preview – Rob Bowman May 23 '16 at 17:28
  • It's not an observable? The first approach I've shown (which is very similar to what you tried) still works, right? https://plnkr.co/edit/131jE4ztY4FRUBRqWr7I?p=preview – user3297291 May 23 '16 at 17:44
  • I can display the timestamp in a human readable way. I can also display a link that works when clicked. What I haven't managed to do is combine the two! I need to somehow wrap a call to moment around the value provided to the text binding of the tag: cellTemplate: '' – Rob Bowman May 23 '16 at 19:16
  • I had another attempt to answer your question, after your clarifications. I've edited my post. – user3297291 May 23 '16 at 20:31
  • That's great thanks. I hadn't used the .map function of a javascript array before - very helpful! – Rob Bowman May 24 '16 at 11:38