nemo_87 nemo_87 - 19 days ago 11
AngularJS Question

Error when building custom filter in AngularJs

I am trying to build custom filter, that will change value for datetime that I have in UTC to user's timezone.

Since I am new to angularJs, I am not sure what should I do and where to register filter.

I have created filter as separated file named clientTime.js:

export default function(startDateTimeString) {

let localTime = this.$rootscope.momentTimezone.utc(startDateTimeString).toDate();
localTime = this.$rootscope.momentTimezone(localTime).format("YYYY-MM-DD HH:mm");

return localTime;
}


As an argument to a function I am passing startDateTimeString, which is a string value of date-time in this format: 'YYYY-MM-DD HH:mm'

In my HTML I have used filter like this:

<tr st-select-row="row" st-select-mode="multiple" ng-repeat="row in dashCtrl.upcomingSessionsDisplay" ng-switch on="row.status"
ng-click="dashCtrl.handleClickOnUpcomingSessions(row)" title="{{dashCtrl.tooltip(row)}}">
<td>{{row.locationName}}</td>
<td>{{row.startDateTimeString | clientTime}}</td>
</tr>


I have app.js where I am registering all components such as: services, interceptor, etc. So I figure it out that I should register filter there as well.

I have import filter like all other components:

import toastr from "toastr";
import _ from "underscore";
import momentTimezone from "moment-timezone";
import Components from "./components";
import Directives from "./shared/directives";
import Constants from "./shared/constants";
import Services from "./shared/services";
import Controllers from "./shared/controllers";
import AuthenticationInterceptor from "./shared/interceptors/AuthenticationInterceptor";
import ClientTime from "./shared/filters/clientTime";


And register it with the name 'clientTime':

angular
.module("examino.main", ["ui.router", "smart-table", "ui.bootstrap", "ui.tree", Directives, Components, Constants, Services, Controllers])

.run(function($rootScope, $state, AuthenticationService) {
$rootScope._ = _;
$rootScope.momentTimezone = momentTimezone;

// register listener to watch route changes
$rootScope.$on("$stateChangeStart", function(event, toState, params) {
let goToStateName = "login";
let isAccessTestSessionRoute = toState.name == "testEnv" || toState.name == "testEnv.dashboard" || toState.name == "sessionExpired";
if(toState.name !== goToStateName && !AuthenticationService.isAuthenticated() && !isAccessTestSessionRoute) {
event.preventDefault();
$state.go(goToStateName);
}

if(toState.redirectTo) {
event.preventDefault();
$state.go(toState.redirectTo, params);
}
});
})

.config(function($urlRouterProvider, $locationProvider, $httpProvider) {
$locationProvider.html5Mode(true);

$urlRouterProvider.otherwise("/login");

$httpProvider.interceptors.push("AuthenticationInterceptor");
})
.filter("clientTime", ClientTime)

.service("AuthenticationInterceptor", AuthenticationInterceptor);

angular.bootstrap(document, ["examino.main"]);

//set toastr options
toastr.options = {
positionClass: "toast-top-full-width"
};


I was pretty sure that this will work, but I get this error:

> vendor.js:26519 Error: [$injector:unpr] Unknown provider:
> startDateTimeStringProvider <- startDateTimeString <- clientTimeFilter
> http://errors.angularjs.org/1.5.8/$injector/unpr?p0=startDateTimeStringProvider%20%3C-%20startDateTimeString%20%3C-%20clientTimeFilter
> at vendor.js:12667
> at vendor.js:17110
> at Object.getService [as get] (vendor.js:17263)
> at vendor.js:17115
> at getService (vendor.js:17263)
> at injectionArgs (vendor.js:17287)
> at Object.invoke (vendor.js:17309)
> at Object.enforcedReturnValue [as $get] (vendor.js:17156)
> at Object.invoke (vendor.js:17317)
> at vendor.js:17116


Now I completely understand what is the problem, I guess that angular do not understand dependency for filter. But I am not sure what is the solution for this.
I was starting from the idea that filter is similar to service or interceptor in a way that it is still an anonymous function, so I was thinking that this is the way for defining it. Can somebody share their experience with creating filters and possible solution to dependency problems?

Answer

The problem lies in HOW you are defining your filter. The 'angular.filter' expects a filter factory function, according to the documentation (Writing your own filter is very easy: just register a new filter factory function with your module - https://docs.angularjs.org/guide/filter).

So, your clientTime.js should be

export default function() {
  return function(startDateTimeString) {

    let localTime  = this.$rootscope.momentTimezone.utc(startDateTimeString).toDate();
    localTime = this.$rootscope.momentTimezone(localTime).format("YYYY-MM-DD HH:mm");

    return localTime;
  }
}

As you can see, the module now exports a function that returns your filtering function. Now it should work.