<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width" />
<title></title>
<link data-require="ionic@1.3.3" data-semver="1.3.3" rel="stylesheet" href="https://code.ionicframework.com/1.3.3/css/ionic.css" />
<link rel="stylesheet" href="style.css" />
<script data-require="ionic@1.3.3" data-semver="1.3.3" src="https://code.ionicframework.com/1.3.3/js/ionic.bundle.js"></script>
<script src="ngStorage.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-resource/1.5.8/angular-resource.min.js"></script>
<!-- ion-autocomplete -->
<script src="ion-autocomplete.js"></script>
<link href="ion-autocomplete.css" rel="stylesheet">
<!-- cordova script (this will be a 404 during development) -->
<script src="cordova.js"></script>
<script src="app.js"></script>
<script src="controllers.js"></script>
<script src="services.js"></script>
</head>
<body ng-app="app" animation="slide-left-right-ios7" ng-controller="mainCtrl">
<div>
<div>
<ion-nav-bar class="bar-stable">
<ion-nav-back-button class="button-icon icon ion-ios-arrow-back">Back</ion-nav-back-button>
</ion-nav-bar>
<ion-nav-view></ion-nav-view>
</div>
</div>
</body>
</html>
/* Styles go here */
// Ionic Starter App, v0.9.20
// angular.module is a global place for creating, registering and retrieving Angular modules
// 'starter' is the name of this angular module example (also set in a <body> attribute in index.html)
// the 2nd parameter is an array of 'requires'
// 'starter.services' is found in services.js
// 'starter.controllers' is found in controllers.js
angular.module('app', ['ionic', 'starter.controllers', 'app.services'])
.run(function($ionicPlatform) {
$ionicPlatform.ready(function() {
if(window.StatusBar) {
StatusBar.styleDefault();
}
});
})
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('menu', {
url: '/side-menu21',
abstract:true,
templateUrl: 'menu.html',
//controller: 'menuCtrl'
})
.state('menu.home', {
url: '/home',
//cache: false,
views: {
'side-menu21': {
templateUrl: 'home.html',
controller: 'homeCtrl'
}
}
})
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/side-menu21/home');
});
Test plnkr for ion-autocomplete
<ion-side-menus enable-menu-with-back-views="false" ng-controller="menuCtrl">
<ion-side-menu-content>
<ion-nav-bar class="bar-stable">
<ion-nav-back-button></ion-nav-back-button>
<ion-nav-buttons side="left">
<button class="button button-icon button-clear ion-navicon" menu-toggle=""></button>
</ion-nav-buttons>
<ion-nav-buttons side="secondary">
<!--ng-hide = "sightId || hideChangeButton"-->
<!--<button ng-hide = "changeLocationButton.hideChangeButton" class="button" modal-select="" ng-model="tempLocation" options-expression="location in locations | orderBy:'name' | filter : externalFilter" modal-title="Choose location of interest" on-select="changeLocation(newValue, oldValue)" on-open="setMapNonclickable()" on-close="setMapClickable()" has-search="true">
Change
<div class="option"><h2>{{option.name}}</h2><p>{{option.country.name}}</p></div>
</button>-->
<input
ion-autocomplete
type="hidden"
readonly="readonly"
ng-model="searchQuery"
class="ion-autocomplete"
autocomplete="off"
max-selected-items="1"
items-method="searchCallback(query, isInitializing)"
item-view-value-key="name"
items-clicked-method="itemClicked(callback)"
manage-externally="true"
template-url="searchTemplate.html"
placeholder="Search for anything..."
/>
<button class="button button-icon icon ion-search" ng-click="clickSearch()"></button>
</ion-nav-buttons>
</ion-nav-bar>
<ion-nav-view name="side-menu21"></ion-nav-view>
</ion-side-menu-content>
<ion-side-menu side="left" style="">
<ion-header-bar class="bar-stable">
<div class="title">Menu</div>
</ion-header-bar>
<ion-content padding="false" class="side-menu-left has-header" ion-content="">
<ion-list>
<ion-item href="#/side-menu21/home" menu-close=""><i class="icon ion-home"></i> Home</ion-item>
</ion-list>
</ion-content>
</ion-side-menu>
</ion-side-menus>
angular.module('starter.controllers', ['ngStorage'])
.controller('mainCtrl', function($scope) {
})
.controller('menuCtrl', ['$scope', '$rootScope', '$ionicSideMenuDelegate', 'SearchService', '$state', '$localStorage',
function($scope, $rootScope, $ionicSideMenuDelegate, SearchService, $state, $localStorage) {
$scope.clickSearch = function () {
var ionAutocompleteElement = document.getElementsByClassName("ion-autocomplete");
angular.element(ionAutocompleteElement).controller('ionAutocomplete').fetchSearchQuery("", true);
angular.element(ionAutocompleteElement).controller('ionAutocomplete').showModal();
};
$scope.searchCallback = function (query, isInitializing) {
$rootScope.searchQuery = query;
if(isInitializing || query === "") {
// depends on the configuration of the `items-method-value-key` (items) and the `item-value-key` (name) and `item-view-value-key` (name)
//return { items: [ { name: "test" } ] }
//console.log(SearchService);
return SearchService.initSearchResultsList().$promise;
} else {
return SearchService.updateSearchResultsList(query).$promise;
}
};
$scope.itemClicked = function (callback) {
// print out the selected item
//console.log(callback.item);
$rootScope.searchModalOpen = true;
if(callback.item.entity_type == "sight") {
$state.go('menu.sightDetails',{id: callback.item.id})
}
if(callback.item.entity_type == "guide") {
$state.go('menu.guideDetails',{id: callback.item.id})
}
if(callback.item.entity_type == "location") {
//$state.go('menu.guideDetails',{id: callback.item.id})
Location.get({locationId: callback.item.id}, function (data) {
$localStorage.locationSelected = data;
$rootScope.locationSelected = $localStorage.locationSelected;
$rootScope.map = MapService.moveMapToLocation(callback.item.id);
});
}
};
}])
.controller('homeCtrl', function($scope, $rootScope) {
$scope.clickSearch = function () {
var ionAutocompleteElement = document.getElementsByClassName("ion-autocomplete");
console.log(ionAutocompleteElement);
console.log(angular.element(ionAutocompleteElement));
console.log(angular.element(ionAutocompleteElement).controller('ionAutocomplete'));
angular.element(ionAutocompleteElement).controller('ionAutocomplete').fetchSearchQuery("", true);
angular.element(ionAutocompleteElement).controller('ionAutocomplete').showModal();
};
$scope.searchCallback = function (query, isInitializing) {
$rootScope.searchQuery = query;
if(isInitializing || query === "") {
// depends on the configuration of the `items-method-value-key` (items) and the `item-value-key` (name) and `item-view-value-key` (name)
//return { items: [ { name: "test" } ] }
//console.log(SearchService);
return SearchService.initSearchResultsList().$promise;
} else {
return SearchService.updateSearchResultsList(query).$promise;
}
};
$scope.itemClicked = function (callback) {
// print out the selected item
//console.log(callback.item);
$rootScope.searchModalOpen = true;
if(callback.item.entity_type == "sight") {
$state.go('menu.sightDetails',{id: callback.item.id})
}
if(callback.item.entity_type == "guide") {
$state.go('menu.guideDetails',{id: callback.item.id})
}
if(callback.item.entity_type == "location") {
//$state.go('menu.guideDetails',{id: callback.item.id})
Location.get({locationId: callback.item.id}, function (data) {
$localStorage.locationSelected = data;
$rootScope.locationSelected = $localStorage.locationSelected;
$rootScope.map = MapService.moveMapToLocation(callback.item.id);
});
}
};
})
.controller('searchCtrl', ['$scope', '$ionicModal', '$log', '$localStorage', '$rootScope', '$ionicTabsDelegate', 'SearchService',
function($scope, $ionicModal, $log, $localStorage, $rootScope, $ionicTabsDelegate, SearchService) {
$scope.currentFilter = {all: true};
}])
<ion-view ng-controller="homeCtrl">
<ion-nav-title>{{locationSelected.name}}</ion-nav-title>
<ion-content overflow-scroll="true" padding="false" class="has-header">
<div style="width:100%;height: calc(100% - 49px);" id="map_canvas" data-tap-disabled="true">
<!--<div id="myOverlay"></div>-->
<!--<div class="bar bar-footer">
<div class="button-bar">
<button class="button button-energized">Guides</button>
<button class="button button-energized">Attractions</button>
<button class="button button-balanced">Both</button>
</div>
</div>-->
<div style="position: absolute; bottom: 59px; left: 50%;">
<div align="center" style="position: relative; left: -50%;">
<input
ion-autocomplete
type="hidden"
readonly="readonly"
ng-model="searchQuery"
class="ion-autocomplete"
autocomplete="off"
max-selected-items="1"
items-method="searchCallback(query, isInitializing)"
item-view-value-key="name"
items-clicked-method="itemClicked(callback)"
manage-externally="true"
template-url="searchTemplate.html"
placeholder="Search for anything..."
/>
<button class="button button-outline button-dark icon-left ion-search" ng-click="clickSearch()">
Search
</button>
</div>
</div>
</div>
</ion-content>
<ion-tabs class="tabs-default tabs-icon-top">
<ion-tab title="Guides" icon-on="ion-ios-person" icon-off="ion-ios-person-outline" ng-click="tabSelect(0)">
</ion-tab>
<ion-tab title="Attractions" icon-on="ion-ios-camera" icon-off="ion-ios-camera-outline" ng-click="tabSelect(1)">
</ion-tab>
<ion-tab title="Both" icon-on="ion-ios-location" icon-off="ion-ios-location-outline" ng-click="tabSelect(2)">
</ion-tab>
</ion-tabs>
</ion-view>
/*
* ion-autocomplete 0.3.3
* Copyright 2016 Danny Povolotski
* Copyright modifications 2016 Guy Brand
* https://github.com/guylabs/ion-autocomplete
*/
(function() {
'use strict';
angular.module('ion-autocomplete', []).directive('ionAutocomplete', [
'$ionicBackdrop', '$ionicScrollDelegate', '$document', '$q', '$parse', '$interpolate', '$ionicPlatform', '$compile', '$templateRequest',
function ($ionicBackdrop, $ionicScrollDelegate, $document, $q, $parse, $interpolate, $ionicPlatform, $compile, $templateRequest) {
return {
require: ['ngModel', 'ionAutocomplete'],
restrict: 'A',
scope: {},
bindToController: {
ngModel: '=',
externalModel: '=',
templateData: '=',
maxSelectedItems: '=',
itemsMethod: '&',
itemsClickedMethod: '&',
itemsRemovedMethod: '&',
modelToItemMethod: '&',
cancelButtonClickedMethod: '&',
placeholder: '@',
cancelLabel: '@',
selectItemsLabel: '@',
selectedItemsLabel: '@',
templateUrl: '@',
itemValueKey: '@',
itemViewValueKey: '@'
},
controllerAs: 'viewModel',
controller: ['$attrs', '$timeout', '$scope', function ($attrs, $timeout, $scope) {
var valueOrDefault = function (value, defaultValue) {
return !value ? defaultValue : value;
};
var controller = this;
// set the default values of the one way binded attributes
$timeout(function () {
controller.placeholder = valueOrDefault(controller.placeholder, 'Click to enter a value...');
controller.cancelLabel = valueOrDefault(controller.cancelLabel, 'Done');
controller.selectItemsLabel = valueOrDefault(controller.selectItemsLabel, "Select an item...");
controller.selectedItemsLabel = valueOrDefault(controller.selectedItemsLabel, $interpolate("Selected items{{maxSelectedItems ? ' (max. ' + maxSelectedItems + ')' : ''}}:")(controller));
controller.templateUrl = valueOrDefault(controller.templateUrl, undefined);
controller.itemValueKey = valueOrDefault(controller.itemValueKey, undefined);
controller.itemViewValueKey = valueOrDefault(controller.itemViewValueKey, undefined);
});
// set the default values of the passed in attributes
this.itemsMethodValueKey = valueOrDefault($attrs.itemsMethodValueKey, undefined);
this.componentId = valueOrDefault($attrs.componentId, undefined);
this.loadingIcon = valueOrDefault($attrs.loadingIcon, undefined);
this.manageExternally = valueOrDefault($attrs.manageExternally, "false");
this.clearOnSelect = valueOrDefault($attrs.clearOnSelect, "true");
this.ngModelOptions = valueOrDefault($scope.$eval($attrs.ngModelOptions), {});
// loading flag if the items-method is a function
this.showLoadingIcon = false;
// the items, selected items and the query for the list
this.searchItems = [];
this.selectedItems = [];
this.searchQuery = undefined;
this.isArray = function (array) {
return angular.isArray(array);
};
}],
link: function (scope, element, attrs, controllers) {
// get the two needed controllers
var ngModelController = controllers[0];
var ionAutocompleteController = controllers[1];
// use a random css class to bind the modal to the component
ionAutocompleteController.randomCssClass = "ion-autocomplete-random-" + Math.floor((Math.random() * 1000) + 1);
var template = [
'<div class="ion-autocomplete-container ' + ionAutocompleteController.randomCssClass + ' modal" style="display: none;">',
'<div class="bar bar-header item-input-inset">',
'<label class="item-input-wrapper">',
'<i class="icon ion-search placeholder-icon"></i>',
'<input type="search" class="ion-autocomplete-search" ng-model="viewModel.searchQuery" ng-model-options="viewModel.ngModelOptions" placeholder="{{viewModel.placeholder}}"/>',
'</label>',
'<div class="ion-autocomplete-loading-icon" ng-if="viewModel.showLoadingIcon && viewModel.loadingIcon"><ion-spinner icon="{{viewModel.loadingIcon}}"></ion-spinner></div>',
'<button class="ion-autocomplete-cancel button button-clear" ng-click="viewModel.cancelClick()">{{viewModel.cancelLabel}}</button>',
'</div>',
'<ion-content class="has-header">',
'<ion-item class="item-divider">{{viewModel.selectedItemsLabel}}</ion-item>',
'<ion-item ng-if="viewModel.isArray(viewModel.selectedItems)" ng-repeat="selectedItem in viewModel.selectedItems track by $index" class="item-icon-left item-icon-right item-text-wrap">',
'<i class="icon ion-checkmark"></i>',
'{{viewModel.getItemValue(selectedItem, viewModel.itemViewValueKey)}}',
'<i class="icon ion-trash-a" style="cursor:pointer" ng-click="viewModel.removeItem($index)"></i>',
'</ion-item>',
'<ion-item ng-if="!viewModel.isArray(viewModel.selectedItems)" class="item-icon-left item-icon-right item-text-wrap">',
'<i class="icon ion-checkmark"></i>',
'{{viewModel.getItemValue(viewModel.selectedItems, viewModel.itemViewValueKey)}}',
'<i class="icon ion-trash-a" style="cursor:pointer" ng-click="viewModel.removeItem(0)"></i>',
'</ion-item>',
'<ion-item class="item-divider" ng-if="viewModel.searchItems.length > 0">{{viewModel.selectItemsLabel}}</ion-item>',
'<ion-item ng-repeat="item in viewModel.searchItems track by $index" item-height="55px" item-width="100%" ng-click="viewModel.selectItem(item)" class="item-text-wrap">',
'{{viewModel.getItemValue(item, viewModel.itemViewValueKey)}}',
'</ion-item>',
'</ion-content>',
'</div>'
].join('');
// load the template synchronously or asynchronously
$q.when().then(function () {
// first check if a template url is set and use this as template
if (ionAutocompleteController.templateUrl) {
return $templateRequest(ionAutocompleteController.templateUrl);
} else {
return template;
}
}).then(function (template) {
// compile the template
var searchInputElement = $compile(angular.element(template))(scope);
// append the template to body
$document.find('body').append(searchInputElement);
// returns the value of an item
ionAutocompleteController.getItemValue = function (item, key) {
// if it's an array, go through all items and add the values to a new array and return it
if (angular.isArray(item)) {
var items = [];
angular.forEach(item, function (itemValue) {
if (key && angular.isObject(item)) {
items.push($parse(key)(itemValue));
} else {
items.push(itemValue);
}
});
return items;
} else {
if (key && angular.isObject(item)) {
return $parse(key)(item);
}
}
return item;
};
// function which selects the item, hides the search container and the ionic backdrop if it has not maximum selected items attribute set
ionAutocompleteController.selectItem = function (item) {
// if the clear on select is true, clear the search query when an item is selected
if (ionAutocompleteController.clearOnSelect == "true") {
ionAutocompleteController.searchQuery = undefined;
}
// return if the max selected items is not equal to 1 and the maximum amount of selected items is reached
if (ionAutocompleteController.maxSelectedItems != "1" &&
angular.isArray(ionAutocompleteController.selectedItems) &&
ionAutocompleteController.maxSelectedItems <= ionAutocompleteController.selectedItems.length) {
return;
}
// store the selected items
if (!isKeyValueInObjectArray(ionAutocompleteController.selectedItems,
ionAutocompleteController.itemValueKey, ionAutocompleteController.getItemValue(item, ionAutocompleteController.itemValueKey))) {
// if it is a single select set the item directly
if (ionAutocompleteController.maxSelectedItems == "1") {
ionAutocompleteController.selectedItems = item;
} else {
// create a new array to update the model. See https://github.com/angular-ui/ui-select/issues/191#issuecomment-55471732
ionAutocompleteController.selectedItems = ionAutocompleteController.selectedItems.concat([item]);
}
}
// set the view value and render it
ngModelController.$setViewValue(ionAutocompleteController.selectedItems);
ngModelController.$render();
// hide the container and the ionic backdrop if it is a single select to enhance usability
if (ionAutocompleteController.maxSelectedItems == 1) {
ionAutocompleteController.hideModal();
}
// call items clicked callback
if (angular.isDefined(attrs.itemsClickedMethod)) {
ionAutocompleteController.itemsClickedMethod({
callback: {
item: item,
selectedItems: angular.isArray(ionAutocompleteController.selectedItems) ? ionAutocompleteController.selectedItems.slice() : ionAutocompleteController.selectedItems,
selectedItemsArray: angular.isArray(ionAutocompleteController.selectedItems) ? ionAutocompleteController.selectedItems.slice() : [ionAutocompleteController.selectedItems],
componentId: ionAutocompleteController.componentId
}
});
}
};
// function which removes the item from the selected items.
ionAutocompleteController.removeItem = function (index) {
// clear the selected items if just one item is selected
if (!angular.isArray(ionAutocompleteController.selectedItems)) {
ionAutocompleteController.selectedItems = [];
} else {
// remove the item from the selected items and create a copy of the array to update the model.
// See https://github.com/angular-ui/ui-select/issues/191#issuecomment-55471732
var removed = ionAutocompleteController.selectedItems.splice(index, 1)[0];
ionAutocompleteController.selectedItems = ionAutocompleteController.selectedItems.slice();
}
// set the view value and render it
ngModelController.$setViewValue(ionAutocompleteController.selectedItems);
ngModelController.$render();
// call items clicked callback
if (angular.isDefined(attrs.itemsRemovedMethod)) {
ionAutocompleteController.itemsRemovedMethod({
callback: {
item: removed,
selectedItems: angular.isArray(ionAutocompleteController.selectedItems) ? ionAutocompleteController.selectedItems.slice() : ionAutocompleteController.selectedItems,
selectedItemsArray: angular.isArray(ionAutocompleteController.selectedItems) ? ionAutocompleteController.selectedItems.slice() : [ionAutocompleteController.selectedItems],
componentId: ionAutocompleteController.componentId
}
});
}
};
// watcher on the search field model to update the list according to the input
scope.$watch('viewModel.searchQuery', function (query) {
ionAutocompleteController.fetchSearchQuery(query, false);
});
// watcher on the max selected items to update the selected items label
scope.$watch('viewModel.maxSelectedItems', function (maxSelectedItems) {
// only update the label if the value really changed
if (ionAutocompleteController.maxSelectedItems != maxSelectedItems) {
ionAutocompleteController.selectedItemsLabel = $interpolate("Selected items{{maxSelectedItems ? ' (max. ' + maxSelectedItems + ')' : ''}}:")(ionAutocompleteController);
}
});
// update the search items based on the returned value of the items-method
ionAutocompleteController.fetchSearchQuery = function (query, isInitializing) {
// right away return if the query is undefined to not call the items method for nothing
if (query === undefined) {
return;
}
if (angular.isDefined(attrs.itemsMethod)) {
// show the loading icon
ionAutocompleteController.showLoadingIcon = true;
var queryObject = {query: query, isInitializing: isInitializing};
// if the component id is set, then add it to the query object
if (ionAutocompleteController.componentId) {
queryObject = {
query: query,
isInitializing: isInitializing,
componentId: ionAutocompleteController.componentId
}
}
// convert the given function to a $q promise to support promises too
var promise = $q.when(ionAutocompleteController.itemsMethod(queryObject));
promise.then(function (promiseData) {
// if the promise data is not set do nothing
if (!promiseData) {
return;
}
// if the given promise data object has a data property use this for the further processing as the
// standard httpPromises from the $http functions store the response data in a data property
if (promiseData && promiseData.data) {
promiseData = promiseData.data;
}
// set the items which are returned by the items method
ionAutocompleteController.searchItems = ionAutocompleteController.getItemValue(promiseData,
ionAutocompleteController.itemsMethodValueKey);
// force the collection repeat to redraw itself as there were issues when the first items were added
$ionicScrollDelegate.resize();
}, function (error) {
// reject the error because we do not handle the error here
return $q.reject(error);
}).finally(function () {
// hide the loading icon
ionAutocompleteController.showLoadingIcon = false;
});
}
};
var searchContainerDisplayed = false;
ionAutocompleteController.showModal = function () {
if (searchContainerDisplayed) {
return;
}
// show the backdrop and the search container
$ionicBackdrop.retain();
angular.element($document[0].querySelector('div.ion-autocomplete-container.' + ionAutocompleteController.randomCssClass)).css('display', 'block');
// hide the container if the back button is pressed
scope.$deregisterBackButton = $ionicPlatform.registerBackButtonAction(function () {
ionAutocompleteController.hideModal();
}, 300);
// get the compiled search field
var searchInputElement = angular.element($document[0].querySelector('div.ion-autocomplete-container.' + ionAutocompleteController.randomCssClass + ' input'));
// focus on the search input field
if (searchInputElement.length > 0) {
searchInputElement[0].focus();
setTimeout(function () {
searchInputElement[0].focus();
}, 100);
}
// force the collection repeat to redraw itself as there were issues when the first items were added
$ionicScrollDelegate.resize();
searchContainerDisplayed = true;
};
ionAutocompleteController.hideModal = function () {
angular.element($document[0].querySelector('div.ion-autocomplete-container.' + ionAutocompleteController.randomCssClass)).css('display', 'none');
ionAutocompleteController.searchQuery = undefined;
$ionicBackdrop.release();
scope.$deregisterBackButton && scope.$deregisterBackButton();
searchContainerDisplayed = false;
};
// object to store if the user moved the finger to prevent opening the modal
var scrolling = {
moved: false,
startX: 0,
startY: 0
};
// store the start coordinates of the touch start event
var onTouchStart = function (e) {
scrolling.moved = false;
// Use originalEvent when available, fix compatibility with jQuery
if (typeof(e.originalEvent) !== 'undefined') {
e = e.originalEvent;
}
scrolling.startX = e.touches[0].clientX;
scrolling.startY = e.touches[0].clientY;
};
// check if the finger moves more than 10px and set the moved flag to true
var onTouchMove = function (e) {
// Use originalEvent when available, fix compatibility with jQuery
if (typeof(e.originalEvent) !== 'undefined') {
e = e.originalEvent;
}
if (Math.abs(e.touches[0].clientX - scrolling.startX) > 10 ||
Math.abs(e.touches[0].clientY - scrolling.startY) > 10) {
scrolling.moved = true;
}
};
// click handler on the input field to show the search container
var onClick = function (event) {
// only open the dialog if was not touched at the beginning of a legitimate scroll event
if (scrolling.moved) {
return;
}
// prevent the default event and the propagation
event.preventDefault();
event.stopPropagation();
// call the fetch search query method once to be able to initialize it when the modal is shown
// use an empty string to signal that there is no change in the search query
ionAutocompleteController.fetchSearchQuery("", true);
// show the ionic backdrop and the search container
ionAutocompleteController.showModal();
};
var isKeyValueInObjectArray = function (objectArray, key, value) {
if (angular.isArray(objectArray)) {
for (var i = 0; i < objectArray.length; i++) {
if (ionAutocompleteController.getItemValue(objectArray[i], key) === value) {
return true;
}
}
}
return false;
};
// function to call the model to item method and select the item
var resolveAndSelectModelItem = function (modelValue) {
// convert the given function to a $q promise to support promises too
var promise = $q.when(ionAutocompleteController.modelToItemMethod({modelValue: modelValue}));
promise.then(function (promiseData) {
// select the item which are returned by the model to item method
ionAutocompleteController.selectItem(promiseData);
}, function (error) {
// reject the error because we do not handle the error here
return $q.reject(error);
});
};
// if the click is not handled externally, bind the handlers to the click and touch events of the input field
if (ionAutocompleteController.manageExternally == "false") {
element.bind('touchstart', onTouchStart);
element.bind('touchmove', onTouchMove);
element.bind('touchend click focus', onClick);
}
// cancel handler for the cancel button which clears the search input field model and hides the
// search container and the ionic backdrop and calls the cancel button clicked callback
ionAutocompleteController.cancelClick = function () {
ionAutocompleteController.hideModal();
// call cancel button clicked callback
if (angular.isDefined(attrs.cancelButtonClickedMethod)) {
ionAutocompleteController.cancelButtonClickedMethod({
callback: {
selectedItems: angular.isArray(ionAutocompleteController.selectedItems) ? ionAutocompleteController.selectedItems.slice() : ionAutocompleteController.selectedItems,
selectedItemsArray: angular.isArray(ionAutocompleteController.selectedItems) ? ionAutocompleteController.selectedItems.slice() : [ionAutocompleteController.selectedItems],
componentId: ionAutocompleteController.componentId
}
});
}
};
// watch the external model for changes and select the items inside the model
scope.$watch("viewModel.externalModel", function (newModel) {
if (angular.isArray(newModel) && newModel.length == 0) {
// clear the selected items and set the view value and render it
ionAutocompleteController.selectedItems = [];
ngModelController.$setViewValue(ionAutocompleteController.selectedItems);
ngModelController.$render();
return;
}
// prepopulate view and selected items if external model is already set
if (newModel && angular.isDefined(attrs.modelToItemMethod)) {
if (angular.isArray(newModel)) {
ionAutocompleteController.selectedItems = [];
angular.forEach(newModel, function (modelValue) {
resolveAndSelectModelItem(modelValue);
})
} else {
resolveAndSelectModelItem(newModel);
}
}
});
// remove the component from the dom when scope is getting destroyed
scope.$on('$destroy', function () {
$ionicBackdrop.release();
// angular takes care of cleaning all $watch's and listeners, but we still need to remove the modal
searchInputElement.remove();
});
// render the view value of the model
ngModelController.$render = function () {
element.val(ionAutocompleteController.getItemValue(ngModelController.$viewValue, ionAutocompleteController.itemViewValueKey));
};
// set the view value of the model
ngModelController.$formatters.push(function (modelValue) {
var viewValue = ionAutocompleteController.getItemValue(modelValue, ionAutocompleteController.itemViewValueKey);
return viewValue == undefined ? "" : viewValue;
});
// set the model value of the model
ngModelController.$parsers.push(function (viewValue) {
return ionAutocompleteController.getItemValue(viewValue, ionAutocompleteController.itemValueKey);
});
});
}
};
}
]);
})();
.ion-autocomplete-container {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 20;
display: none;
margin: auto;
}
input.ion-autocomplete[readonly] {
background-color: transparent;
cursor: text;
}
.ion-autocomplete-loading-icon {
padding-left: 10px;
}
angular.module('app.services', ['ngResource', 'ngStorage'])
.constant('ngSettings', {
apiServiceBaseUrl: 'http://www.guidelance.com/api',
clientId: 'testclient',
})
.factory('Search', [
'$resource','$localStorage','ngSettings',
function($resource,$localStorage,ngSettings){
return $resource(ngSettings.apiServiceBaseUrl+'/v1/default/init-search/:locationId/:lat/:lng/', {}, {
query: {
method:'GET',
isArray:true
}/*,
get: {
method:'GET',
isArray:false
}*/,
byquery: {
url: ngSettings.apiServiceBaseUrl + '/v1/default/search/:locationId/:lat/:lng/:query/',
method:'GET',
isArray:true
}
},
{stripTrailingSlashes: false});
}])
.factory('SearchService', [
'$localStorage', '$stateParams', 'Search', '$rootScope',
function ($localStorage, $stateParams, Search, $rootScope) {
var searchServiceFactory = {};
var _searchResults = {};
var _initSearchResultsList = function() {
_searchResults = Search.query({locationId: $localStorage.locationSelected.id, lat: $rootScope.position.coords.latitude, lng: $rootScope.position.coords.longitude});
return _searchResults;
};
var _updateSearchResultsList = function(query) {
_searchResults = Search.byquery({locationId: $localStorage.locationSelected.id, lat: $rootScope.position.coords.latitude, lng: $rootScope.position.coords.longitude, query: query});
return _searchResults;
};
searchServiceFactory.initSearchResultsList = _initSearchResultsList;
searchServiceFactory.updateSearchResultsList = _updateSearchResultsList;
searchServiceFactory.searchResults = _searchResults;
return searchServiceFactory;
}])
/*! ngstorage 0.3.10 | Copyright (c) 2016 Gias Kay Lee | MIT License */!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["angular"],b):a.hasOwnProperty("angular")?b(a.angular):"object"==typeof exports&&(module.exports=b(require("angular")))}(this,function(a){"use strict";function b(a,b){var c;try{c=a[b]}catch(d){c=!1}if(c){var e="__"+Math.round(1e7*Math.random());try{a[b].setItem(e,e),a[b].removeItem(e,e)}catch(d){c=!1}}return c}function c(c){var d=b(window,c);return function(){var e="ngStorage-";this.setKeyPrefix=function(a){if("string"!=typeof a)throw new TypeError("[ngStorage] - "+c+"Provider.setKeyPrefix() expects a String.");e=a};var f=a.toJson,g=a.fromJson;this.setSerializer=function(a){if("function"!=typeof a)throw new TypeError("[ngStorage] - "+c+"Provider.setSerializer expects a function.");f=a},this.setDeserializer=function(a){if("function"!=typeof a)throw new TypeError("[ngStorage] - "+c+"Provider.setDeserializer expects a function.");g=a},this.supported=function(){return!!d},this.get=function(a){return d&&g(d.getItem(e+a))},this.set=function(a,b){return d&&d.setItem(e+a,f(b))},this.remove=function(a){d&&d.removeItem(e+a)},this.$get=["$rootScope","$window","$log","$timeout","$document",function(d,h,i,j,k){var l,m,n=e.length,o=b(h,c),p=o||(i.warn("This browser does not support Web Storage!"),{setItem:a.noop,getItem:a.noop,removeItem:a.noop}),q={$default:function(b){for(var c in b)a.isDefined(q[c])||(q[c]=a.copy(b[c]));return q.$sync(),q},$reset:function(a){for(var b in q)"$"===b[0]||delete q[b]&&p.removeItem(e+b);return q.$default(a)},$sync:function(){for(var a,b=0,c=p.length;c>b;b++)(a=p.key(b))&&e===a.slice(0,n)&&(q[a.slice(n)]=g(p.getItem(a)))},$apply:function(){var b;if(m=null,!a.equals(q,l)){b=a.copy(l),a.forEach(q,function(c,d){a.isDefined(c)&&"$"!==d[0]&&(p.setItem(e+d,f(c)),delete b[d])});for(var c in b)p.removeItem(e+c);l=a.copy(q)}},$supported:function(){return!!o}};return q.$sync(),l=a.copy(q),d.$watch(function(){m||(m=j(q.$apply,100,!1))}),h.addEventListener&&h.addEventListener("storage",function(b){if(b.key){var c=k[0];c.hasFocus&&c.hasFocus()||e!==b.key.slice(0,n)||(b.newValue?q[b.key.slice(n)]=g(b.newValue):delete q[b.key.slice(n)],l=a.copy(q),d.$apply())}}),h.addEventListener&&h.addEventListener("beforeunload",function(){q.$apply()}),q}]}}return a=a&&a.module?a:window.angular,a.module("ngStorage",[]).provider("$localStorage",c("localStorage")).provider("$sessionStorage",c("sessionStorage"))});
<div class="ion-autocomplete-container {{viewModel.randomCssClass}} modal" style="display: none;" ng-controller="searchCtrl">
<div class="bar bar-header item-input-inset">
<label class="item-input-wrapper">
<i class="icon ion-search placeholder-icon"></i>
<input type="search" class="ion-autocomplete-search" ng-model="viewModel.searchQuery" ng-model-options="viewModel.ngModelOptions" placeholder="{{viewModel.placeholder}}"/>
</label>
<div class="ion-autocomplete-loading-icon" ng-if="viewModel.showLoadingIcon && viewModel.loadingIcon"><ion-spinner icon="{{viewModel.loadingIcon}}"></ion-spinner></div>
<button class="ion-autocomplete-cancel button button-clear" ng-click="viewModel.cancelClick()">{{viewModel.cancelLabel}}</button>
</div>
<ion-content class="has-header">
<ion-item class="item item-divider">
<button class="button icon-left ion-funnel button-full button-positive" ng-click="openFilterModal()">Filter/sort results</button>
<!--<button class="button icon-left ion-arrow-down-b button-stable">Sort</button>-->
</ion-item>
<!--
<ion-item class="item-divider">{{viewModel.selectedItemsLabel}}</ion-item>
<ion-item ng-if="viewModel.isArray(viewModel.selectedItems)" ng-repeat="selectedItem in viewModel.selectedItems track by $index" class="item-icon-left item-icon-right item-text-wrap">
<i class="icon ion-checkmark"></i>
{{viewModel.getItemValue(selectedItem, viewModel.itemViewValueKey)}}
<i class="icon ion-trash-a" style="cursor:pointer" ng-click="viewModel.removeItem($index)"></i>
</ion-item>
<ion-item ng-if="!viewModel.isArray(viewModel.selectedItems)" class="item-icon-left item-icon-right item-text-wrap">
<i class="icon ion-checkmark"></i>
{{viewModel.getItemValue(viewModel.selectedItems, viewModel.itemViewValueKey)}}
<i class="icon ion-trash-a" style="cursor:pointer" ng-click="viewModel.removeItem(0)"></i>
</ion-item>
<ion-item class="item-divider" ng-if="viewModel.searchItems.length > 0">{{viewModel.selectItemsLabel}}</ion-item>
-->
<ion-item ng-repeat="item in viewModel.searchItems | filter : filterFn track by $index" item-height="55px" item-width="100%" ng-click="viewModel.selectItem(item)" class="item-thumbnail-left">
<img ng-src="http://www.guidelance.com/images/{{item.entity_type}}s/square_small/{{item.icon}}">
<h2>{{viewModel.getItemValue(item, viewModel.itemViewValueKey)}}</h2>
<p>{{item.description}}</p>
<h4>{{item.distance | distance}}</h4>
</ion-item>
</ion-content>
</div>