I am creating a handlebars helper, which takes the following form:
define(['Handlebars'], function (Handlebars) {
Handlebars.registerHelper("myHelper", function (options) {
console.log('myHelper');
if (*condition*) {
console.log('myHelper False');
return options.inverse(this);
} else {
console.log('myHelper True');
return options.fn(this);
}
});
});
As you can see, I'm using require.js. I'm also using this as part of a Backbone.js application. In the template, the helper is called like so:
{{#myHelper}}
<!-- Some HTML -->
{{else}}
<!-- Some HTML -->
{{/myHelper}}
However, the helper always returns false because it is not recognized. I know this because the console.log is never called. I have other custom helpers in the application that work, but they all take in arguments. If I add a dummy argument, the helper works fine:
define(['Handlebars'], function (Handlebars) {
Handlebars.registerHelper("myHelper", function (dummy, options) {
console.log('myHelper');
if (*condition*) {
console.log('myHelper False');
return options.inverse(this);
} else {
console.log('myHelper True');
return options.fn(this);
}
});
});
Template:
{{#myHelper "string"}}
<!-- Some HTML -->
{{else}}
<!-- Some HTML -->
{{/myHelper}}
I'm using handlebars v1.0.0. Is this something that is addressed in 2.0.0? This isn't blocker, but I clearly would prefer not to use a dummy argument if possible.
Here is fiddle with helper you need. handlebars-1.0.rc.1 used. Also tried with handlebars-1.3.0 - works fine.
HTML
<script id="topLevel" type="text/x-handlebars-template">
{{#myHelper}}
it's truthy
{{else}}
it's falsy
{{/myHelper}}
</script>
JS
Handlebars.registerHelper('myHelper', function (options) {
if (true) {
console.log("It's true");
return options.fn(this);
}
console.log("It's false");
return options.inverse(this);
});
var _template = Handlebars.compile($('#topLevel').html());
$('body').append(_template());
So your issue could occur:
Outdated library or you are trying to use helper before it has been registered. Require.js loads libraries/files asynchronously, call handlebars as dependency. Example:
define(function(require){
var yourObj = function() {
require(['handlebars'], function (Handlebars) {
// use Handlebars here
});
};
return yourObj;
});
Hope it's help.
Related
In my Meteor project, I want a template section to be displayed according to a Session variable; however, the HTML inside the {{#if}} is not appearing reactively.
How do I get {{#if}}{{/if}} to change reactively with a session variable?
HTML:
<head>
<title>testApp</title>
</head>
<body>
{{> testTemplate}}
</body>
<template name="testTemplate">
{{#if isDisplayed}}
<div>It's working!</div>
{{/if}}
<button id="testButton">Toggle Display</button>
</template>
JavaScript:
Session.setDefault("displayVar", false);
Template.testTemplate.helpers({
isDisplayed: Session.get("displayVar")
});
Template.testTemplate.events({
"click #testButton": function () {
if (Session.get("displayVar")) {
Session.set("displayVar", false);
} else {
Session.set("displayVar", true);
};
}
});
Assigning a helper as a value:
isDisplayed : Session.get('displayVar')
Is the same as doing:
isDisplayed: false // or true, or whatever 'displayVar' is when run
To solve this simply use a function (here, an ES6 Arrow function since Meteor uses Babel):
isDisplayed: () => Session.get('displayVar')
Using a function makes sure that a Tracker computation is generated.
Your helper needs to return the value from an anonymous function:
Template.testTemplate.helpers({
isDisplayed: function(){
return Session.get("displayVar");
}
});
In the index's head I have:
// hashbang is set to true in routing.html
<script type="text/javascript">
if (window.location.href === "/users") {
console.log('Hey you!');
}
</script>
I need to implement some class bindings based on window.location but I'm testing to make sure it works in polymer. Does it? I does not work for me. When I go to localhost:3000/#!/users, noting in console.
Update:
If you're using page.js with the Polymer starter kit, then all the pages of your app are loaded on the first load.
In this starter kit, changing route with Page.js will display the need page and add display: none to hide the other pages.
For example, in your app/index.html, you should have something like this:
<iron-pages attr-for-selected="data-route" selected="{{route}}">
...
<section data-route="home">
...
</section>
<section data-route="users">
...
</section>
<section data-route="user-info">
...
</section>
...
</iron-pages>
The route parameters on the iron-pages element is set by Page.js in app/elements/routing.html:
page('/', function () {
app.route = 'home';
});
page('/users', function () {
app.route = 'users';
});
page('/users/:name', function (data) {
app.route = 'user-info';
app.params = data.params;
});
page('/contact', function () {
app.route = 'contact';
});
So if the route matches /users for example, then Page.js will set app.route = 'users';, and the iron-pages Polymer element will display the section with data-route="users" and hide the other ones without reloading anything, and thus not reloading your script.
But actually, since you're using Page.js, it'd be easier to integrate your code in the routing.html file like so:
page('/users', function () {
app.route = 'users';
console.log('Hey you!');
});
page('/users/:name', function (data) {
app.route = 'user-info';
app.params = data.params;
console.log('Hey ' + data.params.name);
});
page('/contact', function () {
app.route = 'contact';
console.log('Contact page');
});
You could start by trying to debug, for example, try logging the window href:
console.log(window.location.href);
Which should return:
http://localhost:3000/#!/users
Then you can have more insight on what your problem is! ;)
Indeed, window.location.href gives you the full URL, and it's supposed to do so (with Polymer or not).
On the other hand, window.location.hash returns everything in the url starting from the # character, giving you #!/users, so you could try:
<script type="text/javascript">
// Slice(2) to get rid of '#!'
if (window.location.hash.slice(2) === "/users") {
console.log('Hey you!');
}
</script>
Side note:
If you have query parameters after the #, like so:
localhost:3000/#!/users?number=42&name=value
window.location.href will then be #!/users?number=42&name=value
If you also want to get rid of the query parameters ?number=42&name=value in such a case, you could write:
<script type="text/javascript">
if (window.location.hash.slice(2).split("?")[0] === "/users") {
console.log('Hey you!');
}
</script>
I'm using meteorjs and the froala-reactive editor.
In my router I return the collection data to the template, which works fine.
But I need the ability to update the contents of editor. What is the best way to update _value?
The template code:
{{> froalaReactive _onbeforeSave=doSave inlineMode=false _value=getText}}
The router.js code:
Router.route('admin/pages/:_id', function () {
this.render('Page', {
data: function () {
Session.set('editorContent', 'editor content here');
return Pages.findOne({_id: this.params._id})
}});
});
Helper function:
Template.Page.helpers({
getText: function () {
var self = this;
return function (e, editor, data) {
return Session.get("editorContent");
};
}
});
I expect that when the session variable editorContent changes the displayed content in the editor updates, but this is not working.
Your helper function should simply return the Session value, instead of a function.
Like this:
getText: function () {
return Session.get('editorContent');
}
Here's a working example that you can clone and play around with.
In Meteor v0.8.2, it appears that helpers must be created for the individual templates (Template.story_en, Template.story_ne) called by the dynamic template.
Is it possible to create helpers for just the dynamic template (Template.story) and avoid repeating it for all possible templates that the dynamic templates can use, such as in the example below? It appears that the method I'm using requires a lot of repeated code.
story.html
<template name="story">
{{> UI.dynamic template=storyTemplate}}
</template>
story.js
Template.story.storyTemplate = function() {
return "story_" + Session.get('lang')
}
// This does not work
Template.story.color = function() {
return '#f00'
}
// This works
Template.story_en.color = function() {
return '#f00'
}
// This works (but seems to be unnecessary code)
Template.story_ne.color = function() {
return '#f00'
}
You could use global helpers, or pass the helpers in as data
Using Global Helpers (work on every template you have)
UI.registerHelper("color", function() {
return '#f00'
});
Or Passing in the helpers as data (does not work under current version of iron router - open bug).
Template.story.helpers({
dataHelpers: function() {
var data = UI._templateInstance().data || {};
//Add the helpers onto the existing data (if any)
_(data).extend({
color: function() {
return "#f00";
}
});
return data;
});
});
Then the html:
<template name="story">
{{> UI.dynamic template=storyTemplate data=dataHelpers}}
</template>
Then in the subtemplates you can use {{color}} without having the helpers in them.
You could also try your luck with using this instead of UI._remplateInstance.data if you have iron-router issues.
In my app, the <body> tag contains just a single <script type="text/x-handlebars> tag which contains all my views. Sproutcore 2.0 nicely adds a jQuery on-document-ready handler that parses those templates and renders them back into the DOM.
I'd like to call a function on one of the views as soon as it's rendered. The problem is that the re-insertion happens asynchronously, so I don't know when the view is available.
Example
Page
<body>
<script type="text/x-handlebars">
...
{{view "MyApp.TweetInputView"}}
...
</script>
</body>
View:
MyApp.TweetInputView = SC.View.extend({
init: function() {
// act like a singleton
MyApp.TweetInputView.instance = this;
return this._super();
},
focus: function() {
...
this.$().focus();
}
});
Initializer
// if the URL is /tweets/new, focus on the tweet input view
$(function() {
if (window.location.pathname === '/tweets/new') {
// doesn't work, because the view hasn't been created yet:
MyApp.TweetInputView.instance.focus();
}
});
I've also tried SC.run.schedule('render', function() { MyApp.TweetInputView.instance.focus(); }, 'call'); in the hopes that Sproutcore would run that after all the view rendering and insertion, but that does not seem to be the case.
Try this:
MyApp.TweetInputView = SC.View.extend({
didInsertElement: function() {
console.log("I've been rendered!");
}
});