angular.module('plunker', ['ui.bootstrap', 'incSearch', 'movieService'])
.controller('MainCtrl', function($scope, $q, movieService) {
$scope.movies = movieService.movies;
$scope.findMovie = function(value) {
var deferred = $q.defer();
if (!value) {
deferred.reject([]);
} else {
var matched = [];
angular.forEach($scope.movies, function(e) {
var re = new RegExp(value, 'i');
if (e.name.match(re) || String(e.id).match(re)) matched.push(e);
});
deferred.resolve(matched);
}
return deferred.promise;
}
})
<!doctype html>
<html ng-app="plunker">
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.8/angular.js"></script>
<script src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.10.0.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="directives/ui-bootstrap-inc-search.js"></script>
<script src="services/movieService.js"></script>
<script src="app.js"></script>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
<link href="style.css" rel="stylesheet">
</head>
<body ng-controller="MainCtrl">
<div id="content">
<div>
<div class="lead" ng-hide="movie">
You can search your favorite David Lynch's movie incrementally with ID or NAME.
</div>
<div class="lead" ng-show="movie">
<p>You've selected <b><span ng-bind="movie.name" /></b> !!</p>
<small>If you want to search again, please click × in selected label.</small>
</div>
<div class="search_area">
<inc-search
ng-model="movie"
inc-item-value="name"
inc-searcher="findMovie"
inc-item-label="this.id + ':' + this.name"
inc-selected-label="movie.id + ':' + movie.name" />
</div>
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>NAME</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="m in movies">
<td>{{m.id}}</td>
<td>{{m.name}}</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
angular.module('movieService', []).service('movieService', function() {
return {
movies: [
{id:1, name:"Six Men Getting Sick (Six Times)"},
{id:2, name:"Absurd Encounter with Fear"},
{id:3, name:"Fictitious Anacin Commercial"},
{id:4, name:"The Alphabet"},
{id:5, name:"The Grandmother"},
{id:6, name:"The Amputee"},
{id:7, name:"The Cowboy and the Frenchman"},
{id:8, name:"Industrial Symphony No. 1"},
{id:9, name:"Premonition Following An Evil Deed"},
{id:10, name:"Darkened Room"},
{id:11, name:"Dumbland"},
{id:12, name:"Ballerina"},
{id:13, name:"Absurda"},
{id:14, name:"Boat"},
{id:15, name:"Bug Crawls"},
{id:16, name:"Industrial Soundscape"},
{id:17, name:"Lamp"},
{id:18, name:"Out Yonder Neighbor Boy"},
{id:19, name:"Intervalometer Experiments"},
{id:20, name:"Scissors"},
{id:21, name:"Lady Blue Shanghai"},
{id:22, name:"The 3 Rs"},
{id:23, name:"Idem Paris"},
{id:24, name:"Twin Peaks"},
{id:25, name:"On the Air"},
{id:26, name:"Hotel Room"},
{id:27, name:"The Cleveland Show (voice actor only - Gus the Bartender)"},
{id:28, name:"Louie (actor only)"},
{id:29, name:"Eraserhead"},
{id:30, name:"The Elephant Man"},
{id:31, name:"Dune"},
{id:32, name:"Blue Velvet"},
{id:33, name:"Wild at Heart"},
{id:34, name:"Twin Peaks: Fire Walk with Me"},
{id:35, name:"Lost Highway"},
{id:36, name:"The Straight Story"},
{id:37, name:"Mulholland Drive"},
{id:38, name:"Inland Empire"},
]};
});
#content { margin: 40px 10px;}
.table { margin: 10px;}
.search_area { width: 300px; }
angular.module('incSearch', [])
.directive('incSearch', function() {
return {
restrict: 'E',
require: 'ngModel',
link: function(scope, elem, attrs, ctrl) {
var incSearcher = $(elem).attr('inc-searcher');
var incSelectedLabel = $(elem).attr('inc-selected-label');
var incItemLabel = $(elem).attr('inc-item-label');
var width = $(elem).attr('width');
var $selectedLabel = $('<div class="alert alert-success alert-dismissable" style="padding: 6px 25px 6px 6px">')
.append('<span class="inc-label">')
.append('<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>')
.hide();
var $dropDown = $('<div class="dropdown">');
var $searchText = $('<input type="text" class="form-control"/>');
var $dropdownMenu = $('<ul class="dropdown-menu">');
$(elem)
.append($selectedLabel)
.append($dropDown.append($searchText).append($dropdownMenu));
var deffered = null;
$searchText
.bind('keyup', function() {
var value = $searchText.val();
var promise = scope[incSearcher](value);
promise.then(
function(items) {
applyItems(items);
},
function() {
$dropDown.removeClass('open');
}
);
})
.bind('blur', function() {
if (! $dropdownMenu.is(':hover')) {
$dropDown.removeClass('open');
}
})
.bind('focus',function() {
if ($(this).val()) $dropDown.addClass('open');
});
$dropdownMenu.on('click', 'a', function() {
scope[attrs.ngModel] = $(this).data();
$selectedLabel.find('.inc-label').text(scope.$eval(incSelectedLabel)).end().show();
$dropDown.removeClass('open').hide();
$searchText.val('');
scope.$apply();
});
$selectedLabel.on('click', '.close', function() {
$selectedLabel.hide();
$dropDown.show();
scope[attrs.ngModel] = undefined;
scope.$apply();
});
scope.$watch(attrs.ngModel, function(value) {
if (! value) return;
$selectedLabel.find('.inc-label').text(scope.$eval(incSelectedLabel)).end().show();
$dropDown.removeClass('open').hide();
$searchText.val('');
});
function applyItems(items) {
$dropdownMenu.find('li').remove();
if (0 < items.length) {
$dropDown.addClass('open');
} else {
$dropDown.removeClass('open');
}
angular.forEach(items, function(item) {
item.__incCreateName = function() {
return eval(incItemLabel);
};
var label = item.__incCreateName();
$dropdownMenu.append($('<li>').append($('<a>').text(label).data(item)));
});
}
}
}
});