1

In my app, I have got different tasks, each having a due date thay may be like following:

Jun 6, 2014
Apr 12, 2014 @ 1pm
Daily @ 1pm

In my view, I am using ng-repeat to show the tasks and their related information. My ng-repeat looks like below:

  <ul class="tasks">
   <li class="single-task">
       <span class="task-title">{{task.title}}</span>
       <span class="task-duedate">{{task.dueDate}}</span>
   </li>

What I want, is to show the dates differently i.e. instead of showing them the way that they are stored, I want the date to be rendered inside the <span class="task-duedate"></span> like the following:

<span class="task-duedate">
    <span class="date-daymon">Apr 12</span>
    <span class="date-year">2014</span>
    <span class="date-time">1 pm</span>
</span>

So, I have creatd and applied a filter called NormalizeDateTime

<span class="task-duedate">{{task.dueDate | NormalizeDateTime }}</span>

that generates the desired html and returns it.

app.filter('NormalizeDate', function(){
return function( input ) {
        ...
        ...
        return '<span..>' + date + '</span><span..>' + year + '</span><span..>' + time + '</span>';
    };
});

Now the problem I am having is angular, instead of making the browser render the returned html, is showing the date part returned by the filter as it is i.e. in the form of plain text. Now the question is, how may I make it render the text returned by the NormalizeDateTime filter as html?

P.S I have also tried triple braces {{{task.dueDate | NormalizeDateTime}}}, as we do in HandlebarsJS, but that doesn't work either.

UPDATE I have tried to achieve the same using a custom directive:

<span class="task-content group cursor-text group left">
    <span class="task-text cursor-text">{{task.title}}</span>
    <span class="task-trackedtime right">{{task.trackedTime}}</span>
    <span class="task-datetime right" duedate="{{task.dueDate}}"></span>
</span>

Below is how my directive looks like:

app.directive('duedate', [function () {
    return {
        restrict: 'A',
        // transclude: true,
        replace: true,
        template: '<span class="task-datetime right">{{dat}}</span>',
        link: function (scope, iElement, iAttrs) {
            // scope.dat = 'Testing Date';
            console.log(iAttrs.duedate);
        }
    };
}])

But I'm unable to access iAttrs.duedate inside the link of my directive. Why is it so?

5 Answers 5

2

I also had problems with the filter not being able to generate HTML and reverted back to a directive, too.

I would suggest a small change to your directive, i.e. add a scope definition as seen below:

app.directive('duedate', [function () {
    return {
        restrict: 'A',
        replace: true,
        scope: {
            dat: '@duedate'
        },
        template: '<span class="task-datetime right">{{dat}}</span>',
        link: function (scope, iElement, iAttrs) {
            // scope.dat = 'Testing Date';
            console.log(scope.dat);
        }
    };
}])

By adding scope: { dat: '@duedate' } you specify that the directive's scope dat property should be set to the interpreted value of the duedate attribute.

Sign up to request clarification or add additional context in comments.

Comments

1
+100

DOM manipulation should only be done in a directive, so using a filter is not appropriate.

You don't include an example of a task so I'll assume that it looks something like:

{ 
    id: 1, 
    dueDate: new Date( 2014, 5, 13, 13, 30, 0 ), 
    daily: false 
}

A task should be passed to the directive rather than the dueDate because the directive needs to know when to show the word Daily, which it can't do given just a date.

Here's a directive that should hopefully do what you want:

.directive('dueDate', function($filter) {

    return {
        restrict: 'E',
        scope: {
            task: '='
        },
        replace: true,
        template: '<span class="task-duedate"> \
                      <span class="date-daymon">{{daymon}}</span> \
                      <span class="date-year">{{year}}</span> \
                      <span class="date-time">@ {{time}}</span> \
                  </span>',
        link: function(scope){

            if( scope.task.daily ){
                scope.daymon = 'Daily';
            }
            else {
                scope.daymon = $filter('date')(scope.task.dueDate, 'MMM d');
                scope.year = $filter('date')(scope.task.dueDate, 'yyyy');
            }

            scope.time = $filter('date')(scope.task.dueDate, 'h a');
        }
    };
})

Usage:

<div ng-repeat='task in tasks'>
   <due-date task='task'></due-date>
</div>

Fiddle

3 Comments

Thanks for your answer. Would you mind giving me the link to where I can read about scope object i.e. scope: { task: '=' } ?
From the angular docs you could try docs.angularjs.org/guide/…. For a good explanation of the different declarations of scope variables see stackoverflow.com/questions/14050195/…
Congrats @Gruff Bunny. You've got +100 ;-)
1

Not sure but try something in the lines of using ngBindHtml directive in your span

<span class="task-duedate" ng-bind-html='task.dueDate | NormalizeDateTime'></span>

and see if it works. Remember to include the ngSanitize module.

See documentation http://docs.angularjs.org/api/ng/directive/ngBindHtml

Comments

1

You should use the ng-bind-html directive documented here

For your code it should look like:

<span ng-bind-html='task.dueDate | NormalizeDateTime'></span>

You controller should also have 'ngSanitize' as dependency, else you will have an exception.

Comments

1

Here ya go! http://jsfiddle.net/Uv6CP/

The trick with getting interpolated values in attributes to work is that you have to call iAttrs.$observe to get the interpolated value.

myApp.directive('duedate', function() {
    return {
        restrict: 'A',
        replace: true,
        template: '<div><span class="date-daymon">{{day}}</span><br><span class="date-year">{{year}}</span><br><span class="date-time">{{time}}</span><br><hr></div>',
        link: function (scope, iElement, iAttrs) {
            iAttrs.$observe('duedate', function(val) {
                console.log("duedate = " + val);
                var parts = val.split('@');
                if (parts.length > 1) {
                    scope.time = parts[1];
                } else {
                    scope.time = "";
                }
                var moreParts = parts[0].split(',');
                if (moreParts.length > 1) {
                    scope.year = moreParts[1];
                } else {
                    scope.year = "";
                }
                scope.day = moreParts[0];
            });
        }
    };
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.