7

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);
peterh
  • 11,875
  • 18
  • 85
  • 108
meiryo
  • 11,157
  • 14
  • 47
  • 52
  • If you want to change the curly brackets in Angular, just can just follow this : http://docs.angularjs.org/api/ng.$interpolate , no modification needed to the code ;) – DotDotDot Jul 28 '13 at 09:02
  • @meiryo, how did you manage to use Typeahead.js in an angular application ? because I'm trying to include it but I don't know how to proceed (I have already used it but within jQuery). Thanks in advance for your help :) – Anas Aug 03 '13 at 17:39
  • @user1651994 I changed Hogan.js delimiters to `<% %>`. All I did was a find and replace for `"{{"` and `"}}"`. No more conflicts with angular! Let me know if you can use angular as the template engine... not very fond of including Hogan just for a simple typeahead box. – meiryo Aug 04 '13 at 14:30
  • @meiryo Actually I'm not using Typeahead.js in my project yet. How are using it in your project ? is there a custom directive, or you're just using jQuery code ? (just to know because I can't use the current version since I'm relying on an existing API, and the data format doesn't match the required one for typeahead Datum... It seems that building a dataset in the client-side will be possible in the 0.10 release) – Anas Aug 04 '13 at 14:43
  • @user1651994 I just include the `typeahead.js` file in my scripts, and call the `typeahead()` function on the input field I want. Nothing fancy, really easy to set up and I love it. Take a look at the source code for their examples, it help me figure out some of the stuff: http://twitter.github.io/typeahead.js/examples/ – meiryo Aug 04 '13 at 15:05
  • @meiryo Well I see, I thought there was something specific to AngularJS. Do you mind sharing a snippet of the code you are using to do this ? Many thanks – Anas Aug 04 '13 at 15:31
  • @user1651994 Sorry I'm under contract so I cannot show code. But the implementation is dead easy: http://pastebin.com/c4XSLpVL five lines of code pretty much. AngularJS conflicted with the templating engine Hogan because they both use `{{ }}` directives. Yeah, nasty. – meiryo Aug 04 '13 at 15:40
  • @meiryo No worries, thanks for your help ! At least, now I know that someone has used Typeaheadjs with Angularjs :) – Anas Aug 04 '13 at 16:03
  • 1
    @user1651994 you'll need jQuery loaded, then have to create a directive and put the typeahead inside: http://pastebin.com/ALQYdfrk – fiatjaf Sep 03 '13 at 22:25

3 Answers3

14

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>'
  }
})
fiatjaf
  • 11,479
  • 5
  • 56
  • 72
3

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: '<% %>'});
jharding
  • 1,404
  • 10
  • 17
3

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', '');
        });
      }
    };
  }
]);
craigm
  • 59
  • 2
  • 4