angularjs directive communication - angularjs

I am new to angularjs, and I have been reading a ton of documentation and reading through various articles and tutorials as well as videos to help me figure this stuff out.
I am trying to get two directives to interchange information between themselves. a really simplified version of what i am trying to do is at odetocode (http://odetocode.com/blogs/scott/archive/2013/09/11/moving-data-in-an-angularjs-directive.aspx) where k scott allen has wrapped his directives with a div that has the ng-controller attribute it works beautifully.
I have a slightly more complex test I am working on, and I am trying to get it to work similarly to the code I have mentioned.
my two directives talk to each other when I list the ng-controller attribute in the actual template for each directive. it works, but I don't think it is correct. the actual controller code is run twice, once for each directive.
when I move the controller into the div that wraps the two directives, the two directives stop interacting (the change event in the location-selector template doesn't change the park in the controller). I am pretty sure it has something to do with the scope. if anyone can point me in the right direction, or where I should look for information, that would be much appreciated.
here is my fiddle showing my code http://jsfiddle.net/jgbL9/25/
<div ng-app="myApp">
<location-selector ></location-selector ><br/>
<portal-map ></portal-map >
</div>
var App = angular.module('myApp', ['ngResource']);
App.directive('locationSelector',['parkList', function(parkList) {
return {
restrict: 'E',
scope: {
parkId : '=',
parkName : '='
},
template: '<select ng-controller="portalMapCtrl"'+
' ng-model="listParks" ng-change="changePark()" '+
' park-id="parkId" park-name="parkName" ' +
' ng-options="park as park.attributes.NAME for park in Parks" >'+
'</select>',
link: function (scope,element,attrs){
parkList.getListFromGIS().success(function(data) {
scope.Parks = data.features;
});
}
};
}]);
App.directive('portalMap', function(){
return {
restrict: 'E',
scope:{
parkId: "=",
parkName: "="
},
template: '<style type="text/css" media="screen">'+
'#mapCanvas {height: 500px; width:75%; border:thin black solid; }'+
'</style>'+
'<div id="mapCanvas" park-id="parkId" park-name="parkName" ng-controller="portalMapCtrl" ></div>'
}
});
App.controller('portalMapCtrl',['$scope','parkList', function( $scope, parkList ){
var map = {};
var STREETMAPSERVICE = "https://gis.odot.state.or.us/ArcGIS/rest/services/BASEMAPS/Basemap_Streets/MapServer";
var FOTOSSERVICE = "https://maps.prd.state.or.us/arcgis/rest/services/ESRI_TEST/MapServer?f=jsapi";
var UTILSSERVICE = "http://gis.prd.state.or.us/ArcGIS/rest/services/OPRDAssets/MapServer";
var UTILSSERVICE_PARKLAYER = 0;
var UTILSSERVICE_STRUCTUREPOLY = 7;
var UTILSSERVICE_SURFACE = 11;
var UTILSSERVICE_PARCELS = 12;
var timer;
var ALL_LAYERS = [UTILSSERVICE_PARKLAYER,UTILSSERVICE_STRUCTUREPOLY,UTILSSERVICE_SURFACE,UTILSSERVICE_PARCELS];
$scope.parkId = 0;
$scope.parkName = "";
$scope.changePark = function (){
require(["esri/SpatialReference","esri/geometry/Polygon"],
function(SpatialReference,Polygon){
console.log('change park');
$scope.parkId = $scope.listParks.attributes.PARK_HUB_ID;
$scope.parkName = $scope.listParks.attributes.NAME;
parkList.getParkFromGIS($scope.parkId).then(function(data){
var x = data.data;
var y = x.features[0];
var rings = y['geometry'];
var poly = new Polygon(rings);
var xtnt = poly.getExtent();
var sr = new SpatialReference({wkid:2992});
xtnt.setSpatialReference (sr);
map.setExtent(xtnt,true);
});
});
};
function addService(srvc, srvcType, lyrId){require([
"esri/layers/ArcGISTiledMapServiceLayer",
"esri/layers/ArcGISDynamicMapServiceLayer",
"esri/layers/ImageParameters"], function(Tiled,Dynamic,Parameters){
var mapService = {};
if(srvcType == 'Tiled'){
mapService = new Tiled(srvc);
}else{
var imageParameters = new Parameters();
imageParameters.layerIds = lyrId;
imageParameters.transparent = true;
mapService = new Dynamic(srvc,{"imageParameters":imageParameters});
}
map.addLayer(mapService);
});
}
function createMap(){
require(["esri/map"],function(Map){
console.log('create map');
map = new Map("mapCanvas");
addService(STREETMAPSERVICE,'Tiled');
addService(FOTOSSERVICE,'Tiled');
addService(UTILSSERVICE,'Dynamic',ALL_LAYERS);
});
}
createMap();
}]);
App.factory('parkList',['$http', function($http) {
return {
getListFromGIS: function() {
var myUrl = 'http://maps.prd.state.or.us/arcgis/rest/services/ESRI_TEST/MapServer/0/query?where=OBJECTID+%3E+0&geometryType=esriGeometryEnvelope&spatialRel=esriSpatialRelIntersects&outFields=PARK_HUB_ID%2CNAME&returnGeometry=false&&returnIdsOnly=false&returnCountOnly=false&orderByFields=NAME&returnZ=false&returnM=false&returnDistinctValues=true&f=pjson&callback=JSON_CALLBACK';
return $http ({ url: myUrl, method: 'JSONP'});
},
getParkFromGIS: function (id){
var myUrl = "http://maps.prd.state.or.us/arcgis/rest/services/ESRI_TEST/MapServer/0/query?where=PARK_HUB_ID%3d"+id+"&f=pjson&callback=JSON_CALLBACK";
return $http ({ url: myUrl, method: 'JSONP'});
},
JSON_CALLBACK: function(data) {}
};
}]);
(this is the code working with the ng-controller listed in the template of each directive).
any other comments or suggestions you would like to offer about my code structure or code choices will be appreciated also, as I mentioned, I am learning, and the more I can learn the more fun I will have coding.

I believe you're having problems due to your isolate scopes on your directives (the scope: { } in your link function).
I would suggest inlining the templates into your application, instead of trying to make them directives.
In particular, the locationSelector is going to be hard to make a directive - it's usually easier to have your input elements be a part of the element their controller is on.
If you did want to have them be a directive, I'd suggest passing the listParts value into the changePark function:
<select ... ng-change="changePark(listParks)" ...>

Related

wikipedia api - relative urls | remove or redirect links

Currently I've been using the following request to display the Wikipedia content on my AngularJS app:
https://en.wikipedia.org/w/api.php?action=parse&format=json&callback=JSON_CALLBACK&page=little%20tinamou
Using the following, I display all the text on the page:
var lowercaseBirdname = $filter('lowercase')($scope.birdname);
console.log(lowercaseBirdname);
birdApi.getWikipedia(lowercaseBirdname)
.then(function(res) {
console.log(res.data.parse.text['*']);
var toHtml = res.data.parse.text['*'];
document.getElementById("extract").innerHTML = toHtml;
});
All the images, external links are showing, though in the html you can see that the page has alot of '/wiki/' links which redirects me to my own url.
How do I bypass this, do give a redirect to the wikipage on a new tab, or can I simply remove all the links while keeping the layout/images?
You can manipulate the html using angular.element() and adjust the href of resultant dom nodes then return to string before passing to the view:
var url = 'https://en.wikipedia.org/w/api.php?action=parse&format=json&callback=JSON_CALLBACK&page=little%20tinamou',
wikiBaseUrl = "http://en.wikipedia.org";
$http.jsonp(url).then(function(resp){
var html = resp.data.parse.text['*'];
// create a div and append html data
var div = angular.element('<div>').append(html),
// create collection of the `<a>` elements
links = div.find('a');
// loop over `<a>` elements and adjust href
for(var i =0; i<links.length; i++ ){
var el = links[i];
var $link =angular.element(el) , href = $link.attr('href');
if(href[0] ==='/'){
// set absolute URL.
$link.attr('href', wikiBaseUrl + href)
}
}
// return the modified html string from the div element
$scope.html = div.html();
});
Note that many of the href are hashes for in-page ID. Not sure what you want to do with those
Should use ng-bind-html along with ngSanitze and not do any dom manipulation in controllers
<div ng-bind-html="html">
DEMO
Since angular has embedded jquery functionality as angular.element, you can do and wrap all the html manipulation in a custom directive.
This directive will get the html string from wikipedia api response, load them into an element, lookup and replace for relative urls and with wikipedia base url and will store the result on the directive's element.
Online demo - https://plnkr.co/edit/0wtFVOhxw0NfRw43x8K6?p=preview
html:
<button ng-click="reload()">Reload</button>
<hr>
<div wikipedia-content="getWikipediaContent()"></div>
javascript:
var app = angular.module('plunker', []);
var WIKIPEDIA_BASE_URL = 'http://en.wikipedia.org';
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.reload = function() {
// hard coded for testing purposes
$scope.response = {
"parse": {
"title": "Little tinamou",
"pageid": 805527,
"revid": 697345219,
"text": {
"*": "<div>\n<table cl ... "
}
}
};
};
$scope.getWikipediaContent = function getWikipediaContent() {
if (!$scope.response) {
return '';
}
return $scope.response.parse.text['*']
};
});
app.directive("wikipediaContent", function() {
return {
scope: {
wikipediaContent: '='
},
link: function(scope, directiveElement) {
scope.$watch('wikipediaContent', function() {
if (!scope.wikipediaContent) {
return;
}
var wikipediaElement = angular.element(scope.wikipediaContent);
wikipediaElement.find('a').each(function() {
var element = angular.element(this);
var href = element.attr('href');
if (href.match(/^http/)) {
return;
}
element.attr('href', WIKIPEDIA_BASE_URL + href);
});
directiveElement.replaceWith(wikipediaElement);
});
}
}
});
https://plnkr.co/edit/0wtFVOhxw0NfRw43x8K6?p=preview
Replace all the relative urls with absoluteUrls,
example: replace /wiki/File:Crypturellus_soui.jpg with https://en.wikipedia.org/wiki/File:Crypturellus_soui.jpg
NOTE: This should open the image in wikipedia page in current browser tab.

ng-hide & ng-show in leaflet legend

I tried to create a leaflet legend using the 'ng-show' and 'ng-hide' attributes.
Unfortunately, the legend is not created on site load but on map load.
The attributes don't seem to work if they are added with javascript directly.
This code:
onAdd: function() {
var controlDiv = L.DomUtil.create('div', 'air-quality-legend');
controlDiv.setAttribute('ng-hide', 'true');
controlDiv.className = "airQualityIndex";
L.DomEvent
.addListener(controlDiv, 'click', L.DomEvent.stopPropagation)
.addListener(controlDiv, 'click', L.DomEvent.preventDefault);
var table = document.createElement('table');
var tr = document.createElement('table');
var td = document.createElement('table');
td.innerHTML = "test";
tr.appendChild(td);
table.appendChild(tr);
controlDiv.appendChild(table);
return controlDiv;
}
Produces that output.
As described there is a table when there should not.
Is there any way to add 'ng-hide' or 'ng-show' via javascript on runtime?
Thank you for your help in advance.
You'll need to compile the DOM of your custom control. To do that, you'll need to inject $compile into your controller, then after having added the control to your map use the getContainer method on your control instance and run $compile on it and attach it to the scope:
Control:
L.Control.Custom = L.Control.extend({
onAdd: function () {
var container = L.DomUtil.create('div', 'leaflet-control-custom')
header = L.DomUtil.create('h1', 'leaflet-control-custom-header', container);
header.textContent = 'NG-Hide test';
header.setAttribute('ng-hide', 'hide');
return container;
}
});
Controller:
angular.module('app').controller('controller', [
'$scope', 'leaflet', '$compile',
function ($scope, leaflet, $compile) {
$scope.hide = false;
leaflet.map.then(function (map) {
var control = new L.Control.Custom().addTo(map);
$compile(control.getContainer())($scope);
});
}
]);
Here's a working example on Plunker: http://plnkr.co/edit/xzRwTp9OZ8Zp8v7ktt2c?p=preview

Angular Custom Directive - How to make it Behave Like Ng-if

I'm trying to build a custom directive to either show items on a page or completely remove them based on authorization data, and I'm clearly missing something, 'cuz its not working and I'm having a hard time figuring out why.
I've been following the guide here:
http://adamalbrecht.com/2014/09/22/authorization-with-angular-and-ui-router/
Which said to take a copy of the NgIf source code, and modify it (as I have below).
I'm really confused because not only is it not working as expected, but even when I put break-points within the function calls in the directive, it never hits those break points.
I'm not sure what I'm doing wrong. Is there something else not documented within the steps that I need to do in order to use a custom directive, or did I somehow miss a step? Regular ng-if's work on the same page just fine.
EDIT: I should note that AuthorizeService.isAuthorizedForOne returns a promise value which is either true or false. This works fine in other contexts.
'use strict';
/**
* #ngdoc directive
* #name ngIfPermission
* #restrict A
*
* #description
**/
var ngIfPermissionDirective = ['$animate', function($animate, AuthorizeService) {
return {
multiElement: true,
transclude: 'element',
priority: 600,
terminal: true,
restrict: 'A',
$$tlb: true,
link: function($scope, $element, $attr, ctrl, $transclude) {
console.log("I am here");
var block, childScope, previousElements;
$attr.$observe("ngIfPermission", function(value){
console.log("I am there");
var permissions = JSON.parse(value.replace(/'/g, '"'));
AuthorizeService.isAuthorizedForOne(permissions).then(function(auth){
if (!childScope) {
$transclude(function(clone, newScope) {
childScope = newScope;
clone[clone.length++] = document.createComment(' end ngIfPermission: ' + $attr.ngIfPermission + ' ');
// Note: We only need the first/last node of the cloned nodes.
// However, we need to keep the reference to the jqlite wrapper as it might be changed later
// by a directive with templateUrl when its template arrives.
block = {
clone: clone
};
$animate.enter(clone, $element.parent(), $element);
});
}
},
function(err) {
if (previousElements) {
previousElements.remove();
previousElements = null;
}
if (childScope) {
childScope.$destroy();
childScope = null;
}
if (block) {
previousElements = getBlockNodes(block.clone);
$animate.leave(previousElements).then(function() {
previousElements = null;
});
block = null;
}
});
});
}
};
}];
How I'm referencing it:
<div ng-if-permission="['OOGY']">You can see this.</div>
<div ng-if-permission='["BOOGY"]'>or this</div>
I think you might got the declaration for the directive wrong.
app.directive( 'ngIfPermissionDirective', function($animate){
//directive here
));
DEMO http://plnkr.co/edit/BhubrfMAiW3K4ANI3pTx

Proper place for data-saving logic in AngularJS

App design question. I have a project which has a very large number of highly customized inputs. Each input is implemented as a directive (and Angular has made this an absolute joy to develop).
The inputs save their data upon blur, so there's no form to submit. That's been working great.
Each input has an attribute called "saveable" which drives another directive which is shared by all these input types. the Saveable directive uses a $resource to post data back to the API.
My question is, should this logic be in a directive at all? I initially put it there because I thought I would need the saving logic in multiple controllers, but it turns out they're really happening in the same one. Also, I read somewhere (lost the reference) that the directive is a bad place to put API logic.
Additionally, I need to introduce unit testing for this saving logic soon, and testing controllers seems much more straightforward than testing directives.
Thanks in advance; Angular's documentation may be… iffy… but the folks in the community are mega-rad.
[edit] a non-functional, simplified look at what I'm doing:
<input ng-model="question.value" some-input-type-directive saveable ng-blur="saveModel(question)">
.directive('saveable', ['savingService', function(savingService) {
return {
restrict: 'A',
link: function(scope) {
scope.saveModel = function(question) {
savingService.somethingOrOther.save(
{id: question.id, answer: question.value},
function(response, getResponseHeaders) {
// a bunch of post-processing
}
);
}
}
}
}])
No, I don't think the directive should be calling $http. I would create a service (using the factory in Angular) OR (preferably) a model. When it is in a model, I prefer to use the $resource service to define my model "classes". Then, I abstract the $http/REST code into a nice, active model.
The typical answer for this is that you should use a service for this purpose. Here's some general information about this: http://docs.angularjs.org/guide/dev_guide.services.understanding_services
Here is a plunk with code modeled after your own starting example:
Example code:
var app = angular.module('savingServiceDemo', []);
app.service('savingService', function() {
return {
somethingOrOther: {
save: function(obj, callback) {
console.log('Saved:');
console.dir(obj);
callback(obj, {});
}
}
};
});
app.directive('saveable', ['savingService', function(savingService) {
return {
restrict: 'A',
link: function(scope) {
scope.saveModel = function(question) {
savingService.somethingOrOther.save(
{
id: question.id,
answer: question.value
},
function(response, getResponseHeaders) {
// a bunch of post-processing
}
);
}
}
};
}]);
app.controller('questionController', ['$scope', function($scope) {
$scope.question = {
question: 'What kind of AngularJS object should you create to contain data access or network communication logic?',
id: 1,
value: ''
};
}]);
The relevant HTML markup:
<body ng-controller="questionController">
<h3>Question<h3>
<h4>{{question.question}}</h4>
Your answer: <input ng-model="question.value" saveable ng-blur="saveModel(question)" />
</body>
An alternative using only factory and the existing ngResource service:
However, you could also utilize factory and ngResource in a way that would let you reuse some of the common "saving logic", while still giving you the ability to provide variation for distinct types of objects / data that you wish to save or query. And, this way still results in just a single instantiation of the saver for your specific object type.
Example using MongoLab collections
I've done something like this to make it easier to use MongoLab collections.
Here's a plunk.
The gist of the idea is this snippet:
var dbUrl = "https://api.mongolab.com/api/1/databases/YOURDB/collections";
var apiKey = "YOUR API KEY";
var collections = [
"user",
"question",
"like"
];
for(var i = 0; i < collections.length; i++) {
var collectionName = collections[i];
app.factory(collectionName, ['$resource', function($resource) {
var resourceConstructor = createResource($resource, dbUrl, collectionName, apiKey);
var svc = new resourceConstructor();
// modify behavior if you want to override defaults
return svc;
}]);
}
Notes:
dbUrl and apiKey would be, of course, specific to your own MongoLab info
The array in this case is a group of distinct collections that you want individual ngResource-derived instances of
There is a createResource function defined (which you can see in the plunk and in the code below) that actually handles creating a constructor with an ngResource prototype.
If you wanted, you could modify the svc instance to vary its behavior by collection type
When you blur the input field, this will invoke the dummy consoleLog function and just write some debug info to the console for illustration purposes.
This also prints the number of times the createResource function itself was called, as a way to demonstrate that, even though there are actually two controllers, questionController and questionController2 asking for the same injections, the factories get called only 3 times in total.
Note: updateSafe is a function I like to use with MongoLab that allows you to apply a partial update, basically a PATCH. Otherwise, if you only send a few properties, the entire document will get overwritten with ONLY those properties! No good!
Full code:
HTML:
<body>
<div ng-controller="questionController">
<h3>Question<h3>
<h4>{{question.question}}</h4>
Your answer: <input ng-model="question.value" saveable ng-blur="save(question)" />
</div>
<div ng-controller="questionController2">
<h3>Question<h3>
<h4>{{question.question}}</h4>
Your answer: <input ng-model="question.value" saveable ng-blur="save(question)" />
</div>
</body>
JavaScript:
(function() {
var app = angular.module('savingServiceDemo', ['ngResource']);
var numberOfTimesCreateResourceGetsInvokedShouldStopAt3 = 0;
function createResource(resourceService, resourcePath, resourceName, apiKey) {
numberOfTimesCreateResourceGetsInvokedShouldStopAt3++;
var resource = resourceService(resourcePath + '/' + resourceName + '/:id',
{
apiKey: apiKey
},
{
update:
{
method: 'PUT'
}
}
);
resource.prototype.consoleLog = function (val, cb) {
console.log("The numberOfTimesCreateResourceGetsInvokedShouldStopAt3 counter is at: " + numberOfTimesCreateResourceGetsInvokedShouldStopAt3);
console.log('Logging:');
console.log(val);
console.log('this =');
console.log(this);
if (cb) {
cb();
}
};
resource.prototype.update = function (cb) {
return resource.update({
id: this._id.$oid
},
angular.extend({}, this, {
_id: undefined
}), cb);
};
resource.prototype.updateSafe = function (patch, cb) {
resource.get({id:this._id.$oid}, function(obj) {
for(var prop in patch) {
obj[prop] = patch[prop];
}
obj.update(cb);
});
};
resource.prototype.destroy = function (cb) {
return resource.remove({
id: this._id.$oid
}, cb);
};
return resource;
}
var dbUrl = "https://api.mongolab.com/api/1/databases/YOURDB/collections";
var apiKey = "YOUR API KEY";
var collections = [
"user",
"question",
"like"
];
for(var i = 0; i < collections.length; i++) {
var collectionName = collections[i];
app.factory(collectionName, ['$resource', function($resource) {
var resourceConstructor = createResource($resource, dbUrl, collectionName, apiKey);
var svc = new resourceConstructor();
// modify behavior if you want to override defaults
return svc;
}]);
}
app.controller('questionController', ['$scope', 'user', 'question', 'like',
function($scope, user, question, like) {
$scope.question = {
question: 'What kind of AngularJS object should you create to contain data access or network communication logic?',
id: 1,
value: ''
};
$scope.save = function(obj) {
question.consoleLog(obj, function() {
console.log('And, I got called back');
});
};
}]);
app.controller('questionController2', ['$scope', 'user', 'question', 'like',
function($scope, user, question, like) {
$scope.question = {
question: 'What is the coolest JS framework of them all?',
id: 1,
value: ''
};
$scope.save = function(obj) {
question.consoleLog(obj, function() {
console.log('You better have said AngularJS');
});
};
}]);
})();
In general, things related to the UI belong in a directive, things related to the binding of input and output (either from the user or from the server) belong in a controller, and things related to the business/application logic belong in a service (of some variety). I've found this separation leads to very clean code for my part.

Twitter typeahead.js: Possible to use Angular JS as template engine? If not how do I replace "{{}}" for Hogan/Mustache js?

I am working with twitter's typeahead.js and I was wondering if it was possible to modify hogan.js to use something other than {{}}?
I am looking at the minified code now and I have no idea what to change for something so simple. Doing a find and replace breaks it.
I am asking this mainly because I'm using Angular JS but twitter's typeahead requires a templating engine, causing hogan and angular's {{}} to clash. An even better solution would be simply modifying Angular JS (I know it's not a templating engine) and ditching Hogan to fit the following criteria:
Any template engine will work with typeahead.js as long as it adheres to the following API:
// engine has a compile function that returns a compiled template
var compiledTemplate = ENGINE.compile(template);
// compiled template has a render function that returns the rendered template
// render function expects the context to be first argument passed to it
var html = compiledTemplate.render(context);
Ignore the documentation on this, just look at the source code:
function compileTemplate(template, engine, valueKey) {
var renderFn, compiledTemplate;
if (utils.isFunction(template)) {
renderFn = template;
} else if (utils.isString(template)) {
compiledTemplate = engine.compile(template);
renderFn = utils.bind(compiledTemplate.render, compiledTemplate);
} else {
renderFn = function(context) {
return "<p>" + context[valueKey] + "</p>";
};
}
return renderFn;
}
It happens you can just pass a function to template, callable with a context object which contains the data you passed in the datum objects at the time of instantiation, so:
$('#economists').typeahead({
name: 'economists',
local: [{
value: 'mises',
type: 'austrian economist',
name: 'Ludwig von Mises'
}, {
value: 'keynes',
type: 'keynesian economist',
name: 'John Maynard Keynes'
}],
template: function (context) {
return '<div>'+context.name+'<span>'+context.type.toUpperCase()+'</span></div>'
}
})
If you want to use Hogan.js with Angular, you can change the delimiters by doing something like:
var text = "my <%example%> template."
Hogan.compile(text, {delimiters: '<% %>'});
It appears that the template engine result that typeahead.js expects is an html string and not the dom element (in dropdown_view.js). So I am not sure there is a good solution for using an angular template. As a test I was able to get it binding the result to an angular template but it has to render to an element and then get the html value from the element after binding with the data. I don't like this approach but I figured someone might find it useful. I think I will go with a template function like in the previous post.
Jade template looks like
.result
p {{datum.tokens}}
p {{datum.value}}
Directive
angular.module('app').directive('typeahead', [
'$rootScope', '$compile', '$templateCache',
function ($rootScope, $compile, $templateCache) {
// get template from cache or you can load it from the server
var template = $templateCache.get('templates/app/typeahead.html');
var compileFn = $compile(template);
var templateFn = function (datum) {
var newScope = $rootScope.$new();
newScope.datum = datum;
var element = compileFn(newScope);
newScope.$apply();
var html = element.html();
newScope.$destroy();
return html;
};
return {
restrict: 'A',
link: function (scope, element, attrs, ctrl) {
element.typeahead({
name: 'server',
remote: '/api/search?q=%QUERY',
template: templateFn
});
element.on('$destroy', function () {
element.typeahead('destroy');
});
element.on('typeahead:selected', function () {
element.typeahead('setQuery', '');
});
}
};
}
]);

Resources