<!DOCTYPE html>
<html ng-app="plunker">
  <head>
  <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Open+Sans:400,300,600,700">
  <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/selectize.js/0.12.1/css/selectize.css">
  <link rel=stylesheet href="style.css">
  
  </head>
  <body ng-controller="MainCtrl"> 

    <h2 class='header'>Tagging</h2>
    <selectize config="tagsConfig" options='tagOptions' ng-model="tags"></selectize>

    <p>
      <b>ngModel:</b>
      {{tags}}
      <br>
      <b>options:</b>
      {{tagOptions}}
    </p>
    
    <br>
    <h2 class='header'>E-mail Contacts</h2>
    <button ng-click="addEmail()">Add an e-mail</button>
    <selectize placeholder='Pick some people...' config="emailsConfig" options="emailsOptions" class='contacts' ng-model="emails"></selectize>

    <div>
      <b>ngModel:</b>
      {{emails}}<br>
      <b>options:</b>
      <pre>{{emailsOptions | json}}</pre>
    </div>

    <br>
    <h2 class='header'>Single Item Select</h2>
    <input type="checkbox" ng-model="display">Enable
    <button ng-click="addPerson()">Add a person</button>
    <selectize placeholder='Select a person...' config="singleConfig" options="singleItem.options" ng-model="singleItem.single" ng-if="display"></selectize>

    <p>
      <b>ngModel:</b>
      {{singleItem.single}} {{getAge()}}
    </p>
    
    
    <br>
    <form name='myForm'>
      <h2 class='header'>Angular Form Bindings</h2>
      <selectize placeholder='Pick some things...' options='myOptions' config="myConfig" ng-model="myModel" ng-disabled='disable' required></selectize>

      <br>
      <input type='checkbox' ng-model='disable'>Disable
      <div>
        <b>ngModel:</b>
        <pre>{{myModel |json}}</pre>
        <br>
        <b>myForm.$valid:</b>
        <pre>{{myForm.$valid | json}}</pre>
        <b>myForm.$dirty:</b>
        <pre>{{myForm.$dirty | json}}</pre>
        <b>Async option loading:</b>
        <pre>{{myOptions | json}}</pre>
      </div>
    </form>

    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/selectize.js/0.12.1/js/standalone/selectize.js"></script>
    <script src="http://code.angularjs.org/1.3.8/angular.js" type="text/javascript"></script>
    <script src="angular-selectize.js"></script>
    <script src="app.js"></script>
  </body>
</html>
/* Styles go here */
body{
  font-family: 'Open Sans', Helvetica, arial, sans-serif;
  font-size: 14px;
}
.header {
  font-size: 20px;
  font-weight: 600;
  padding-top: 0;
  margin-bottom: 10px;
}
b{
  font-weight: normal;
  margin-bottom: 10px;
}
pre{
  background: rgb(239, 239, 239);
  font-size: 10px;
}
.ng-invalid{
  border-color: red;
}

/**
 * Email Contacts
 */
.selectize-control.contacts .selectize-input [data-value] .email {
	opacity: 0.5;
}
.selectize-control.contacts .selectize-input [data-value] .name + .email {
	margin-left: 5px;
}
.selectize-control.contacts .selectize-input [data-value] .email:before {
	content: '<';
}
.selectize-control.contacts .selectize-input [data-value] .email:after {
	content: '>';
}
.selectize-control.contacts .selectize-dropdown .caption {
	font-size: 12px;
	display: block;
	opacity: 0.5;
}
angular-selectize2
==================
This is an Angular.js directive for Brian Reavis's Selectize. It supports 2-way model binding, ng-required, and 2-way binding of the options. 
https://github.com/machineboy2045/angular-selectize
/**
 * Angular Selectize2
 * https://github.com/machineboy2045/angular-selectize
 **/

angular.module('selectize', []).value('selectizeConfig', {}).directive("selectize", ['selectizeConfig', function(selectizeConfig) {
  return {
    restrict: 'EA',
    require: '^ngModel',
    scope: { ngModel: '=', config: '=?', options: '=?', ngDisabled: '=', ngRequired: '&' },
    link: function(scope, element, attrs, modelCtrl) {

      Selectize.defaults.maxItems = null; //default to tag editor

      var selectize,
          config = angular.extend({}, Selectize.defaults, selectizeConfig, scope.config);

      modelCtrl.$isEmpty = function(val) {
        return val === undefined || val === null || !val.length; //override to support checking empty arrays
      };

      function createItem(input) {
        var data = {};
        data[config.labelField] = input;
        data[config.valueField] = input;
        return data;
      }

      function addOptions(options) {
        angular.forEach(options, function(opt) { selectize.registerOption(opt); });
      }

      function toggle(disabled) {
        disabled ? selectize.disable() : selectize.enable();
      }

      var validate = function() {
        var isInvalid = (scope.ngRequired() || attrs.required || config.required) && modelCtrl.$isEmpty(scope.ngModel);
        modelCtrl.$setValidity('required', !isInvalid);
      };

      function normalizeOptions(data) {
        if (!data)
          return [];

        data = angular.isArray(data) || angular.isObject(data) ? data : [data]

        angular.forEach(data, function(opt, key) {
            if (typeof opt === 'string') {
                data[key] = createItem(opt);
            }
        });
        return data;
      }

      function updateSelectize() {
        validate();

        selectize.$control.toggleClass('ng-valid', modelCtrl.$valid);
        selectize.$control.toggleClass('ng-invalid', modelCtrl.$invalid);
        selectize.$control.toggleClass('ng-dirty', modelCtrl.$dirty);
        selectize.$control.toggleClass('ng-pristine', modelCtrl.$pristine);

        if (!angular.equals(selectize.items, scope.ngModel)) {
          selectize.addOption(normalizeOptions(angular.copy(scope.ngModel)));
          selectize.setValue(scope.ngModel, true);
        }
      }

      var onChange = config.onChange,
          onOptionAdd = config.onOptionAdd,
          onOptionRemove = config.onOptionRemove;

      config.onChange = function() {
        if (scope._disableOnChange)
          return;

        if (!angular.equals(selectize.items, scope.ngModel))
          scope.$evalAsync(function() {
            var value = angular.copy(selectize.items);
            if (config.maxItems == 1) {
              value = value[0]
            }
            modelCtrl.$setViewValue( value );
          });

        if (onChange) {
          onChange.apply(this, arguments);
        }
      };

      config.onOptionAdd = function(value, data) {
        if (scope.options.indexOf(data) === -1) {
          scope._disableWatchOptions = true;
          scope.options.push(data);

          if (onOptionAdd) {
            onOptionAdd.apply(this, arguments);
          }
        }
      };

      config.onOptionRemove = function(value) {
        var idx = -1;
        for (var i = 0; i < scope.options.length; i++) {
          if (scope.options[i][config.valueField] === value) {
            idx = i;
            break;
          }
        }
        if (idx === -1)
          return;
        scope._disableWatchOptions = true;
        scope.options.splice(idx, 1);

        if (onOptionRemove) {
          onOptionRemove.apply(this, arguments);
        }
      };

      if (scope.options) {
        // replace string options with generated options while retaining a reference to the same array
        normalizeOptions(scope.options);
      } else {
        // default options = [ngModel] if no options specified
        scope.options = normalizeOptions(angular.copy(scope.ngModel));
      }

      var angularCallback = config.onInitialize;

      config.onInitialize = function() {
        selectize = element[0].selectize;
        addOptions(scope.options);
        selectize.setValue(scope.ngModel, true);

        //provides a way to access the selectize element from an
        //angular controller
        if (angularCallback) {
          angularCallback(selectize);
        }

        scope.$watch('options', function() {
          if (scope._disableWatchOptions) {
            scope._disableWatchOptions = false;
            return;
          }

          scope._disableOnChange = true;
          selectize.clearOptions();
          addOptions(scope.options);
          selectize.setValue(scope.ngModel);
          scope._disableOnChange = false;
        }, true);

        scope.$watchCollection('ngModel', updateSelectize);
        scope.$watch('ngDisabled', toggle);
      };

      element.selectize(config);

      element.on('$destroy', function() {
        if (selectize) {
          selectize.destroy();
          element = null;
        }
      });
    }
  };
}]);
var app = angular.module('plunker', ['selectize']);

app.controller('MainCtrl', function($scope, $timeout) {
  $scope.disable = false;
  $scope.display = true;
  
        function monkeyPatch(selectize)
        {
            // Fix unwanted behavior for Selectize. Warning: might have side effects.

            selectize.$control.off('mousedown');
            selectize.$control_input.off('mousedown');
            selectize.$control.on('mousedown', function()
            {
                // We click on the control, so we have to set the focus on it!
                // Even if openOnFocus is false. Which is more to control the default focus (first item of a forum)
                // or focus on keyboard navigation (tab).
                selectize.isFocused = false; // Set focus
                selectize.onMouseDown.apply(selectize, arguments);
                selectize.isFocused = true; // Open drop-down if needed
                return selectize.onMouseDown.apply(selectize, arguments);
            });
            selectize.$control_input.on('mousedown', function(e)
            {
                e.stopPropagation();
                if (selectize.settings.mode === 'single')
                {
                    // toggle dropdown
                    if (selectize.isOpen) selectize.close(); else selectize.open();
                }
            });
        }

  //=======================================================
  //Tagging
  //=======================================================
  $scope.tagOptions = [ 'Awesome', 'Neat', 'Stupendous' ];
  $scope.tags = ['Awesome', 'Great'];
  
  $scope.tagsConfig = {
    delimiter: ',',
    persist: false,
    plugins: [ 'remove_button' ],
    create: function(input) {
        return {
            value: input,
            text: input
        }
    },
    onInitialize: function(selectize){
      console.log('Initialized', selectize);
    },
    onChange: function(value){
      console.log('T onChange', value);
    }
  }
  
  //=======================================================
  //Email Contacts
  //=======================================================
  $scope.emails; 
  

  var REGEX_EMAIL = '([a-z0-9!#$%&\'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&\'*+/=?^_`{|}~-]+)*@' +
                  '(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)';
  $scope.emailsOptions = [
    {email: 'brian@thirdroute.com', name: 'Brian Reavis'},
    {email: 'nikola@tesla.com', name: 'Nikola Tesla'},
    {email: 'someone@gmail.com'}
  ];
  $scope.emailsConfig = {
    persist: false,
    maxItems: null,
    valueField: 'email',
    labelField: 'name',
    plugins: [ 'remove_button' ],
    searchField: ['name', 'email'],
    render: {
        item: function(item, escape) {
            return '<div>' +
                (item.name ? '<span class="name">' + escape(item.name) + '</span>' : '') +
                (item.email ? '<span class="email">' + escape(item.email) + '</span>' : '') +
            '</div>';
        },
        option: function(item, escape) {
            var label = item.name || item.email;
            var caption = item.name ? item.email : null;
            return '<div>' +
                '<span class="label">' + escape(label) + '</span>' +
                (caption ? '<span class="caption">' + escape(caption) + '</span>' : '') +
            '</div>';
        }
    },
    createFilter: function(input) {
        var match, regex;

        // email@address.com
        regex = new RegExp('^' + REGEX_EMAIL + '$', 'i');
        match = input.match(regex);
        if (match) return !this.options.hasOwnProperty(match[0]);

        // name <email@address.com>
        regex = new RegExp('^([^<]*)\<' + REGEX_EMAIL + '\>$', 'i');
        match = input.match(regex);
        if (match) return !this.options.hasOwnProperty(match[2]);

        return false;
    },
    create: function(input) {
        if ((new RegExp('^' + REGEX_EMAIL + '$', 'i')).test(input)) {
            return {email: input};
        }
        var match = input.match(new RegExp('^([^<]*)\<' + REGEX_EMAIL + '\>$', 'i'));
        if (match) {
            return {
                email : match[2],
                name  : $.trim(match[1])
            };
        }
        alert('Invalid email address.');
        return false;
    }
  }
    
  $scope.addEmail = function() {
    $scope.emailsOptions.push({ email: 'frank@einstein.com', name: 'Frank Einstein' });
  };

  //=======================================================
  //Single Item Select
  //=======================================================
  var someAjaxData = {
    'A': { key: 'C', name: 'Chuck Testa', age: 20 }, 
    'B': { key: 'N', name: 'Nikola Tesla', age: 217 },
    'C': { key: 'R', name: 'Rosa Testa', age: 8 }
  };
  $scope.singleItem = { single: null, options: someAjaxData };
  
  $scope.singleConfig = {
//    options: [{value: 1, text: 'Chuck Testa'}, {value: 2, text:'Nikola Tesla'}],
//    create: true,
    openOnFocus: false,
    valueField: 'key',
    labelField: 'name',
    sortField: 'name',
    maxItems: 1,
    onInitialize: function(selectize)
    {
      monkeyPatch(selectize);
    },
    onChange: function(value){
      console.log('I onChange', value);
    }
  };
  
  $scope.getAge = function() {
    var key = $scope.singleItem.single;
    if (!key)
      return -1;
//    console.log(someAjaxData);
    for (var opt in someAjaxData) {
      if (someAjaxData[opt].key === key)
        return someAjaxData[opt].age;
    }
    return -1;
  };
  
  $scope.addPerson = function() {
    $scope.singleItem.options['D'] = { value: 'F', text: 'Frank Einstein' };
  };
  
 
  //=======================================================
  //Angular Form Bindings
  //=======================================================
  $scope.myModel;
  
  $scope.myOptions = [];
  
  $scope.myConfig = {
    create: true,
    // required: true,
  }
  
  //simulate async option loading
  $timeout(function(){
    for(var i = 0; i < 10; i++){
      $scope.myOptions.push({value: i, text: 'Option '+i})
    }
  }, 1000)
  
  
});