Typeahead.js with Ember's HTMLBars - javascript

We've been using the Typeahead.js library in our Ember app (via this addon) with success on Ember versions prior to 1.10, but the upgrade to Ember 1.10 is causing us problems.
Until now we've had success compiling templates that are passed into the typeahead component and passing that along to the typeahead library like this:
templates: {
// this.get('suggestionTemplate') is a string of handlebars template
suggestion: Handlebars.compile(this.get('suggestionTemplate')),
<other code>
}
This does not work in Ember 1.10, however, as typeahead.js throws the below error when executing this line of code:
Code:
$el = $(html.suggestion).append(that.templates.suggestion(suggestion)).data(datasetKey, that.name).data(valueKey, that.displayFn(suggestion)).data(datumKey, suggestion);
Error:
Uncaught TypeError: that.templates.suggestion is not a function
Previously that.templates.suggestion, which is the value from the first code block above, was a function that could be passed the object suggestion and it would compile the actual template. With HTMLBars, that.templates.suggestion is no longer a function but rather is an HTMLBars object, so this code no longer works.
Is there a better way to do this same thing in Ember 1.10 that will match the previous behavior?

I remember I had a similar problem using Handlebars.compile I ended up opting for passing a function to suggestion instead, and then the template content:
templates: {
empty: '<span>No results</span>',
suggestion: function(item){
return '<div>' + item.name + '</div>';
}
}
Hope this works for you too.

If such add-on is no longer being maintained I'd suggest you to use typeahead straight (no cli add-on), I've been using it like this since Ember 1.7 and now I'm on 1.11

In the end we just kept a separate handlebars dependency in our bower.json since it seems like the typeahead library requires you to pass in vanilla handlebars templates to it

Related

Building a conditional Ember helper

I'm trying to build a new conditional helper for my Ember application.
Is important mention that I'm using Ember 1.10.1 that uses Handlebars 2.0 and I can't upgrade it, would be great solving the problem with this version of Ember.
Before writing here I tried different solutions and debugged the Ember code a lot, I'm near to the solution but probably I'm missing something.
First of all, I tried with the following approach reading the Handlebar documentation:
Ember.Handlebars.registerHelper('helperName', function(conditional, options) {
if(conditional) {
return options.fn(this);
} else {
return options.inverse(this);
}
});
And here the template:
{{#helperName booleanCondition}}
print true
{{else}}
print false
{{/helperName}}
Everything worked fine calling the fn function but the inversion function (used to render the template of the else branch) was actually an object instead of a function.
Then I started debugging the Ember code and I tried to follow the same approach that Ember uses with the if helper, so I ended up with the following:
Ember.Handlebars.registerHelper('helperName', function(condition, options) {
var permission = Ember.Object.extend({
can: Ember.computed(function() {
return condition;
})
}).create();
Ember.Handlebars.helpers.boundIf.helperFunction.call(this, ["can"], permission, options, options.data.buffer);
});
can is the property bound to the if, used to change the template if the property changes, since we are using the boundIf version of the if (that does what I just said).
The problem of this solution, that imho could be the nearest to be correct, is that the property is not computed correctly and the helper prints always the false value.
I debugged really a lot without making it working, so any help would be very appreciated and I hope this could be useful for someone else as well.
If what you're trying to do is build a conditional approach that supports authorization questions, you should take a look at ember-can. It is an Ember-CLI addon (whereas it looks like you are doing globals) but older versions worked with Ember 1.10. You should be able to reference what they do there and pull it in on your setup
The good news is you are on Ember 1.10!
This means you have subexpressions. And its simple to create a bound non-block helper:
Ember.HTMLBars._registerHelper('foo', function(bar) {
return bar == 'bar';
});
To use it as a block helper combine it with the {{#if}} helper:
{{#if (foo model)}}
in if
{{else}}
in else
{{/if}}

How to stop Ember Render complaining about deprecated quoteless parameters when using call()?

I am following a template for dynamically choosing which model, route, controller to render with a render helper, as outlined in this question (and elsewhere). My problem is that we want to run with the Ember.ENV.RAISE_ON_DEPRECATION flag set, to catch problems before they develop into upgrade nightmares. I am calling Ember.Render from a handlebars helper, like this:
Ember.Handlebars.registerBoundHelper('renderModuleEdit', function(callingContext, module, options) {
var modelName = callingContext.get('type').get('modelName');
return Ember.Handlebars.helpers.render.call(callingContext, "edit_" + modelName, modelName, options);
});
The template has this code in it:
{{#each module in modules}}
<div class="tab-content" {{bind-attr class="module.active:active"}}>
{{renderModuleEdit module module}}
</div>
{{/each}}
The problem is that render fails on the test for 'quoteless parameters', even though I'm using call() on it, rather than a direct handlebars template syntax. The test is defined in the source on this line. The actual test is options.types[0] !== 'ID', and while the options parameter is available in the register helper function (first code block above), and thus I would be able to change the 1st type away from 'ID', I'm not sure what I can change it to that wouldn't cause something else to subtly break later. The error message comes through as:
Uncaught Error: Using a quoteless parameter with {{render}} is deprecated. Please update to quoted usage '{{render "edit_Intro"}}.
As I am not using {{render edit_Intro}} to make this call, I have no idea how to correct this. If I change the template code to {{renderModuleEdit 'module' 'module'}}, then the parameters to my renderModuleEdit come through as strings of 'module' rather than the model instance I need.
I feel like I have no understanding of what this test is actually for and what the 'quoteless' vs 'quoted' parameters even mean. Can someone explain this? Is there a way around this deprecation warning for calling render from a registered bound handlebars helper like this?
A dirty solution, but working for now:
Ember.Handlebars.registerBoundHelper('renderModuleEdit', function(callingContext, module, options) {
options.types[0] = "STRING"; // FIXME: hack
var modelName = callingContext.get('type').get('modelName');
return Ember.Handlebars.helpers.render.call(callingContext, "edit_" + modelName, modelName, options);
});
I also, wasn't able to find a proper fix yet :-/

Is ko.mapping.fromJS method depreceated in knockout 2.2 and 3?

I'm having a problem to use ko.mapping.fromJS( in knockout. I've been testing on version 2.2 and 3.
Anybody else who has had problems? Or are there any other alternatives to updating a instatieted viewmodel?
if (PAGE.blogViewModel == null) {
PAGE.blogViewModel = new BlogViewModel(data);
ko.applyBindings(PAGE.blogViewModel, document.getElementById("blog_container"));
} else {
ko.fromJSON(data, PAGE.blogViewModel);
}
The updating ko.fromJSON(data, PAGE.blogViewModel); does not work.
*TypeError: ko.fromJSON is not a function*
And oddly enough I've used ko.mapping.fromJS( before and it has worked.
ko.mapping.fromJS(data, PAGE.blogViewModel);
TypeError: ko.mapping is undefined
ko.mapping.fromJS is not deprecated because it was never part of the core Knockout library.
The ko.mapping.fromJS is comming from the Knockout Mapping plugin.
You need to download and include it separately in your HTML to use it.
It sounds like you haven't included the ko.mappings-library. Make sure that it is loaded after Knockout is loaded.

undefined angularJS control function error

I am discovering angularJS, but I get an error Argument 'LawContrl' is not a function, got undefined. In my form, I have in my rails app
%div{"ng-controller" => "LawContrl"}
=form-for ...
%li{"ng-repeat"=>"entry in entries"} {{entry.name}} //I am using haml file
in my laws.js.coffee file, I have
#LawContrl = ($scope) ->
$scope.entries = [
{ name: "hi"}
{name: "ho"}
]
Ok, I think I got it. I don't know what ide you're using, but rename (or refactor then rename if you're using Eclipse or Rubymine) your application.js.coffee to application.js and everything must be working just fine. Btw, you should declare some LawContrl module in your angularJS so the controller can be passed to the view
Not sure what it could be, some things to check that might help with debugging this:
angular.js library script is included only once in the page
LawContrl script is included in the page after angular.js
ng-app attribute (it can be empty) is present in the HTML
If ng-app has a value, e.g. ng-app="foo", then LawContrl should be part of the foo module

Backbone.js inline templates not working in Opera

I've created a backbone.js app which uses inline templates, example of below:
<script type="text/html" id="header-template">
<div class='header'>
<strong><%= name %></strong>
</div>
</script>
Then in the the View:
template = _.template($("#header-template").html());
In Opera this throws an error due to $("#header-template").html() returning null. Does anyone know how to fix this issue?
Thanks
Update:
The error I'm getting in the console is Unhandled Error: 'App.view.header' is not a constructor. When I update underscore.js and backbone.js to the latest versions I also get this error in Chrome.
The header is defined like this:
App.view.header = App.view.header || {}
App.view.header = Backbone.View.extend({
...
});
and rendered like this in the router:
$('header').html( new App.view.header().render().el);
Another update
I've created a tiny backbone.js app which has the same problem:
http://goo.gl/KoOvq
You could wrap the template in a function and call it only when needed.
I like this way more because if you start to put your templates in another files they will be loaded only when needed, the way you are doing now they are loaded at the app start.
template = function(variables){ return _.template(template, variables) }

Categories

Resources