<!DOCTYPE html>
<html>
<head>
<script data-require="jquery@2.0.3" data-semver="2.0.3" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<link data-require="select2@3.4.5" data-semver="3.4.5" rel="stylesheet" href="//cdn.jsdelivr.net/select2/3.4.5/select2.css" />
<script data-require="select2@3.4.5" data-semver="3.4.5" src="//cdn.jsdelivr.net/select2/3.4.5/select2.min.js"></script>
<script data-require="lodash.js@2.4.1" data-semver="2.4.1" src="http://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.js"></script>
<script data-require="angular.js@1.1.5" data-semver="1.1.5" src="http://code.angularjs.org/1.1.5/angular.js"></script>
<script data-semver="0.0.5" src="ui-select2.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="myApp" ng-controller="AppController">
<pre>{{eEntity.tags|json}}</pre>
<select-multiple ng-model="eEntity.tags" transform-to-model="transformToModel" transform-to-view="transformToView"/>
<small>hint: start to type with a</small>
</body>
</html>
// Code goes here
angular.module('myApp', ['ui.select2'])
.directive('selectMultiple', function() {
return {
restrict: 'E',
require: ['ngModel'],
replace: true,
scope: true,
template: '<input type="hidden" ui-select2="select2Options" class="form-element" style="width: 200px" />',
controller: ['$scope', '$attrs',
function($scope, $attrs) {
var options = [{
text: '案件名1',
id: 1
}, {
text: '案件名2',
id: 2
}, {
text: '案件名2',
id: 3
}, ];
$scope.enums = {};
angular.forEach(options, function(option, key){
$scope.enums[option.id] = option.text;
});
$scope.select2Options = {
data: options,
multiple: true,
minimumInputLength: 1,
formatResult: function(item) {
return item.text;
},
formatSelection: function(item) {
return item.text;
},
};
$scope.transformToModel = $scope.$eval($attrs.transformToModel);
$scope.transformToView = $scope.$eval($attrs.transformToView);
}
],
link: function(scope, element, attrs, ctrl) {
ctrl[0].$formatters.push(function(modelValue){
console.log('modelValue',modelValue);
if(modelValue){
var viewValue = [];
angular.forEach(modelValue, function(value, key){
var viewItemValue = value;
if(scope.transformToView){
viewItemValue = scope.transformToView(value, scope.enums);
}
if(viewItemValue){
viewValue.push(viewItemValue);
}
});
return viewValue;
}
return [];
});
ctrl[0].$parsers.push(function(viewValue) {
//console.log('viewValue', viewValue);
if (viewValue) {
var modelValue = [];
angular.forEach(viewValue, function(value, key) {
var modelItemValue = value;
if(scope.transformToModel){
modelItemValue = scope.transformToModel(value);
}
console.log('modelItemValue',modelItemValue);
if(modelItemValue){
modelValue.push(modelItemValue);
}
});
return modelValue;
}
return [];
});
}
}
})
.controller('AppController', function($scope) {
$scope.eEntity = {
tags: [
{ "code": 1}
]
};
$scope.transformToModel = function(viewItem){
if(viewItem && viewItem.id){
console.log('viewItem',viewItem);
return { code: viewItem.id };
}
return undefined;
};
$scope.transformToView = function(modelItem, enums){
console.log('modelItem',modelItem);
if(modelItem){
return { id: modelItem.code, text: enums[modelItem.code] }
}
return undefined;
};
});
/* 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('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.val(controller.$viewValue);
// important!
controller.$render();
// Not sure if I should just check for !isSelect OR if I should check for 'tags' key
if (!opts.initSelection && !isSelect) {
controller.$setViewValue(
convertToAngularModel(elm.select2('data'))
);
}
});
};
}
};
}]);