angular.module("myApp", ['ui.sortable'])
.directive('editor', function() {
return {
require: '?ngModel',
compile: function (tElement, tAttrs, transclude) {
tElement.after(angular.element('<div class="ui-cover"></div>'));
return function (scope, element, attrs, ctrl) {
if (!ctrl) return;
// UI -> model
element.bind('click', function() {
element.addClass('ui-editable');
this.nextSibling.style.display = 'none';
scope.oldVal = ctrl.$viewValue;
}).bind('blur', function() {
element.removeClass('ui-editable');
this.nextSibling.style.display = 'block';
// Add class for empty element
if (!ctrl.$viewValue || ctrl.$viewValue === '') {
element.addClass('ui-empty');
} else {
element.removeClass('ui-empty');
}
}).bind('keydown', function (e) {
if (e.keyCode === 13 && this.tagName !== 'TEXTAREA') {
// Focus to next input from group on ENTER
e.preventDefault();
element[0].blur();
//var $group = self.$element.filter('[data-group=' + $this.attr('data-group') + ']:visible')
//$group.eq($group.index($this) + 1).focus();
} else if (e.keyCode === 27) {
// Blur on ESC and restore previous value
scope.$apply(ctrl.$setViewValue(scope.oldVal));
this.value = scope.oldVal;
element[0].blur();
}
}).next().bind('click', function(e) {
element.addClass('ui-editable');
element[0].focus();
}).css({
position: 'absolute',
top: 0 + 'px',
left: 0 + 'px',
height: '100%',
width: '100%'
});
// model -> UI
ctrl.$render = function() {
scope.oldVal = ctrl.$modelValue;
element.val(scope.oldVal);
if (!ctrl.$viewValue || ctrl.$viewValue === '') element.addClass('ui-empty') ;
};
};
}
};
})
.controller('MainCtrl',['$scope', function ($scope) {
$scope.list = [
{name: 'Apple'},
{name: 'Orange'},
{name: 'Pear'},
{name: ''}
];
$scope.items = [{name: 'one', id: 30 },{ name: 'two', id: 27 },{ name: 'threex', id: 50 }];
}]);
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<title></title>
<link type="text/css" rel="stylesheet" href="style.css"/>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.js"></script>
<script src="ui-sortable.js"></script>
<script src="script.js"></script>
<style>
ul li {
position: relative;
}
</style>
</head>
<body>
<div ng-controller="MainCtrl">
<ul ui-sortable ng-model="list">
<li ng-repeat="item in list">
<!--<input type="text" editor ng-model="item.name" placeholder="write something..."/>-->
<!--<select ng-model="selectedItem" ng-options="item.name for item in items track by item.id"></select>-->
<textarea editor ng-model="item.name"></textarea>
</li>
</ul>
<pre>{{ list }}</pre>
</div>
</body>
</html>
input,
textarea {
margin: 0;
padding: 4px;
font-family: Arial, sans;
font-size: 13px;
border: 1px solid #ddd;
border-radius: 2px;
box-shadow: 0 1px 5px -1px rgba(0, 0, 0, .25) inset;
line-height: 20px;
}
input[editor] {
border: 1px solid #fff;
border-color: rgba(255,255,255,0);
box-shadow: none;
background: #fff;
background: rgba(255,255,255,0);
-webkit-appearance: none;
outline: none;
cursor: pointer;
}
input[editor]:focus {
border: 1px solid #ddd;
background-color: rgba(255,255,255,.2);
box-shadow: 0 1px 5px -1px rgba(0, 0, 0, .25) inset;
cursor: auto;
}
.ui-cover:hover {
opacity: 0.1;
}
.ui-cover {
background-color: #fff000;
cursor: pointer;
opacity: 0;
}
/*
jQuery UI Sortable plugin wrapper
@param [ui-sortable] {object} Options to pass to $.fn.sortable() merged onto ui.config
*/
angular.module('ui.sortable', []).value('uiSortableConfig',{}).directive('uiSortable', [
'uiSortableConfig', function(uiSortableConfig) {
return {
require: '?ngModel',
link: function(scope, element, attrs, ngModel) {
var onReceive, onRemove, onStart, onStop, onUpdate, opts = {};
angular.extend(opts, uiSortableConfig);
scope.$watch(attrs.uiSortable, function(newVal, oldVal){
angular.forEach(newVal, function(value, key){
element.sortable('option', key, value);
});
}, true);
if (ngModel) {
ngModel.$render = function() {
element.sortable( "refresh" );
};
onStart = function(e, ui) {
// Save position of dragged item
ui.item.sortable = { index: ui.item.index() };
};
onUpdate = function(e, ui) {
// For some reason the reference to ngModel in stop() is wrong
ui.item.sortable.resort = ngModel;
};
onReceive = function(e, ui) {
ui.item.sortable.relocate = true;
// added item to array into correct position and set up flag
ngModel.$modelValue.splice(ui.item.index(), 0, ui.item.sortable.moved);
};
onRemove = function(e, ui) {
// copy data into item
if (ngModel.$modelValue.length === 1) {
ui.item.sortable.moved = ngModel.$modelValue.splice(0, 1)[0];
} else {
ui.item.sortable.moved = ngModel.$modelValue.splice(ui.item.sortable.index, 1)[0];
}
};
onStop = function(e, ui) {
// digest all prepared changes
if (ui.item.sortable.resort && !ui.item.sortable.relocate) {
// Fetch saved and current position of dropped element
var end, start;
start = ui.item.sortable.index;
end = ui.item.index();
// Reorder array and apply change to scope
ui.item.sortable.resort.$modelValue.splice(end, 0, ui.item.sortable.resort.$modelValue.splice(start, 1)[0]);
}
if (ui.item.sortable.resort || ui.item.sortable.relocate) {
scope.$apply();
}
};
// If user provided 'start' callback compose it with onStart function
opts.start = (function(_start){
return function(e, ui) {
onStart(e, ui);
if ( typeof _start === "function") {
_start(e, ui);
}
};
})(opts.start);
// If user provided 'stop' callback compose it with onStop function
opts.stop = (function(_stop){
return function(e, ui) {
onStop(e, ui);
if (typeof _stop === "function") {
_stop(e, ui);
}
};
})(opts.stop);
// If user provided 'update' callback compose it with onUpdate function
opts.update = (function(_update){
return function(e, ui) {
onUpdate(e, ui);
if (typeof _update === "function") {
_update(e, ui);
}
};
})(opts.update);
// If user provided 'receive' callback compose it with onReceive function
opts.receive = (function(_receive){
return function(e, ui) {
onReceive(e, ui);
if (typeof _receive === "function") {
_receive(e, ui);
}
};
})(opts.receive);
// If user provided 'remove' callback compose it with onRemove function
opts.remove = (function(_remove){
return function(e, ui) {
onRemove(e, ui);
if (typeof _remove === "function") {
_remove(e, ui);
}
};
})(opts.remove);
}
// Create sortable
element.sortable(opts);
}
};
}
]);