Handlebars variable is undefined when used inside custom helper - handlebars.js

I am using Handlebars in combination with express.js and registered a custom helper:
let hbs = exphbs.create({
extname: 'hbs',
defaultLayout: 'layout',
layoutsDir: path.join(__dirname, 'views/'),
partialsDir: [
path.join(__dirname, 'views/modals/'),
path.join(__dirname, 'views/partials/'),
path.join(__dirname, 'views/charts/'),
path.join(__dirname, 'views/customScripts/'),
],
helpers: {
when: function(operand_1, operator, operand_2, options){
// Use with:
// {{#when <operand1> 'eq' <operand2>}}
console.log("Using the when helper:");
console.log("operand_1: " + operand_1 + " operator: " + operator + " operand_2: " + operand_2);
let operators = {
'eq': function (l, r) {
return l == r;
},
'noteq': function (l, r) {
return l != r;
},
'gt': function (l, r) {
return Number(l) > Number(r);
},
'or': function (l, r) {
return l || r;
},
'and': function (l, r) {
return l && r;
},
'%': function (l, r) {
return (l % r) === 0;
}
}
, result = operators[operator](operand_1, operand_2);
if(result) return options.fn(this);
else return options.inverse(this);
}
}
In my file I have the following HTML code:
<div class="col-lg-2">
<label for="assetSelection">
Asset:
</label>
{{currentFormSelectionAsset}} <-- This works
<select class="form-control" id="assetSelection" name="assetSelection">
{{#each assetList}}
<option value="{{this.id}}" {{#when currentFormSelectionAsset 'eq' this.id}}selected{{/when}}>{{this.name}}</option>
{{/each}}
</select>
</div>
Calling my html file I only get the following output in my console:
Using the when:
operand_1: undefined operator: eq operand_2: 2
It looks like I cannot pass the variable into my custom helper but due to the fact that I can print it (one line above the select) makes sure that the variable is present and works.
Is there a limitation about using variables inside custom helper or am I missing something?

Related

How to use equal condition in Handelbars (HBS) with each condition?

I am using Handlebar and try to use if condition with equal like if(value.A==value.B).
Below is the code where i am trying this.
<div>
{{#each Data}}
{{#ifCond value.A value.B}}
<tr>
<td>{{value.C}}</td>
<td>{{value.D}}</td>
<td>{{value.E}}</td>
</tr>
{{/ifCond}}
{{/each}}
</div>
I have used below code but it is giving TypeError: hbs.registerHelper is not a function
The code is in app.js :-
var expressHbs = require('express-handlebars');
app.engine('.hbs', expressHbs.engine({ defaultLayout: 'layout', extname: '.hbs'}));
app.set('view engine', '.hbs');
var hbs = expressHbs.create({});
hbs.registerHelper('ifCond', function(v1, v2, options) {
if(v1 === v2) {
return options.fn(this);
}
return options.inverse(this);
});
There are a few problems here.
First, as your error indicates, registerHelper is not a function on the instance object returned by expressHbs.create({}). The returned object is an instance of an ExpressHandlebars object and this is not the same thing as the Handlebars library which has a registerHelper function.
The way to register helpers on an instance of ExpressHandlebars is via the configuration object passed to .create():
var hbs = expressHbs.create({
helpers: {
ifCond(v1, v2, options) {
if (v1 === v2) { return options.fn(this); }
return options.inverse(this);
},
}
});
Unfortunately, this change alone will not suffice. It will lead to the Error:
Missing helper: "ifCond"
The issue here is that we have created the ifCond helper on the hbs instance, but the hbs instance is not the one we are setting as our Express View Engine.
Our Express View Engine is set with the result of a call to expressHbs.engine with some configuration properties.
Express-Handlebars gives us the option of using the .engine function to construct an engine object or the .create function to create instances of ExpressHandlebars, each with its own .engine object.
In your example, you are not doing anything with the hbs instance. Your helpers need to be added to the configuration passed in your expressHbs.engine call:
app.engine(
".hbs",
expressHbs.engine({
defaultLayout: "layout",
extname: ".hbs",
helpers: {
ifCond(v1, v2, options) {
if (v1 === v2) { return options.fn(this); }
return options.inverse(this);
},
},
})
);
var hbs = expressHbs.create({}); can be removed.

angular js : ng-class use

i get a data for RestApi and i want create a function to retrieve data from json
if the defautl == null or the default. value = false . i change the color using ng -class of the component.
1:
commandStateTypes:{
defaut interne:null
defaut liaison:{date: "2016-05-30T01:01:04", value: true, name: "defaut liaison", idComponent: 1}}
component:{id:1 ,idInstallation:1,name:"Commande1"}
2:
commandStateTypes:{
defaut interne:null
defaut liaison:{date: "2016-05-30T01:01:04", value: true, name: "defaut liaison", idComponent: 2}}
component:{id:1 ,idInstallation:1, name:"Commande 2"}
controller :
$scope.status = { state: ['OK', 'KO'] }
Service.GetComponentsHelpers(function (data)
{
$scope.componentsHelper = data;
},
function (data)
{
});
$scope.GetStateByComponents = function ()
{
angular.forEach($scope.componentsHelper, function (componentsState, key)
{
angular.forEach($scope.componentsHelper.commandStateTypes, function(value, key){
});
});
}
chtml:
<div class="ajoutComponent">
<div class="motorsVitesse" ng-class="{ GetStateByComponents() ? 'mDangerV':'mDefaultV'}" alt="{{status.state[1]}}"> {{status.state[1]}}</div>
</div>
Try this in HTML:
ng-class="{'mDangerV': GetStateByComponents(), 'mDefaultV': !GetStateByComponents()}"

How could I make this Meteor code reactive?

I have a number of 'multiple' selectors, but for the sake of this example, let's say I have two.
<form class="input-field col s10">
<select multiple id="jans-room21">
<option value="" disabled selected>Add Names</option>
{{#each Jans21}}
<option value= '{{FullName}}' selected = {{formatSelectedNames21 RoomJans}} >{{FullName}}</option>
{{/each}}
</select>
</form>
<form class="input-field col s10">
<select multiple id="jans-room22">
<option value="" disabled selected>Add Names</option>
{{#each Jans22}}
<option value='{{FullName}}' selected = {{formatSelectedNames22 RoomJans}}>{{FullName}}</option>
{{/each}}
</select>
</form>
Jans21 and Jans22 are returning a number of documents from the DB. They'll display the selected names for that room, or those that have no 'RoomJans' property or have a 'RoomJans' equal to ''. They will exclude those names that were chosen in the other selector.
Template.jansNameSelect.helpers({
Jans21: function () {
return Programs.find({ $and: [{ CampYear: Session.get('GlobalCurrentCampYear') }, { $or: [{ RoomJans: '' }, { RoomJans: { $exists: 0 }}, { RoomJans: { $in: ['21A', '21B'] }}]}]}, { sort: { FullName: 1 }}).fetch();
},
Jans22: function () {
return Programs.find({ $and: [{ CampYear: Session.get('GlobalCurrentCampYear') }, { $or: [{ RoomJans: '' }, { RoomJans: { $exists: 0 }}, { RoomJans: { $in: ['22A', '22B'] }}]}]}, { sort: { FullName: 1 }}).fetch();
}
});
When a button is clicked, a method is called to update the DB and store those names.
// ...
$('#room_21_jans_button').on('click', function() {
var roomValue = $('input[name="room_select_21_jans"]:checked').val();
if (roomValue) {
var selectedValues = [];
$r21jans.find("option:selected").each(function () {
selectedValues.push($(this).val());
});
selectedValues.splice(0, 1);
var unselectedValues = [];
$r21jans.find("option:not(:selected)").each(function () {
unselectedValues.push($(this).val());
});
Meteor.call('roomUpdateSN',
selectedValues,
unselectedValues,
roomValue,
Session.get('GlobalCurrentCampYear')
);
//...
What I'm after is when names are selected in the first selector, and subsequently saved to the database, the second selector will update its list of names to remove those names that were selected from the first. I had thought that this would be reactive since I am performing a database action, such that the 'Jans22' function would fire again if names were chosen from the first selector and saved to the DB. But it isn't. It will, however, load the right names on a refresh. Is there a way to get this to be reactive?
When using a UI component framework on top of Meteor templates, you need to tell the framework when the template underneath it has changed. This is because the framework (materialize in this case) uses the <select> rendered by the template as an input, and then creates a new set of DOM elements to render the desired UI look-and-feel. If the <option>'s change, you need to tell the framework to re-run this process.
In this case you need to re-run the
$('select').material_select();
every time there is a change. The easiest way to do this in my opinion is using a deferred function from the helper itself:
Template.jansNameSelect.helpers({
Jans21: function () {
Meteor.defer( function() { $('select#jans-room21').material_select(); } );
return Programs.find({ $and: [{ CampYear: Session.get('GlobalCurrentCampYear') }, { $or: [{ RoomJans: '' }, { RoomJans: { $exists: 0 }}, { RoomJans: { $in: ['21A', '21B'] }}]}]}, { sort: { FullName: 1 }}).fetch();
},
Jans22: function () {
Meteor.defer( function() { $('select#jans-room22').material_select(); } );
return Programs.find({ $and: [{ CampYear: Session.get('GlobalCurrentCampYear') }, { $or: [{ RoomJans: '' }, { RoomJans: { $exists: 0 }}, { RoomJans: { $in: ['22A', '22B'] }}]}]}, { sort: { FullName: 1 }}).fetch();
}
});

How to Write an Angular directive to update CSS class based on form validation

I have the following Angular/HTML which uses Bootstrap CSS classes to indicate whether a form is valid or not using Angular validation.
<form name="editor" novalidate>
<div class="form-group" ng-class="{'has-error': editor.name.$dirty && (editor.name.$error.invalid || editor.name.$error.required)}">
<input type="text" class="form-control" name="name" maxlength="150" data-ng-model="name" required />
</div>
</form>
With more than one div.form-group obviously there is a lot of code repetition. What I would like to do is create an Angular attribute directive which will update the class of the div.form-group element if the input contained within it is invalid.
This is the markup I would like to get to:
<div class="form-group" data-form-group data-input="editor.name">
...
</div>
I have the following shell of a directive but I don't know how to monitor the editor.name (or input attribute) in order to update the class.
myApp.directive("formGroup", function () {
return {
restrict: "A",
scope: {
input: "#"
},
replace: false,
link: function (scope, elem, attrs) {
}
};
});
I assume I need to put the relevant code in the link function, and perhaps using $watch but other than that I am a bit in the dark
you should use ngModelController properties for doing this:
myApp.directive("formGroupElement", function () {
return {
restrict: "A",
require: "ngModel"
scope: {
input: "#"
},
replace: false,
link: function (scope, elem, attrs, ngModelController) {
//ngModelController.$setValidity();
}
};
});
or ngFormController:
myApp.directive("formGroup", function () {
return {
restrict: "A",
require: "ngForm"
scope: {
input: "#"
},
replace: false,
link: function (scope, elem, attrs, ngFormController) {
//ngFormController.$setValidity();
}
};
});
I have ended up with the following:
myApp.directive("formGroup", function ($parse) {
return {
restrict: "A",
scope: {
input: "#"
},
replace: false,
require: "^form",
link: function (scope, elem, attrs, ctrl) {
var expression = [ctrl.$name, scope.input, "$invalid"].join(".");
scope.$parent.$watch(expression, function (val) {
alert(expression + " " + val); // Pops the value.
});
}
};
});
Note that although the expression in the HTML is editor.name.$error.invalid, in the link function it is editor.name.$invalid.
Using the form controller means I don't have to set the ng-model attribute on the <div>.

How to Get Array Data in HTML Using Meteor JS?

I need to know about to get array data of collections using Meteor JS. I did a simple example with insert data to collections in Meteor JS as shown below :
if (Hcare_Fileds.find().count() === 0) {
var fieldData = [
{fieldName: "Hcare1",
fieldOptions: [ "Bike","Car","TV","Radio","etc"]
},
{fieldName: "Hcare2",
fieldOptions: [ "Bike1","Car1","TV1","Radio1","etc"]
},
{fieldName: "Hcare3",
fieldOptions: [ "Bike2","Car2","TV2","Radio2","etc"]
}
];
for (var i = 0; i < fieldData.length; i++)
{
var list_id = Hcare_Fileds.insert({fieldname: fieldData[i].fieldName,fieldoptions: fieldData[i].fieldOptions}
, function( error, result)
{
if ( error ) console.log ( "Error :"+error.reason ); //info about what went wrong
if ( result )
{
console.log ( "Success="+result );//the _id of new object if successful
}
});
}
}
And Access the above collection fieldoption array data sa shown below :
{{#each fieldName}}
<div class="fmenu {{isselected}}"> {{ fieldname }} </div>
<div class="foptions">
{{#if isselected}}
{{ fieldoptions }}
// Here get fieldoptions data when selected filedname as Bike,Car,Tv,Radio,etc but i need to return like as array but it returns string
{{/if}}
</div>
{{/each}}
JS Code :
if (Meteor.isClient)
{
Session.set('currentFieldName', '');
Template.main.fieldName = function ()
{
return Hcare_Fileds.find().fetch();
};
//TODO ::
Template.main.events
({
'click .fmenu': function (e,t)
{
// template data, if any, is available in 'this'
if (typeof console !== 'undefined')
console.log("You pressed the button"+this.fieldname);
e.preventDefault();
Session.set('currentFieldName', this.fieldname);
}
});
Template.main.isselected = function ()
{
console.log("currentFieldName");
return Session.equals("currentFieldName", this.fieldname) ? "selected" : '';
};
}
I need to get data as array not string like Bike,Car,Tv,Radio,etc. I didn't get any idea about this.So Can you please suggest me what to do?

Resources