var app = angular.module('plunker', []);

app.controller('DemoController', [function () {
    var demo = this;
    
    demo.tags = ['foo'];
}]);

app.directive('tags', [function () {
    return {
        restrict: 'E',
        replace: true,
        require: ['tags', 'ngModel'],
        scope: {},
        controller: 'TagsController as vm',
        bindToController: {
            'placeholder': '@',
            'tags': '=ngModel'
        },
        template: `
            <div class="tags-host form-control"
                ng-class="{
                    'focus': vm.hasFocus,
                    'has-error': vm.ngModel.$invalid
                }"
                ng-click="vm.handleClick($event)"
            >
                <div class="tag-item label label-default"
                    ng-blur="vm.handleBlurTag($event, $index)"
                    ng-click="vm.handleFocusTag($event, $index)"
                    ng-keydown="vm.handleTagKeyDown($event, $index)"
                    ng-repeat="tag in vm.tags"
                    tabindex="-1"
                >
                    <span class="tag-name">{{ ::tag }}</span>
                    <button type="button" class="tag-remove btn btn-default btn-xs"
                        ng-click="vm.handleRemoveTag($event, $index)"
                    >
                        &times;
                    </button>
                </div>
                <input class="tag-input"
                    ng-blur="vm.handleBlur($event)"
                    ng-focus="vm.handleFocus($event)"
                    ng-keydown="vm.handleKeyDown($event)"
                    ng-keyup="vm.handleKeyUp($event)"
                    ng-model="vm.input"
                    placeholder="{{ vm.placeholder }}"
                >
            </div>
        `,
        link: function ($scope, $element, $attrs, ctls) {
            ctls[0].ngModel = ctls[1];
            ctls[0].$element = $element;
            ctls[0].inputEl = $element.find('input')[0];
        }
    };
}]);

app.controller('TagsController', [function () {
    var vm = this;
    
    vm.hasFocus = false;
    vm.input = '';
    
    vm.handleBlur = handleBlur;
    vm.handleClick = handleClick;
    vm.handleFocus = handleFocus;
    vm.handleFocusTag = handleFocusTag;
    vm.handleKeyDown = handleKeyDown;
    vm.handleKeyUp = handleKeyUp;
    vm.handleRemoveTag = handleRemoveTag;
    vm.handleSubmitTag = handleSubmitTag;
    vm.handleTagKeyDown = handleTagKeyDown;
    
    
    function handleBlur($event) {
        addTag(vm.input);
        
        vm.hasFocus = false;
    }
    
    function handleFocus($event) {
        vm.hasFocus = true;
    }
    
    function handleClick($event) {
        $event.preventDefault();
        
        vm.inputEl.focus();
    }
    
    function handleBlurTag($event, idx) {
        $event.stopPropagation();
    }
    
    function handleFocusTag($event, idx) {
        $event.stopPropagation();
        
        focusTag(idx);
    }
    
    function handleKeyDown($event) {
        switch ($event.keyCode) {
            case 8:
                if (!vm.input) {
                    handleFocusTag($event, vm.tags.length - 1);
                }
                break;
            case 9: // Tab
                if (!vm.input.trim()) break;
            case 32: // Space
            case 188: // Comma
                $event.preventDefault();
                addTag(vm.input);
                break;
        }
    }
    
    function handleKeyUp($event) {
        vm.ngModel.$setValidity('tags', !vm.input || isValid(vm.input));
    }
    
    function handleRemoveTag($event, idx) {
        vm.tags.splice(idx, 1);
        
        focusChild(vm.tags.length);
    }
    
    function handleSubmitTag($event, tag) {
        $event.preventDefault();
        $event.stopPropagation();
        
        addTag(tag);
    }
    
    function handleTagKeyDown($event, idx) {
        switch ($event.keyCode) {
            case 8:
            case 46:
                $event.preventDefault();
                $event.stopPropagation();
                return handleRemoveTag($event, idx);
            case 37: // Left
                return focusChild(idx - 1);
            case 39: // Right
                return focusChild(idx + 1);
        }
    }
    
    function addTag(str) {
        str = str.trim();
        
        if (isValid(str)) {
            vm.tags.push(str);
            vm.input = '';
        }
    }
    
    function isValid(str) {
        return !!str.match(/^[-_a-zA-Z0-9]+$/)
            && vm.tags.indexOf(str) < 0;
    }
    
    function focusChild(idx) {
        if (idx === vm.tags.length) {
            vm.inputEl.focus();
        } else {
            focusTag(idx);
        }
    }
    
    function focusTag(idx) {
        var tags = Array.prototype.slice.call(vm.$element.children(), 0, -1);
        var tagEl = tags[idx];
        
        if (tagEl) {
            tagEl.focus();
        }
    }
}]);
<!DOCTYPE html>
<html ng-app="plunker">

<head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <link data-require="bootstrap-css@*" data-semver="3.3.6" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.css" />
    <script>
        document.write('<base href="' + document.location + '" />');
    </script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="angular.js@1.4.x" src="https://code.angularjs.org/1.5.0/angular.js" data-semver="1.5.0"></script>
    <script src="app.js"></script>
</head>

<body class="container" ng-controller="DemoController as demo">

    <div class="well">
        <h1>Tags input demonstration</h1>
        <tags ng-model="demo.tags" placeholder="Enter a tag"></tags>
    </div>
</body>

</html>
/* Put your css in here */

.tags-host.form-control {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    width: 100%;
    height: auto;
    padding: 4px;
    
    & > .tag-item {
        margin: 4px;
    }
    
    & > .tag-input {
        flex-grow: 1;
        margin: 4px;
    }
    
    &.focus {
        border-color: #66afe9;
        outline: 0;
        -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);
        box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);
    }
    
    &.ng-invalid {
        border-color: #a94442;
        -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
        box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
        
        &.focus {
            -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;
            box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;
        }
    }
}

.tag-item {
    &:focus {
        border-width: 1px;
        border-color: #66afe9;
        outline: 0;
        -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);
        box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);
    }
}

.tag-input {
    border: none;
    
    &:focus, &:active {
        outline: none;
    }
}