Building a conditional Ember helper - javascript

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}}

Related

Custom Unobtrusive Validation Method Not Firing as Per Documentation

I've been attempting to implement a ASP.NET MVC custom validation method. Tutorials I've used such as codeproject explain that you add data-val-customname to the element. Then jQuery.validate.unobtrusive.js then uses the third segment of the attribute
data-val-<customname>
as the name of the rule, as shown below.
$.validator.addMethod('customname', function(value, element, param) {
//... return true or false
});
However I just can't get the customname method to fire. By playing around I have been able to get the below code to work, but according to all the sources I've read Unobtrusive validation should not work like this.
$.validator.addMethod('data-val-customname', function(value, element, param) {
//... return true or false
});
I've posted an example of both methods
jsfiddle example
Any help would be much appreciated
I've updated my question hopefully to make clearer.
I have finally found got there in the end, but still feels like too much hard work and therefore I've probably got something wrong. Initial I was scuppered by a bug in Chrome Canary 62 which refused to allow the adding of a custom method.
My next issue was having to load jQuery, jQuery.validate and jQuery.validate.unobtrusive in the markup and then isolate javascript implementation in a ES6 class. I didn't want to add my adaptors before $().ready() because of my class structure and loading of the app file independent of jQuery. So I had to force $.validator.unobtrusive.parse(document);.
Despite this I was still having issues and finally debugged the source code and found that an existing validator information that is attached to the form was not merging with the updated parsed rules, and essentially ignoring any new adaptors added.
My final work around and admit feels like I've done too much, was to destroy the initial validation information before my forced re-parse.
Here is the working jsfiddle demo
Here is some simplified code
onJQueryReady() {
let formValidator = $.data(document.querySelector('form'), "validator" );
formValidator.destroy();
$.validator.unobtrusive.adapters.add("telephone", [], function (options) {
options.rules['telephone'] = {};
options.messages['telephone'] = options.message;
});
$.validator.unobtrusive.parse(document);
$.validator.addMethod("telephone", this.handleValidateTelephoneNumber);
}

Typeahead.js with Ember's HTMLBars

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

Template helpers not working after meteor update

So I updated my Meteor from v0.8, to latest 1.0, and one of the issues I encountered was that normal template helpers stopped working, as in:
This used to work:
Template.allCustomers.helpers({
getAllCustomers: function(filter) {
return Customers.find(filter, {sort: {registered: -1 } });
},
when called like:
Template.allCustomers.getAllCustomers();
..but after the update it didnt. So I looked it up into the Template.allCustomers object, and found that by calling it like this it works:
Template.allCustomers.__helpers.get("getAllCustomers")()
So my question is, is this the actual intention? Seems like it was uselessly complicated. I tried to search documentation on templating changes but could not locate and understand:
why was this change made
where is the improvement.
Thanks,

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 :-/

jquery tmpl in a closure

i am working on this little javascript library and following various suggestions i am wrapping my functionality in a closure for the various reasons (encapsulation of variables, hidding of code and so on). since I query a JSON webservice and display the results I also use the jquery tmpl engine.
I think I understand what closures are good for but I sure don't understand them in general. meaning I get totally lost between all those scope changes and whatnot. especially annoying is this exception I get. consider the following code (a simplified ugly version of the code in question, but it reproduces the problem)
// something would be the object that handles all the library functionality
var something = function(){
// creating a local function that goes as a parameter into the Array.filter
function isBar(data){
return data.name === "bar";
}
// the template code
var bla = "<h1>${name}<\h1><h2>${attribute.filter(isBar)[0].value}</h2>";
// precompiling the the template
$.template("test", bla);
// and returning a function that should render the template with the provided data
return {
funny: function(){
$.tmpl("test", [{"name":"Fo", "attribute":[{"name":"bar", "value":"how"}]},
{"name":"Foo", "attribute":[{"name":"fnord","value":"can"}]},
{"name":"Fooo", "attribute":[{"name":"bar","value":"this"}]},
{"name":"Foooo", "attribute":[{"name":"Bar", "value":"be"}]}
]);
}
}
}();
// calling the function
something.funny();
So when calling the something.funny() I would the following expect to happen: the function funny, being a closure gets called in its original context (e.g. the function isBar and the variable bar are defined). So when I call $.tmpl I hoped that attribute.filter(isBar) within the template would also be in this scope. but it isn't. I Chrome i get ReferenceError: isBar is not defined.
If someone would be so nice to show me the error of my ways I would be very happy.
edit oops I missed the "()".
OK, well the problem is that those references to the local variables in the closure are not really references to local variables - they're part of a string. The template code has to parse that string, so when it does that the fact that there was a function called "isBar()" in the closure from where "$.tmpl()" was called really doesn't matter; jQuery can't access them because you just can't do that in JavaScript.
You can, however, pass in an "options" third parameter to "$.tmpl()" and provide extra stuff there. I'm not 100% sure how to do it as I've only played with the template plugin a little bit, but I'll try a jsfiddle when I have a chance. I think that you'd basically do something like this:
funny: function(){
$.tmpl("test", [{"name":"Fo", "attribute":[{"name":"bar", "value":"how"}]},
{"name":"Foo", "attribute":[{"name":"fnord","value":"can"}]},
{"name":"Fooo", "attribute":[{"name":"bar","value":"this"}]},
{"name":"Foooo", "attribute":[{"name":"Bar", "value":"be"}]}
], { isBar: isBar });
}
What I'm not sure of is whether you refer to that as "${isBar()}" or "${item.isBar()}" inside the template text.

Categories

Resources