handlebars, access to parent each loop variable - javascript

I have code like here. I would like to set input name to #index from previous each loop.
Can I access #previousIndex somehow? Or can I assign inputs to some kind of group created before second each loop that will set all input names inside?
Data I receive:
questions:[
{
question:"How old are you?",
answers:[
'16',
'18',
'20',
'25',
'other'
]
},
{
question:"How many kids do you have?",
answers:[
'0',
'1',
'2',
'other'
]
}
]
hbs code:
{{#each questions}}
<li>
<h3 class='question'>{{this.question}}</h2>
<div class='flexbox'>
{{#each this.answers}}
<label class="container">
<input type='radio' name='{{#previousIndex}}' value='{{#index}}'>
<span class='checkmark'>{{this}}</span>
</label>
{{/each}}
</div>
</li>
{{/each}}

When you are using #each then you get the index in #index and if you want previousIndex why not just do parseInt(#index) - 1
Here's how you do it:
var Handlebars = require('handlebars');
Handlebars.registerHelper("previous", function(value, options)
{
return parseInt(value) - 1;
});
Now update your hbs code as:
{{#each questions}}
<li>
<h3 class='question'>{{this.question}}</h2>
<div class='flexbox'>
{{#each this.answers}}
<label class="container">
<input type='radio' name='{{previous #index}}' value='{{#index}}'>
<span class='checkmark'>{{this}}</span>
</label>
{{/each}}
</div>
</li>
{{/each}}
Hope this works for you!

I can't give you a 100% working code (environment is not currently setup) but from my previous experience with handlebars I will try to guide you to a possible solution.
{{assign "previousIndex" 0}}
{{#each questions}}
<li>
<h3 class='question'>{{this.question}}</h2>
<div class='flexbox'>
{{#each this.answers}}
<label class="container">
<input type='radio' name='{{../previousIndex}}' value='{{#index}}'>
<span class='checkmark'>{{this}}</span>
</label>
{{/each}}
</div>
</li>
{{assign "../previousIndex" "{{#index}}"}}
{{/each}}

I had to create two helper functions like here. Get is here because I wasn`t able to access variable directly from .hbs file by simply writing {{myVariableName}}.
Hbs file looks now like here.
<div class='flexbox'>
{{assign "parentIndex" #index }}
{{#each this.answers}}
<label class="container">
<input type='radio' name='{{get "parentIndex"}}' value='{{#index}}'>
<span class='checkmark'>{{this}}</span>
</label>
{{/each}}
</div>
helpers
assign: function(varName, varValue, options) {
options.data.root[varName] = varValue;
}
get: function(varName, options) {
return options.data.root[varName]
}

Related

Run functions with checkbox interactions

I am trying to create checkboxes that will add an object to an array when checked, but will remove that object when unchecked. I am not sure if this is the correct way, but will show what I have below. Using Angular 2 by the way.
Original Code:
<div>
<ul>
<a *ngFor="let perkResult of perkList.results" (click)="onAddPerk(perkResult)">
<li>{{ perkResult.perk }}</li>
</a>
</ul>
</div>
<div *ngFor="let perk of company.perks;>
{{ perk.perk }}
<a (click)="onDeletePerk(i)"></a>
</div>
Functions:
onAddPerk(perkResult) {
// Adds a new perk to the array
this.company.perks.push({perk: (perkResult.perk)});
}
onDeletePerk(i: number) {
// Delete the perk in the selected index
this.company.perks.splice(i, 1);
}
And I want to do something like this:
<div *ngFor="let benefitResult of benefitList.results" >
<a (click)="onAddBenefit(benefitResult)">
<input type="checkbox" />
//Basically if checked then run add function, if not checked then run delete function
</a> {{ benefitResult.benefit }}
</div>
EDIT:
Got to this point, but cannot reference my other scope for the delete.
<ul *ngIf="benefitList">
<div *ngFor="let benefitResult of benefitList.results; let i = index" >
<input type="checkbox" (change)="updateBenefits($event, benefitResult, i)" >
<li>
{{ benefitResult.benefit }}
</li>
</div>
</ul>
//Original delete
<div *ngFor="let benefit of company.benefits; trackBy: customTrackBy; let i = index">
{{benefit.benefit}}
<a (click)="onDeleteBenefit(i)"></a>
</div>
//function
updateBenefits(event, benefitResult, i) {
if(event.srcElement.checked) {
this.company.benefits.push({benefit: (benefitResult.benefit)});
} else {
this.company.benefits.splice(i, 1);
}
}
You could listen to the change event on the checkboxes. Then pass the event along with the object and index to a method that will look at the status of the checkbox element.
In the component:
optionsList = {
results: [{
perk: 'free coffee',
checked: false
}, {
perk: 'car',
checked: false
}, {
perk: 'pastry',
checked: false
}, {
perk: 'free post-it notes',
checked: false
}]
};
updatePerks(event, perkResult) {
perkResult.checked = event.srcElement.checked;
}
In your HTML template:
<ul>
<li *ngFor="let listItem of optionsList.results">
<label><input type="checkbox" (change)="updatePerks($event, listItem)">
{{ listItem.perk }}</label>
</li>
</ul>
Note: I don't know your data structure, so make the necessary adjustment or post a data sample if you need more help.

How to get access to outside properties in a nested {{#each}}?

I have an {{#each}} block that runs through a list of stops that a driver must make. Each stop needs to have a select run and the select draws in from another collection. So I have the select in another {{#each}}.
Now I need to programmatically check if a driver was already selected in the db for this particular stop as the select is running and have it get selected.
My problem is that I need to access info from the external {{#each}} to do the comparison in the internal {{#each}}.
Below is the code that I have but when I run it the stopNum is undefined.
Any help here is greatly appreciated.
<td class="text-left">
{{#if notEquals stopNum 0}}
<select class="form-control driverName clearForm" id="driverName{{stopNum}}" name="driverName{{stopNum}}">
<option value="" ></option>
{{#each drivers}}
{{#if dispatchDriverSelected driversName stopNum}}
<option value="{{driversName}}" selected>{{driversName}}</option>
{{else}}
<option value="{{driversName}}">{{driversName}}</option>
{{/if}}
{{/each}}
</select>
{{/if}}
</td>
Within #each, the context is set to the current element.
If you want to get an external item/property, you can get the parent context using .. in the template code or using Template.parentData(numLevels) in helpers:
{{#each drivers}}
{{#if dispatchDriverSelected driversName ../stopNum}}
<option value="{{driversName}}" selected>{{driversName}}</option>
{{else}}
<option value="{{driversName}}">{{driversName}}</option>
{{/if}}
{{/each}}
Here is a simple example:
Template structure:
<template name="example">
{{#with datum}}
<div class="wrapper">
{{outer}}
<div class="items">
{{#each inner}}
<div class="inner">
{{prop}} {{someFunc ../outer}}
</div>
{{/each}}
</div>
</div>
{{/with}}
</template>
Helpers:
Template.example.helpers({
datum() {
return {
outer: 'outer 1',
inner: [{
prop: 'inner 1'
},{
prop: 'inner 2'
}]
}
},
someFunc(datum) {
return `processed ${datum}`;
}
});
renders to:
<div class="wrapper">
outer 1
<div class="items">
<div class="inner">
inner 1 processed outer 1
</div>
<div class="inner">
inner 2 processed outer 1
</div>
</div>
</div>

Array with ng-repeat error

$http.get('***').success(function(data, status,response)
{
$scope.items=data;
var getdata=JSON.stringify(data.D_Services);
console.log(getdata);
});
im getting in console
D_Services: "Wash,Tyres,Spares,Accessories";
please any one help me out
<div ng-controller="Test1Controller" data-ng-init="loadservice()">
<div ng-repeat="item in items">
<input type="checkbox" ng-model="item.SELECTED" ng-true-value="'Y'" ng-false-value="'N'"/> {{item.D_Services}}
</div>
</div>
I need answer like this please any one help me out
now im getting
You declare your "array" as a String.
Make it an Array instead:
$scope.items = ['Wash', 'Tyres', 'Spares', 'Accessories'];
If you need to keep it as a String, use .split():
<div ng-repeat="item in items.split(',')">
Store your items in an array
$scope.items = ['Wash', 'Tyres', 'Spares', 'Accessories'];
Then
<div ng-repeat="item in items">
Because you want to be able to set a selected flag for each string, you need a list of objects.
$scope.items=[
{title: "Wash", selected: false},
{title: "Tyres", selected: false},
{title: "Spares", selected: false },
{title: "Accessories", selected: false}" ];
Then you can use the following code.
<div ng-controller="Test1Controller" data-ng-init="loadservice()">
<div ng-repeat="item in items">
<input type="checkbox" ng-model="item.selected" ng-true-value="'Y'" ng-false-value="'N'"/> {{item.title}}
</div>
</div>
HTML
<div ng-controller="Test1Controller" data-ng-init="loadservice()">
<div ng-repeat="item in items">
<input type="checkbox" ng-model="item.SELECTED" ng-true-value="'Y'" ng-false-value="'N'"/> {{item}}
</div>
</div>
In controller
$scope.items=["Wash","Tyres","Spares","Accessories"];
Fiddle for better understanding
http://jsfiddle.net/sykxr2ex/
plunker If you want to use services
http://plnkr.co/edit/Y0Y5o2?p=preview

Bind-attr "for" attribute in Ember.js

I'm trying to include a unique id for an and tag to get a custom checkbox. The {{input}} tag outputs it correctly but the <label {{bind-attr for=binding}} does not. I'm a frontend guy new to Ember so I'm sure this should be trivial.
<ul class="col-menu dropdown-menu">
{{#each columns}}
<li>
{{input type="checkbox" checked=checked id=binding}}
<label {{bind-attr for=binding}}><span></span></label>
<span>{{heading}}</span>
{{columns}}
</li>
{{/each}}
</ul>
Here is the output ...
<li>
<input id="activityTotal" class="ember-view ember-checkbox" type="checkbox" checked="checked">
<label data-bindattr-2689="2689">
<span></span>
</label>
<span>
<script id="metamorph-53-start" type="text/x-placeholder"></script>
Events
<script id="metamorph-53-end" type="text/x-placeholder"></script>
</span>
<script id="metamorph-54-start" type="text/x-placeholder"></script>
<script id="metamorph-54-end" type="text/x-placeholder"></script>
First of all you should wrap all this in a component.
You really should not be manually binding the id like this as ember uses it internally but I think it is acceptable in this case as I can't think of a better way but you need to maintain the uniqueness.
I would so something like this to ensure the id is unique and uses
checkBoxId: Ember.computed(function(){
return "checker-" + this.elementId;
}),
Here is the component's javascript file that will be executed when the component is ran:
App.XCheckboxComponent = Ember.Component.extend({
setup: Ember.on('init', function() {
this.on('change', this, this._updateElementValue);
}),
checkBoxId: Ember.computed(function(){
return "checker-" + this.elementId;
}),
_updateElementValue: function() {
this.sendAction();
return this.set('checked', this.$('input').prop('checked'));
}
});
Here is the component's template, I use unbound to bind the unique checkBoxId with the label's for:
<label for="{{unbound checkBoxId}}">
{{label}}
<input id="{{unbound checkBoxId}}" type="checkbox" {{bind-attr checked="checked"}}>
</label>
Here is how you might use it:
<ul>
{{#each contact in model}}
<li>{{x-checkbox label=contact.name enabled=contact.enabled}}</li>
{{/each}}
</ul>
And here is a working jsbin for ember 1.7.1 and here is a working jsbin for ember 1.11.1.
As of version 1.11, you don't need bind-attr. You should be able to bind the attribute like this:
<label for={{binding}}></label>

Handlebars.js custom helper to count nested elements

I want to make a custom helper to give {{count}} the value of an increasing number based on the number of answers. I'm having trouble getting the right equation and building the helper. I've had several attempts using the values of the parent {{#index}} and nested {{#index}} to no avail.
The purpose of this is to render a form under each question with radio buttons to select each answer. I need individual IDs to be the same on both the ID attribute on the input tag and the for attribute on the label tag. Thx.
NB the other attributes (name, value etc.) have been programmed correctly so I'm giving thm hard values in this example.
{{#questions}}
<div class="question-{{#index}}">
<form>
{{#each answers}}
<div>
<input type="radio" name="radios[0]" id="radios-{{count}}" value="1">
<label for="radios-{{count}}">Answer 1</label>
</div>
{{/each}}
<form>
</div>
{{/questions}}
Desired output in plain text class names:
div.question-0
div
input#radios-0
label[for=radios-0]
div
input#radios-1
label[for=radios-1]
div.question-1
div
input#radios-2
label[for=radios-2]
div
input#radios-3
label[for=radios-3]
div
input#radios-4
label[for=radios-4]
div.question-2
div
input#radios-5
label[for=radios-5]
div
input#radios-6
label[for=radios-6]
Use registerHelper method to add individual incremental counter
<script type="text/template" id="template">
{{#each questions}}
<div class="question-{{#index}}">
{{#each answers}}
<div class="answer-{{count}}"></div>
{{/each}}
</article>
{{/each}}
</script>
<script type="text/javascript">
var inputdata = {
questions: [
{ answers: [2,3,4,5] },
{ answers: [1, 2,3,4,5] }
]
};
var Counter = 1;
Handlebars.registerHelper('count', function() {
return Counter ++;
});
var template = Handlebars.compile($("#template").html());
$("body").html(template(inputdata));
</script>
DEMO
My solution:
{{#questions}}
{{questionIndex #index}}
<div class="question-{{#index}}">
<form>
{{#each answers}}
<div>
<input type="radio" name="radios[{{../questionIndex}}]" id="radios-{{../questionIndex}}{{#index}}" value="{{value}}">
<label for="radios-{{../questionIndex}}{{#index}}">{{answer}}</label>
</div>
{{/each}}
<form>
</div>
{{/questions}}
Handlebars.registerHelper('questionIndex', function(value) {
this.questionIndex = Number(value);
});
{{../questionIndex}}{{#index}} will result in: 00, 01, 10, 11, 20, 21 and so on...

Categories

Resources