<!DOCTYPE html>
<html ng-app="MyApp">
<head>
<link data-require="bootstrap-css@*" data-semver="2.3.2" rel="stylesheet" href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" />
<link data-require="select2@*" data-semver="3.4.2" rel="stylesheet" href="https://rawgithub.com/ivaynberg/select2/3.4.2/select2.css" />
<link rel="stylesheet" href="style.css" />
<script data-require="jquery@*" data-semver="2.0.3" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<script data-require="select2@*" data-semver="3.4.2" src="https://rawgithub.com/ivaynberg/select2/3.4.2/select2.js" ></script>
<script data-require="angular.js@*" data-semver="1.0.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular.js"></script>
<script src="select2.js"></script>
<script src="script.js"></script>
</head>
<body ng-controller="Control">
<h1>Hello Plunker!</h1>
<div class="container">
<div class="row">
<div class="span12">
<div class="row-fluid">
<div class="span3" style="border:1px solid">
<form style="padding:14px">
<fieldset>
<div class="control-group">
<div class="controls">
<input class='form-control' ui-select2='select2Options' ng-model="page.tags" data-placeholder="Pick a number">
</div>
</div>
</fieldset>
<fieldset>
<div class="control-group">
<div class="controls">
<select class='form-control' multiple ui-select2 ng-model="page.categoryIds" data-placeholder="Pick a number">
<option value=""></option>
<option ng-repeat="category in categories" value="{{category.id}}">{{category.title}}</option>
</select>
</div>
</div>
</fieldset>
</form>
</div>
</div>
</div>
</div>
</div>
<pre>{{page|json}}</pre>
</body>
</html>
// Code goes here
var app = angular.module('MyApp', ['ui.select2']);
app.controller( "Control",function($scope, $filter){
$scope.tags = ['tag1','tag2','tag3','tag4','tag5'];
$scope.select2Options = {
multiple: true,
simple_tags: true,
tags: $scope.tags
};
$scope.categories = [
{
id: 'cat1',
title: 'First Category'
},
{
id: 'cat2',
title: 'Second Category'
},
{
id: 'cat3',
title: 'Third Category'
},
{
id: 'cat4',
title: 'Fourth Category'
},
{
id: 'cat5',
title: 'Fifth Category'
}
]
$scope.page ={
tags: ["tag1", 'tag2', 'tag3'],
categoryIds: ["cat2", 'cat5'],
title: 'title'
};
});
/* Styles go here */
/**
* Enhanced Select2 Dropmenus
*
* @AJAX Mode - When in this mode, your value will be an object (or array of objects) of the data used by Select2
* This change is so that you do not have to do an additional query yourself on top of Select2's own query
* @params [options] {object} The configuration options passed to $.fn.select2(). Refer to the documentation
*/
angular.module('ui.select2', []).value('uiSelect2Config', {}).directive('uiSelect2', ['uiSelect2Config', '$timeout', function (uiSelect2Config, $timeout) {
var options = {};
if (uiSelect2Config) {
angular.extend(options, uiSelect2Config);
}
return {
require: 'ngModel',
priority: 1,
compile: function (tElm, tAttrs) {
var watch,
repeatOption,
repeatAttr,
isSelect = tElm.is('select'),
isMultiple = angular.isDefined(tAttrs.multiple);
// Enable watching of the options dataset if in use
if (tElm.is('select')) {
repeatOption = tElm.find( 'optgroup[ng-repeat], optgroup[data-ng-repeat], option[ng-repeat], option[data-ng-repeat]');
if (repeatOption.length) {
repeatAttr = repeatOption.attr('ng-repeat') || repeatOption.attr('data-ng-repeat');
watch = jQuery.trim(repeatAttr.split('|')[0]).split(' ').pop();
}
}
return function (scope, elm, attrs, controller) {
// instance-specific options
var opts = angular.extend({}, options, scope.$eval(attrs.uiSelect2));
/*
Convert from Select2 view-model to Angular view-model.
*/
var convertToAngularModel = function(select2_data) {
var model;
if (opts.simple_tags) {
model = [];
angular.forEach(select2_data, function(value, index) {
model.push(value.id);
});
} else {
model = select2_data;
}
return model;
};
/*
Convert from Angular view-model to Select2 view-model.
*/
var convertToSelect2Model = function(angular_data) {
var model = [];
if (!angular_data) {
return model;
}
if (opts.simple_tags) {
model = [];
angular.forEach(
angular_data,
function(value, index) {
model.push({'id': value, 'text': value});
});
} else {
model = angular_data;
}
return model;
};
if (isSelect) {
// Use <select multiple> instead
delete opts.multiple;
delete opts.initSelection;
} else if (isMultiple) {
opts.multiple = true;
}
if (controller) {
// Watch the model for programmatic changes
scope.$watch(tAttrs.ngModel, function(current, old) {
if (!current) {
return;
}
if (current === old) {
return;
}
controller.$render();
}, true);
controller.$render = function () {
if (isSelect) {
elm.select2('val', controller.$viewValue);
} else {
if (opts.multiple) {
var viewValue = controller.$viewValue;
if (angular.isString(viewValue)) {
viewValue = viewValue.split(',');
}
elm.select2(
'data', convertToSelect2Model(viewValue));
} else {
if (angular.isObject(controller.$viewValue)) {
elm.select2('data', controller.$viewValue);
} else if (!controller.$viewValue) {
elm.select2('data', null);
} else {
elm.select2('val', controller.$viewValue);
}
}
}
};
// Watch the options dataset for changes
if (watch) {
scope.$watch(watch, function (newVal, oldVal, scope) {
if (angular.equals(newVal, oldVal)) {
return;
}
// Delayed so that the options have time to be rendered
$timeout(function () {
elm.select2('val', controller.$viewValue);
// Refresh angular to remove the superfluous option
elm.trigger('change');
if(newVal && !oldVal && controller.$setPristine) {
controller.$setPristine(true);
}
});
});
}
// Update valid and dirty statuses
controller.$parsers.push(function (value) {
var div = elm.prev();
div
.toggleClass('ng-invalid', !controller.$valid)
.toggleClass('ng-valid', controller.$valid)
.toggleClass('ng-invalid-required', !controller.$valid)
.toggleClass('ng-valid-required', controller.$valid)
.toggleClass('ng-dirty', controller.$dirty)
.toggleClass('ng-pristine', controller.$pristine);
return value;
});
if (!isSelect) {
// Set the view and model value and update the angular template manually for the ajax/multiple select2.
elm.bind("change", function (e) {
e.stopImmediatePropagation();
if (scope.$$phase || scope.$root.$$phase) {
return;
}
scope.$apply(function () {
controller.$setViewValue(
convertToAngularModel(elm.select2('data')));
});
});
if (opts.initSelection) {
var initSelection = opts.initSelection;
opts.initSelection = function (element, callback) {
initSelection(element, function (value) {
var isPristine = controller.$pristine;
controller.$setViewValue(convertToAngularModel(value));
callback(value);
if (isPristine) {
controller.$setPristine();
}
elm.prev().toggleClass('ng-pristine', controller.$pristine);
});
};
}
}
}
elm.bind("$destroy", function() {
elm.select2("destroy");
});
attrs.$observe('disabled', function (value) {
elm.select2('enable', !value);
});
attrs.$observe('readonly', function (value) {
elm.select2('readonly', !!value);
});
if (attrs.ngMultiple) {
scope.$watch(attrs.ngMultiple, function(newVal) {
attrs.$set('multiple', !!newVal);
elm.select2(opts);
});
}
// Initialize the plugin late so that the injected DOM does not disrupt the template compiler
$timeout(function () {
elm.select2(opts);
// Set initial value - I'm not sure about this but it seems to need to be there
elm.select2('data', controller.$modelValue);
// important!
controller.$render();
// Not sure if I should just check for !isSelect OR if I should check for 'tags' key
if (!opts.initSelection && !isSelect) {
var isPristine = controller.$pristine;
controller.$setViewValue(
convertToAngularModel(elm.select2('data'))
);
if (isPristine) {
controller.$setPristine();
}
elm.prev().toggleClass('ng-pristine', controller.$pristine);
}
});
};
}
};
}]);