adamkdean

software engineering

Reverse the polarity of AngularJS filters

By Adam K Dean on

AngularJS allows you to use strings, objects, and functions as filter objects. You can use them to filter certain text, such as a name in a list of names, or a number in a list of phone numbers. But what happens when you want to filter out those results?

We'll pretend we have a big list of open and closed support tickets, and a checkbox, which we will use to toggle the filter. When it's on, we want to filter out closed tickets. When it's off, we want to see all tickets.

<input type="checkbox" 
    ng-true-value="closed" 
    ng-false-value="" 
    ng-model="hideClosedTickets"> 
Hide closed tickets

ng-true-value and ng-false-value are interesting. They allow you to override the true/false value of the checkbox, setting hideClosedTickets to closed when checked, instead of just true.

Now let's use the ng-repeat directive to list our tickets. It'll look like this:

<ul>
    <li ng-repeat="ticket in tickets">
        # {{ticket.id}} - {{ticket.title}} - {{ticket.status}}
    </li>
</ul>

Which will give us our pretend output:

# 1 - Example ticket - open  
# 2 - My computer is broken - closed 
# 3 - jQuery fails to load - open  
# 4 - Somebody stole my drink - closed 
# 5 - Internet isn't working - open  

To filter these, we're going to leverage the filter function of ng-repeat and the scope variable hideClosedTickets which we set before. We'll pass in an object so that we can match against the ticket status only.

We could just filter the tickets for open tickets only like so:

<li ng-repeat="ticket in tickets | filter: {status: 'open'}">

but what if we have other ticket statuses, such as blocked, awaiting reply, or resolved etc?

For this, we need to reverse the polarity of the filter, so we match against the text. We can do this by prefixing the filter with the logical-not operator, the mighty !:

<li ng-repeat="ticket in tickets | filter: {status: '!' + hideClosedTickets}">

The only problem is here is that if hideClosedTickets is an empty string, it's not going to show anything. We do this by first checking that hideClosedTickets is set and then matching against it:

<li ng-repeat="ticket in tickets | filter: {status: hideClosedTickets && '!' + hideClosedTickets}">

When the checkbox is checked, hideClosedTickets equals closed, and we will get this output:

# 1 - Example ticket - open  
# 3 - jQuery fails to load - open  
# 5 - Internet isn't working - open  

When the checkbox is unchecked, hideClosedTickets is an empty string, so we get everything:

# 1 - Example ticket - open  
# 2 - My computer is broken - closed 
# 3 - jQuery fails to load - open  
# 4 - Somebody stole my drink - closed 
# 5 - Internet isn't working - open  

This is a neat little trick, but not always apparent at first.

If you have a better way of doing this, please post your solution in the comments!

#Update

It has just been pointed out to me that adding ! to the ng-true-value will do the trick as well, so:

<input type="checkbox" 
    ng-true-value="!closed" 
    ng-false-value="" 
    ng-model="hideClosedTickets">

And then:

<li ng-repeat="ticket in tickets | filter: {status: hideClosedTickets}">

~~I'm not sure why this didn't occur to me earlier, I was sure I tried it first, but maybe it's just been a long day. ~~

Update: I remember now why I didn't do it this way. I wanted to use the value ("closed") elsewhere, where I was ng-show rather than ng-repeat filter. Both approaches work, pick which suits you best!

Remove all tags from git repo

By Adam K Dean on

I had started to tag builds with a branch name and date, but after a while, these started to make the log unreadable. It was time to Arnold Schwarzenegger-ize them.

I found this somewhere online:

for t in `git tag`
do
    git push origin :$t
    git tag -d $t
done

Saved it to removetags.sh, ran it, and now my life (and log) is much happier.

Modify your last commit message

By Adam K Dean on

It's easy to modify your last commit message, simply add --amend and commit again.

git commit -m "Fixed issue where udpate failed"

Oh, not again, look at that typo!

git commit --amend -m "Fixed issue where update failed"

As simple as that.

Changing text selection colours

By Adam K Dean on

A nice little trick to add a bit of polish to your site is changing the colour of text selection. Stockapps Blog have a nice orange colour, and of course, Si Digital have their well known Fuchsia highlighting.

You can easily implement this using the pseudo-element ::selection.

::selection {
    color: white;
    background: red;
}

Unfortunately, Gecko requires prefixing, so you also have to add in ::moz-selection:

::moz-selection {
    color: white;
    background: red;
}
::selection {
    color: white;
    background: red;
}

It is worth noting that this is experimental, having been dropped from the specification, so I guess you shouldn't rely on this for anything "important" just yet. Hopefully it will stick around!

Using number inputs, and hiding the arrows

By Adam K Dean on

For a long time we've lived with a few basic input types; text, password, select, and submit. But with HTML5 there are a few more input types that we get to play with. Some of these are yet to be fully supported, but if you're developing for a single browser like I am at the moment, and that single browser happens to be Chrome, you're in luck.

After finding Angular's ng-pattern to be cumbersome, I decided to checkout the number input type. The client-side validation was just what I wanted, but the pesky up/down buttons made it look ugly and took up precious space on my already cramped user interface.

Having used the power of Google, I found a way to disable these ugly buttons.

input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;        
}

The next issue I had was that it wanted to accept integers only. Not good for currency operations.

Luckily, there are a few attributes available to us. One of these is step, which "specifies the value granularity of the element’s value". Setting step to any allows you to go to any granularity you like.

Another two useful attributes are min and max, which allow you to set a defined range.

<input type="number" step="any" min="0" max="100">

This is a truly useful element and hopefully we'll see much more built-in functionality come to HTML5.