What's the best way to implement a single tri-state checkbox within angularjs?

http://jsfiddle.net/xJhEG/ I made it in a commercial project. Tristates are true, false, null (not "unknown")

.directive('indeterminate', [function() {
    return {
      require: '?ngModel',
      link: function(scope, el, attrs, ctrl) {
        var truthy = true;
        var falsy = false;
        var nully = null;
        ctrl.$formatters = [];
        ctrl.$parsers = [];
        ctrl.$render = function() {
          var d = ctrl.$viewValue;
          el.data('checked', d);
          switch(d){
          case truthy:
            el.prop('indeterminate', false);
            el.prop('checked', true);
            break;
          case falsy:
            el.prop('indeterminate', false);
            el.prop('checked', false);
            break;
          default:
            el.prop('indeterminate', true);
          }
        };
        el.bind('click', function() {
          var d;
          switch(el.data('checked')){
          case falsy:
            d = truthy;
            break;
          case truthy:
            d = nully;
            break;
          default:
            d = falsy;
          }
          ctrl.$setViewValue(d);
          scope.$apply(ctrl.$render);
        });
      }
    };
  }])

Here my Fiddle, starting from TruongSinh and changing

http://jsfiddle.net/xJhEG/25/

without

var truthy = true;
var falsy = false;
var nully = null;

You have take advantage of the indeterminate state of <input type="checkbox">.

MDN web docs: There exists an indeterminate state of checkboxes, one in which it is not checked or unchecked, but undetermined. This is set using the HTMLInputElement object's indeterminate property via JavaScript (it cannot be set using an HTML attribute).

PLUNKER: TRISTATE DIRECTIVE

HTML

<label>
    <input type="checkbox" ng-model="state" indeterminate /> {{state}}
</label>

DIRECTIVE

app.directive('indeterminate', function() {
  return {
    restrict: 'A',
    scope: {
      model: '=ngModel'
    },
    link: function(scope, el, attrs, ctrl) {
      var states = [true, false, undefined];
      var index = states.indexOf(scope.model);
      setIndeterminate();

      el.bind('click', function() {
        scope.model = states[++index % 3];
        setIndeterminate();
      });

      function setIndeterminate() {
        scope.$applyAsync(function() {
          el[0].indeterminate = (scope.model === undefined);
        });
      }
    }
  };
});