Custom filter (AngularJS)

Hello all,

I’m trying to create a custom AngularJS filter in the same manner that others (in this community) have already created a custom directive. By using ‘oc-lazy-load’ to load the javascript to add a custom directive/filter. However I cannot figure out what I’m doing wrong and why my filter will not work. Below is the code I’m using. It’s worth pointing out that the directive that I create is working just fine, it’s the custom filter on the other hand that I cannot use in my template.

angular.module("backButton", []).directive("backButton", [
  "$window",
  function($window) {
    return {
      restrict: "A",
      link: function(scope, elem, attrs) {
        elem.bind("click", function() {
          $window.history.back();
        });
      }
    };
  }
]);

angular.module("reverse", []).filter("reverse", function() {
  return function(input, uppercase) {
    input = input || "";
    var out = "";
    for (var i = 0; i < input.length; i++) {
      out = input.charAt(i) + out;
    }
    // conditional based on optional argument
    if (uppercase) {
      out = out.toUpperCase();
    }
    return out;
  };
});

In my template I use the lazy-load directive in a seperate

at the top of my template to load my custom js files.

<div oc-lazy-load="['/static/matrix-theme/js/jquery-3.2.1.min.js','/static/matrix-theme/js/matrix.js','/static/matrix-theme/js/app-icons.js']"></div>

When I use the directive in my template, it works exactly as expected - however when I try to use the filter (like in the snipper below) my template fails to render properly…

{{ "Test string" | reverse }}

Obviously I’m not looking for an actual ‘reverse’ filter - this is just a quick test to get me started. I want to make a filter that returns the number of switches/dimmers that are turned on (e.g. either with a state of ‘ON’ or a state > 0 if it’s a dimmer item)

Hopefully someone can point me in the right direction!

Thanks!

I managed to sort of figure out the problem - it has to do with where in the template the script is ‘lazy-loaded’. When I try something like this, it does work…

<div class="name" oc-lazy-load="['/static/matrix-theme/js/matrix.js']">{{ "Lights" | reverse }}</div>

Apparently I’m not yet clear on the inner workings of AngularJS. Maybe someone can shed some light on this for me?

Edit: The location where I previously had the oc-lazy-load statement was in a div that was not a direct descendant of the div where I used the filter… maybe it was out of scope?

According to https://oclazyload.readme.io/docs/oclazyload-directive:

The main interest here is that the content will be included and compiled once your modules have been loaded.
This means that you can use directives that will be lazy loaded inside the oc-lazy-load directive.

In other terms, the ocLazyLoad directive will make sure the additional modules are loaded and only then compile the content (meaning the element’s children), so if you’re trying to use a directive or filter outside the element’s children where you use the ocLazyLoad directive, it might try to compile before the lazy loading occurs - and fail.

Hi @QNimbus and @ysc I’m trying to implement encodeURIComponent() function in my widget, which I’m going to be used for sending http url requests to squeezebox players. The widget will be a Button containing radio station image. By clicking on the widget I want send to squeezebox appropriate link to radio stream in URI format. Parts of the url are taken from defined widget settings and the only I need is to transform http addresses into URIs.

I’ve made the follwing js:

angular.module("encodeuri", []).filter("encodeuri", function() {
  return function(input) {
    return  encodeURIComponent(input);
  };
});

and I’ve made custom widget which looks like this:

<div class="squeezebox-playstream" oc-lazy-load="['/static/converturi.js']">
	<div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; 
                 display: flex; margin: 0; padding: 0; overflow:hidden">
        <button class="btn btn-lg" style="width: 100%; height: 100%; padding: 0; background: inherit; border:0; outline: none; overflow:hidden"
          ng-click="sendCmd('uritestitem', config.server_address + config.address_prefix + {{config.station_address | encodeURIComponent}} + config.address_suffix + config.player_mac)">
      		<div ng-init="image={url: config.logo_url, refresh: config.refresh_period}" style="width: 100%; height: 100%">
        	<widget-image ng-model="image"></widget-image></div>                                                                                                                       
  			</button>
	</div>
</div>

This however doesn’t work. It looks like there is some error even in the javascript itself or in ng-click definition. Can you help me to detect what is wrong? Or maybe you know better way of getting encodeURIComponent function.

You can’t use a filter like that in ng-click.

What you should do is write a controller for your widget instead and define a function in the scope:

angular.module(...).controller('mycontroller', ['$scope', function($scope) {

    $scope.sendURIEncodedCmd = function () {
        $scope.sendCmd('uritestitem', $scope.config.server_address + $scope.config.address_prefix +
                encodeURIComponent($scope.config.station_address) + $scope.config.address_suffix +
                $scope.config.player_mac);
    }

}]);

then call it in your template:

<div class="squeezebox-playstream" oc-lazy-load="['/static/mycontroller.js']">
  <div ng-controller="mycontroller">
    <button ng-click="sendURIEncodedCmd()">
      ...
    </button>
  </div>
</div>

Thanks Yannick. Tried your solution tonight, however without success. Item ‘uritestitem’ does not receive upadate. How can I debug this?

You can verify your file is loaded in the Chrome developer tools (Source tab) and add breakpoints.

I stil cannot make it work. For me it looks like scripts don’t start to execute.
Why exacly my first proposed solution can’t be used? What is wrong with it?
I’ve made it based on this book Page 150

Post a screenshot of the developer tools, Source tab. You should see your script in the tree under ‘static’ if it’s loaded properly.

Sorry for late response, I was sick…

<div class="squeezebox-playstream" oc-lazy-load="['/static/mycontroller.js']">
    <div ng-controller="mycontroller">
      <button ng-click="sendURIEncodedCmd()">
      </button>
    </div>
  </div>

Sorry, my bad, turns out you can use filters in ng-click.
But your syntax is wrong, instead of:

ng-click="sendCmd('uritestitem', config.server_address + config.address_prefix + {{config.station_address | encodeURIComponent}} + config.address_suffix + config.player_mac)">

try:

ng-click="sendCmd('uritestitem', config.server_address + config.address_prefix + (config.station_address | encodeuri) + config.address_suffix + config.player_mac)">

also make sure your ‘uritestitem’ item exists, if it doesn’t HABPanel will fail silently and discard the command.

Hi,
I tried it already, however with brackets it seems to be not interpreted correctly:

EDIT: But somehow it seems that it works now!
However I had to put script directly into html directory instead of html/static. Why?

static refers to the html folder. Stuff in html/static would be referred to as being at /static/static. I found putting the js in a a folder at /html/js as helping keep clutter down,( referred to as /static/js/scripname.js)

Thanks for clarification. I’m convinced that I’ve put another .js to /static/ folder and it worked. I didn’t try it again, so maybe that was just some “cache” related strange behavior.