Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add bsTagsInput angular directive & example for bootstrap3 with typea…
…head.
  • Loading branch information
dwalters committed Sep 11, 2013
commit a832c06f828ce891a310db5f5903ec49d9aca46b
14 changes: 13 additions & 1 deletion examples/assets/app_bs3.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,16 @@ elt.tagsinput('input').typeahead({
}).bind('typeahead:selected', $.proxy(function (obj, datum) {
this.tagsinput('add', datum);
this.tagsinput('input').typeahead('setQuery', '');
}, elt));
}, elt));

angular.module('AngularExample', ['bsTagsInput'])
.controller('Ctrl',
function Ctrl($scope) {
$scope.tags = ['Amsterdam', 'Washington'];
$scope.tagsOptions = {
typeahead: {
local: ['Sydney', 'Beijing', 'Cairo']
}
};
}
);
104 changes: 104 additions & 0 deletions examples/assets/bsTagsInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
angular.module('bsTagsInput', [])
/**
* @ngdoc directive
* @name bsTagsInput
* @restrict A
*
* @description
* Sets up an input field for tag inputs, using the bootstrap-tagsinput jQuery plugin. Optionally
* uses typeahead.js for autocompletion.
*
* @element INPUT or SELECT
* @param {Object} options passed to the bootstrap-tagsinput plugin at initialization.
* The typeahead option, if specified, will instead be passed to the typeahead.js plugin.
*
* @example
<doc:example>
<doc:source>
<script>
function Ctrl($scope) {
$scope.tags = ['Amsterdam', 'Washington'];
$scope.tagsOptions = {
typeahead: {
local: ['Sydney', 'Beijing', 'Cairo']
}
}
}
</script>
<form ng-controller="Ctrl">
<input type="text" ng-model="tags" bs-tags-input="tagsOptions">
<pre>{{tags}}</pre>
</form>
</doc:source>
</doc:example>
*/
.directive('bsTagsInput', function() {
// reference to underscorejs/lodash difference method
var difference;
if (typeof(_) !== 'undefined') {
difference = _.difference;
} else {
// fallback to a naive implementation
difference = function(array, other) {
var results = [];
angular.forEach(array, function(value) {
if (other.indexOf(value) === -1) {
results.push(value);
}
});
return results;
};
}

return {
require: 'ngModel',
link: function(scope, element, attrs, ngModelCtrl) {
// parse options
var options = {};
if (attrs.bsTagsInput) {
options = scope.$eval(attrs.bsTagsInput);
}
var typeaheadOpts = options.typeahead;
delete options.typeahead;
if (jQuery.fn.typeahead === undefined) {
typeaheadOpts = undefined;
}

// initialize tagsinput
element.tagsinput(options);

// handle changes from the underlying model
ngModelCtrl.$render = function() {
var oldVal = element.tagsinput('items'),
newVal = ngModelCtrl.$viewValue;
difference(oldVal, newVal).forEach(function(item) {
element.tagsinput('remove', item, true);
});
difference(newVal, oldVal).forEach(function(item) {
element.tagsinput('add', item, true);
});
};

// handle changes from the UI
element.on('change', function() {
var items = element.tagsinput('items');
ngModelCtrl.$setViewValue(items);
});

// handle cleanup
scope.$on('$destroy', function() {
element.tagsinput('destroy');
});

// setup typeahead, if desired
if (typeaheadOpts !== undefined) {
element.tagsinput('input')
.typeahead(typeaheadOpts)
.bind('typeahead:selected', function(obj, datum) {
element.tagsinput('add', datum.value);
element.tagsinput('input').typeahead('setQuery', '');
});
}
}
};
});
62 changes: 18 additions & 44 deletions examples/bootstrap3/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -297,19 +297,15 @@ <h3>Categorizing tags</h3>
<table class="table table-bordered table-condensed"><thead><tr><th>statement</th><th>returns</th></tr></thead><tbody><tr><td><code>$("input").val()</code></td><td><pre class="val prettyprint linenums"></pre></td></tr><tr><td><code>$("input").tagsinput('items')</code></td><td><pre class="items prettyprint linenums"></td></tr></tbody></table>
</div>

<!-- div id="angular" class="example example_angular" ng-app="AngularExample" ng-controller="CityTagsInputController">
<div id="angular" class="example example_angular" ng-app="AngularExample" ng-controller="Ctrl">
<h3>AngularJS support</h3>
<p>
Include <code>bootstrap-tagsinput-angular.js</code> and register the 'bootstrap-tagsinput' in your Angular JS application to use the bootstrap-tagsinput directive.
Include <code>bsTagsInput.js</code> and register the 'bsTagsInput' module in your Angular JS application to use the bs-tags-input directive.
</p>
<div class="bs-example">
<bootstrap-tagsinput
ng-model="cities"
typeahead-source="queryCities"
tagclass="getTagClass"
itemvalue="value"
itemtext="text">
</bootstrap-tagsinput>
<input type="text"
ng-model="tags"
bs-tags-input="tagsOptions">
</div>
<div class="accordion">
<div class="accordion-group">
Expand All @@ -320,38 +316,18 @@ <h3>AngularJS support</h3>
</div>
<div id="example_angular" class="accordion-body collapse">
<div class="accordion-inner">
<pre class="prettyprint linenums">&lt;bootstrap-tagsinput
ng-model="cities"
typeahead-source="queryCities"
tagclass="getTagClass"
itemvalue="value"
itemtext="text"&gt;
&lt;/bootstrap-tagsinput&gt;
<pre class="prettyprint linenums">&lt;input type="text"
ng-model="tags"
bs-tags-input="tagsOptions"&gt;

&lt;script&gt;
angular.module('AngularExample', ['bootstrap-tagsinput'])
.controller('CityTagsInputController',
function CityTagsInputController($scope) {
// Init with some cities
$scope.cities = [
{ "value": 1 , "text": "Amsterdam" , "continent": "Europe" },
{ "value": 4 , "text": "Washington" , "continent": "America" },
{ "value": 7 , "text": "Sydney" , "continent": "Australia" },
{ "value": 10, "text": "Beijing" , "continent": "Asia" },
{ "value": 13, "text": "Cairo" , "continent": "Africa" }
];

$scope.queryCities = function(query) {
return $http.get('cities.json');
};

$scope.getTagClass = function(city) {
switch (city.continent) {
case 'Europe' : return 'badge badge-info';
case 'America' : return 'label label-important';
case 'Australia': return 'badge badge-success';
case 'Africa' : return 'label label-inverse';
case 'Asia' : return 'badge badge-warning';
angular.module('AngularExample', ['bsTagsInput'])
.controller('Ctrl',
function Ctrl($scope) {
$scope.tags = ['Amsterdam', 'Washington'];
$scope.tagsOptions = {
typeahead: {
local: ['Sydney', 'Beijing', 'Cairo']
}
};
}
Expand All @@ -366,13 +342,11 @@ <h3>AngularJS support</h3>
<tr><th>statement</th><th>returns</th></tr>
</thead>
<tbody>
<tr><td><code>$scope.cities</code></td><td><pre class="items prettyprint linenums">{{cities}}</td></tr>
<tr><td><code>$("select").val()</code></td><td><pre class="val prettyprint linenums"></pre></td></tr>
<tr><td><code>$("select").tagsinput('items')</code></td><td><pre class="items prettyprint linenums"></td></tr>
<tr><td><code>$scope.tags</code></td><td><pre class="items prettyprint linenums">{{tags}}</td></tr>
</tbody>
</table>
</div>
</section -->
</section>

<section id="options">
<div class="page-header">
Expand Down Expand Up @@ -561,7 +535,7 @@ <h2>Methods</h2>
<script src="assets/angular.min.js"></script>
<script src="assets/google-code-prettify/prettify.js"></script>
<script src="assets/bootstrap-tagsinput.js"></script>
<script src="assets/bootstrap-tagsinput-angular.js"></script>
<script src="assets/bsTagsInput.js"></script>
<script src="assets/typeahead.min.js"></script>
<script src="assets/hogan.js"></script>
<script src="assets/app_bs3.js"></script>
Expand Down
104 changes: 104 additions & 0 deletions src/bsTagsInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
angular.module('bsTagsInput', [])
/**
* @ngdoc directive
* @name bsTagsInput
* @restrict A
*
* @description
* Sets up an input field for tag inputs, using the bootstrap-tagsinput jQuery plugin. Optionally
* uses typeahead.js for autocompletion.
*
* @element INPUT or SELECT
* @param {Object} options passed to the bootstrap-tagsinput plugin at initialization.
* The typeahead option, if specified, will instead be passed to the typeahead.js plugin.
*
* @example
<doc:example>
<doc:source>
<script>
function Ctrl($scope) {
$scope.tags = ['Amsterdam', 'Washington'];
$scope.tagsOptions = {
typeahead: {
local: ['Sydney', 'Beijing', 'Cairo']
}
}
}
</script>
<form ng-controller="Ctrl">
<input type="text" ng-model="tags" bs-tags-input="tagsOptions">
<pre>{{tags}}</pre>
</form>
</doc:source>
</doc:example>
*/
.directive('bsTagsInput', function() {
// reference to underscorejs/lodash difference method
var difference;
if (typeof(_) !== 'undefined') {
difference = _.difference;
} else {
// fallback to a naive implementation
difference = function(array, other) {
var results = [];
angular.forEach(array, function(value) {
if (other.indexOf(value) === -1) {
results.push(value);
}
});
return results;
};
}

return {
require: 'ngModel',
link: function(scope, element, attrs, ngModelCtrl) {
// parse options
var options = {};
if (attrs.bsTagsInput) {
options = scope.$eval(attrs.bsTagsInput);
}
var typeaheadOpts = options.typeahead;
delete options.typeahead;
if (jQuery.fn.typeahead === undefined) {
typeaheadOpts = undefined;
}

// initialize tagsinput
element.tagsinput(options);

// handle changes from the underlying model
ngModelCtrl.$render = function() {
var oldVal = element.tagsinput('items'),
newVal = ngModelCtrl.$viewValue;
difference(oldVal, newVal).forEach(function(item) {
element.tagsinput('remove', item, true);
});
difference(newVal, oldVal).forEach(function(item) {
element.tagsinput('add', item, true);
});
};

// handle changes from the UI
element.on('change', function() {
var items = element.tagsinput('items');
ngModelCtrl.$setViewValue(items);
});

// handle cleanup
scope.$on('$destroy', function() {
element.tagsinput('destroy');
});

// setup typeahead, if desired
if (typeaheadOpts !== undefined) {
element.tagsinput('input')
.typeahead(typeaheadOpts)
.bind('typeahead:selected', function(obj, datum) {
element.tagsinput('add', datum.value);
element.tagsinput('input').typeahead('setQuery', '');
});
}
}
};
});