I am using express handlebars to generate my view but I am having a problem which is that some of the content inside of my view is not translated properly because I am calling the I18n helper inside of an #each helper.
Index.handlebars :
{{#if roomsList}}
{{#each roomsList}}
<section id="roomNumber-{{RoomNumber}}">
<div class="room-status">
<div class="room-number">
<p>{{RoomNumber}}</p>
</div>
<div class="room-description room-status-{{RoomStatus}}">
<p class="clean">{{i18n "clean"}}</p>
<p class="dirty">{{i18n "dirty"}}</p>
<p class="in-progress">{{i18n "in-progress"}}</p>
<p class="to-inspect">{{i18n "to-inspect"}}</p>
</div>
</div>
<span class="clearfix"></span>
</section>
{{/each}}
{{/if}}
JS:
var Handlebars = require('Handlebars');
var i18n = require('i18n');
module.exports = {
//Setup our default layout
defaultLayout: 'default',
//Register handlebars helpers
helpers: {
//Register your helpers
//Helper for multiple languages
i18n: function () {
return i18n.__.apply(this, arguments);
},
__n: function () {
return i18n.__n.apply(this, arguments);
},
section: function (name, options) {
if (!this._sections) this._sections = {};
this._sections[name] = options.fn(this);
return null;
},
breaklines: function(text) {
text = Handlebars.Utils.escapeExpression(text);
text = text.replace(/(\r\n|\n|\r)/gm, '<br>');
return new Handlebars.SafeString(text);
}
}
}
How am I to resolve the issue?
It works for me:
{{#with ..}}{{i18n "to-inspect"}}{{/with}}
This moves the context upward
Easier and easier-to-read solutions are:
{{../i18n "parameter"}}
The solution above also works for dynamic variables
Related
I can't register Handlebars helper missing
This code below shows you the condition block in the page:
<table class="table table-striped sorting">
<thead>
<tr class="home">
<th>Title</th>
<th>Edit</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{{#each pages}}
<tr id="id_{{this._id}}" class="{{this.slug}}">
<td>{{this.title}}</td>
<td>Edit</td>
{{#checkhomepage home}}
<td></td>
{{else}}
<td><a class="confirmDeletion" href="/admin/pages/delete-page/{{this._id}}">Delete</a></td>
{{/if}}
</tr>
{{/each}}
</tbody>
</table>
And this code below show you my script code block:
<script src="https://code.jquery.com/ui/1.12.0/jquery-ui.min.js"></script>
<script>
$('tbody').sortable({
placeholder: "ui-state-hightlight",
update: function () {
var ids = $('tbody').sortable("serialize");
var url = "/admin/pages/reorder-pages";
$.post(url, ids);
}
});
Handlebars.registerHelper('checkhomepage', function (home) {
if(this.title == home){
return true;
}else{
return false;
}
});
});
</script>
in app.js
// view engine
var expressHbs = require('express-handlebars');
var app = express();
app.set('views', __dirname + '/views/');
app.set('view engine', 'handlebars');
var hbs = require('handlebars');
hbs.registerHelper("HelperMissing", function(value, options)
{
return parseInt(value) + 1;
});
app.engine('handlebars', engines.handlebars);
ReferenceError: engines is not defined
engines inside app.js what can i do to solve this problem
You are trying to create a Handlebars Block Helper, but you have a few issues.
In your template, you have the following:
{{checkhomepage home}}
<!-- Home stuff -->
{{else}}
<!-- Non-Home stuff -->
{{/if}}
This will not work because:
Block helpers must begin with a "#" in the opening tag, before the helper name.
The opening and closing helper tags in the template must match, ie. {{#checkhomepage home}}{{else}}{{/checkhomepage}}`.
You are passing the value of home to your helper. However, it is not apparent that there is a home value in the current context, so this value is probably undefined. Perhaps you want to pass the String "home", in which case, you will need to include quotes: {{#checkhomepage 'home'}}.
Next, in order for a block helper to work, you need to use the options argument. In particular, the options arg has an fn function that will render the template nested within the block, as well as an inverse function that will render what is nested in the else block.
The following is the helper re-written to use these options functions. (I have also renamed the home arg to title for clarity:
Handlebars.registerHelper('checkhomepage', function (title, options) {
if (this.title === title) {
return options.fn(this);
} else {
return options.inverse(this);
}
});
I have created a fiddle for your reference.
now i solved my problem
in past i dont set register helper in correct place
i'll post that code to see it
in app.js set helper into hbs caller like this
// view engine setup
app.engine('.hbs' , expressHbs({defaultLayout : 'layout' , extname : '.hbs' , helpers : {
checkhomepage: function (title, options) {
if (this.title === title) {
return options.fn(this);
} else {
return options.inverse(this);
}
},
if you see set helper function inside app.engine(.......,helper:{//here set your function helper like above
finally ill say thank's 76484 :) because he also help me to fix this issue
I'm developing a new proyect using angular and I have separated the: App (main module), controller and services in diferent files:
The responsabilities are:
indexApp.js
And them code is:
(function(indexApp) {
indexApp.App = {};
indexApp.Init = function() {
indexApp.App = angular.module("MainAppModule", ["MainControllerModule", "MainServiceModule"]);
};
}(window.indexApp = window.indexApp || {}));
indexController.js
And them code is:
(function (indexController) {
indexController.App = {};
indexController.MainController = function (service) {
var self = this;
var dataRetrieved = service.Login();
self.movie = {
title: dataRetrieved.Id,
director: dataRetrieved.Name,
date: dataRetrieved.LastName,
mpaa: "PG-13",
id: 0,
clickCommand: function () {
alert(self.movie.director);
},
loadData: function (id) {
console.log(id);
if (id !== 0) {
self.movie.title = "Titulo";
self.movie.director = "Director";
self.movie.date = "Mayo 16 de 2015";
self.movie.mpaa = "PG-25";
self.movie.id = id;
}
}
}
};
indexController.SetUrl = function (data) {
indexController.Redirect = data.Redirect;
};
indexController.Init = function () {
indexController.App = angular.module("MainControllerModule", []);
indexController.App.controller("MainController", indexController.MainController);
indexController.MainController.$inject = ["MainService"];
};
}(window.indexController = window.indexController || {}));
indexService.js
Them code is:
(function (indexService) {
indexService.App = {};
indexService.MainService = function () {
var self = this;
self.Login = function () {
return {
Id: 1,
Name: "Freddy",
LastName: "Castelblanco"
};
};
};
indexService.SetUrl = function (data) {
indexService.Login = data.Login;
};
indexService.Init = function () {
indexService.App = angular.module("MainServiceModule", []);
indexService.App.service("MainService", indexService.MainService);
};
}(window.indexService = window.indexService || {}));
At the end in my view I call the follow methods:
#using System.Web.Optimization
#{
Layout = "~/Views/Shared/_Layout.cshtml";
var id = 20;
}
<div ng-app="MainAppModule">
<div ng-controller="MainController as vm">
<div ng-init="vm.movie.loadData(#id)">
<div class="row">
<div class="col-md-12">{{vm.movie.title}}</div>
<input type="text" ng-model="vm.movie.title"><br>
</div>
<div class="row">
<div class="col-md-12">{{vm.movie.director}}</div>
</div>
<div class="row">
<div class="col-md-12">{{vm.movie.date}}</div>
</div>
<div class="row">
<div class="col-md-12">{{vm.movie.mpaa}}</div>
</div>
<div class="row">
<div class="col-md-12">
<button type="button" ng-click="vm.movie.clickCommand()">Click me !!</button>
</div>
</div>
</div>
</div>
</div>
#section scripts
{
#Scripts.Render("~/bundles/index")
<script type="text/javascript">
indexApp.Init();
indexService.Init();
indexController.Init();
</script>
}
Is a correct way to use angular ?? Im using dependency injection.
How you define an angular app is up to you but angular provides modules to deal with code organization, prevent global scope pollution, dependency injection among other things
Angular apps don't have a main method. Instead modules declaratively specify how an application should be bootstrapped
You are using a common method found in other frameworks of using var self = this to add functionality to your app but angular comes with a nice gift scopes. Scopes are extremely useful because all angular apps have one and only one $rootScope wich you can use to store commonly used functionality all across your application. Also scope are organized in a hierarchy wich give you the abitity to nest scopes and make some logic work only on specific DOM elements.
Scopes are arranged in hierarchical structure which mimic the DOM structure of the application. Scopes can watch expressions and propagate events.
To glue your application you should use $watch on the scope to be notified of changes but usually you use any of the predefined directives that do this automatically for simple task like binding and changing attributes eg. ngBind, ngClick, etc.
Scope is the glue between application controller and the view. During the template linking phase the directives set up $watch expressions on the scope. The $watch allows the directives to be notified of property changes, which allows the directive to render the updated value to the DOM.
I personally don't use IIFE when I'm using angular but this is a personal choice. The iife allows you to prevent global scope pollution by wrapping variables inside a function scope so you don't have name collisions but angular introduces providers which can help you to create functionality using factories and services so basically you wrap all your functionality in one of them (read which is the most suitable for your task) and you have already included dependency injection in the mix for free.
Finally there are three ways to use dependency injection (or ways to anotate it).
Inline Array Annotation
mymodule.controller('MyController', ['$scope', function($scope) {
// your code
}]);
$inject Property Annotation
var MyController = function($scope) {
// ...
}
MyController.$inject = ['$scope'];
someModule.controller('MyController', MyController);
Implicit Annotation
someModule.controller('MyController', function($scope) {
// ...
});
You are free to use the way that you feel more confortable with but you should be aware that the last alternative is dangerous if you plan to minify your code because angular rely in variable names to find the dependencies and those will get renamed in the minification process. Personaly I use the first and it seems the most popular since you don't need the extra variable used in the second alternative.
Your code can be rewritten as follows
angular.module('services', []).service('MainService', function () {
return {
Login: function () {
return {
Id: 1,
Name: "Freddy",
LastName: "Castelblanco"
};
}
};
});
angular.module('controllers', []).controller('MainController', ['$scope', 'MainService', function ($scope, service) {
var dataRetrieved = service.Login();
$scope.movie = {
title: dataRetrieved.Id,
director: dataRetrieved.Name,
date: dataRetrieved.LastName,
mpaa: "PG-13",
id: 0
};
$scope.clickCommand = function () {
alert($scope.movie.director);
};
$scope.loadData = function (id) {
if (id !== 0) {
$scope.movie.title = "Titulo";
$scope.movie.director = "Director";
$scope.movie.date = "Mayo 16 de 2015";
$scope.movie.mpaa = "PG-25";
$scope.movie.id = id;
}
}
}]);
angular.module('MainAppModule', ['controllers', 'services']);
And your html
<div ng-app="MainAppModule">
<div ng-controller="MainController">
<div ng-init="loadData(#id)">
<div class="row">
<div class="col-md-12">{{movie.title}}</div>
<input type="text" ng-model="movie.title">
<br>
</div>
<div class="row">
<div class="col-md-12">{{movie.director}}</div>
</div>
<div class="row">
<div class="col-md-12">{{movie.date}}</div>
</div>
<div class="row">
<div class="col-md-12">{{movie.mpaa}}</div>
</div>
<div class="row">
<div class="col-md-12">
<button type="button" ng-click="clickCommand()">Click me !!</button>
</div>
</div>
</div>
</div>
</div>
{Update}
You can also check AngularJS: Understanding design pattern for guidelines on how you should structure your angular app
I have some code I am trying to reverse engineer. Here is the script...
define(function (require) {
"use strict";
var $ = require('jquery'),
Handlebars = require('handlebars'),
groupAdapter = require('adapters/group'),
homeHtml = require('text!tpl/Home.html'),
groupListItemHtml = require('text!tpl/GroupList.html'),
homeTpl = Handlebars.compile(homeHtml),
groupListItemTpl = Handlebars.compile(groupListItemHtml);
return function () {
this.initialize = function () {
// Define a div wrapper for the view. The div wrapper is used to attach events.
this.$el = $('<div/>');
this.$el.on('keyup', '.search-key', this.findByName);
};
this.render = function () {
this.$el.html(homeTpl());
return this;
};
this.findByName = function () {
groupAdapter.findByName($('.search-key').val()).done(function (products) {
$('.product-list').html(groupListItemTpl(products));
});
};
this.initialize();
};
});
...here is the template that is being used in tpl/home.html
<div class="topcoat-navigation-bar">
<div class="topcoat-navigation-bar__item center full">
<h1 class="topcoat-navigation-bar__title">Daily Comedy App</h1>
</div>
</div>
<div class="search-bar">
<input type="search" placeholder="search" class="topcoat-search-input search-key">
</div>
<div class="topcoat-list__container scroller" style="top:138px;">
<ul class='topcoat-list list product-list '></ul>
</div>
So...the current functionality is that a search bar is displayed on the home page, and when text is typed data from my json is displayed on the page.
What I would like to do is simply loadd all the data from my json on this initial page, rather than have the user search.
My JS stinks, so be kind! ;)
PS phonegap people might recognise this code from the EU Day.
my first post on stackoverflow. (and english is not my native tongue).
I am trying to learn how to use emberjs.
It's not easy because of the lack of good tutorials.
So I decided to code a chat, I use nodejs and socket.io server-side.
Html
<script type="text/x-handlebars">
<div class="page">
<div class="users">
</div>
<div class="messagebox">
{{#view App.TextField valueBinding="currentMsg" placeholder="your message"}}{{/view}}
<button {{action "sendMsg"}}>Send</button>
</div>
<div id="chatbox">
{{#collection contentBinding="App.MsgsController" tagName="ul"}}
<b>{{value}}</b>
{{/collection}}
</div>
</div>
</script>
Javascript
var id;
var socketio = io.connect("127.0.0.1:8888");
socketio.on('id', function (data) {
id = data;
});
socketio.on("broadcast", function (data) {
if (id == data.id) {
return
}
App.MsgsController.addMsg(data.message);
});
socketio.on("own", function (data) {
App.MsgsController.addMsg(data.message);
});
App = Ember.Application.create();
App.Msg = Ember.Object.extend({
value: null
});
App.MsgsController = Ember.ArrayController.create({
content: [],
addMsg: function (value) {
var msg = App.Msg.create({
value: value
});
this.pushObject(msg);
}
});
App.TextField = Ember.TextField.extend({
insertNewline: function() {
this.get("controller").send("sendMsg");
}
});
App.ApplicationController = Ember.Controller.extend({
currentMsg: 't',
sendMsg: function () {
var currentMsg = this.get('currentMsg');
if(currentMsg) {
socketio.emit("message", { message: currentMsg, id: id});
this.set('currentMsg', '');
}
}
});
I want to focus App.TextField after the App.ApplicationController.sendMsg call.
I tried
App.TextField.$().focus()
but it seems that I can only use $() inside of its methods.
Someone can help me, please?
Edit :
Ok, I found the answer.
App.TextField is like "class", and the one on the view is an instance.
I must add an id in my view
{{#view App.TextField valueBinding="currentMsg" placeholder="your message" id="mytextfield"}}{{/view}}
and use jquery selector to access to the instance
$('#mytextfield').focus();
Use didInsertElement hook for the view to handle jquery methods.
http://emberjs.com/api/classes/Ember.View.html#event_didInsertElement
I created a helper in Handlebars to help with logic, but my template parses the returned html as text rather than html.
I have a quiz results page that is rendered after the quiz is completed:
<script id="quiz-result" type="text/x-handlebars-template">
{{#each rounds}}
{{round_end_result}}
{{/each}}
<div class="clear"></div>
</script>
For each of the rounds, I use a helper to determine which template to render a round's result:
Handlebars.registerHelper("round_end_result", function() {
if (this.correct) {
var source = '';
if (this.guess == this.correct) {
console.log("correct guess");
var source = $("#round-end-correct").html();
} else {
var source = $("#round-end-wrong").html();
}
var template = Handlebars.compile(source);
var context = this;
var html = template(context);
console.log(html);
return html;
} else {
console.log("tie");
}
});
Here is a template that describes a correct round (let's take say it rendered the #round-end-correct template):
<script id="round-end-correct" type="text/x-handlebars-template">
<div></div>
</script>
Here is what gets rendered:
<div></div>
Not as HTML, but as text. How do I get it to actually render the HTML as HTML, rather than text?
I assume that unescaping in Handlebars works the same as in vanilla Mustache.
In that case use triple mustaches to unescape html, i,e: {{{unescapedhtml}}}, like:
<script id="quiz-result" type="text/x-handlebars-template">
{{#each rounds}}
{{{round_end_result}}}
{{/each}}
<div class="clear"></div>
for ref see:
http://mustache.github.com/mustache.5.html
Geert-Jan's answers is correct but just for reference you can also set the result to "safe" directly inside the helper (code from handlebars.js wiki)
Handlebars.registerHelper('foo', function(text, url) {
text = Handlebars.Utils.escapeExpression(text);
url = Handlebars.Utils.escapeExpression(url);
var result = '' + text + '';
return new Handlebars.SafeString(result);
});
With that you can use regular double handlebars {{ }} and handlebars won't escape your expression.