I want to change the classname during an each loop;
so that it will look like this;
<div class="active">
// do somthing
</div>
<div class="static">
//do
</div>
my code looks like this
{{#each pages}}
<div class="active">
//do
</div>
{{/each}}
there is no identifier of the class. So the 1st one will be active, the rest of the items will be static.
You could add your own helper if you want to do this inside the template. Something like this:
Handlebars.registerHelper('each_with_class', function(ary, first, rest, options) {
if(!ary || ary.length == 0)
return options.inverse(this);
var result = [ ];
var context = null;
var cls = { cls: first };
for(var i = 0; i < ary.length; ++i) {
context = _({}).extend(ary[i], cls);
result.push(options.fn(context));
cls.cls = rest;
}
return result.join('');
});
Then your template could say things like this:
{{#each_with_class pages "active" "static"}}
<div class="{{cls}}">
Same stuff you're doing now.
</div>
{{/each_with_class}}
If you don't mind ary[i] getting modified along the way then you can assign directly to ary[i].cls instead of using _.extend to make a copy.
Demo: http://jsfiddle.net/ambiguous/ZMSQh/1/
as far as I know there are no indices provided by {{#each}} that could be checked..
a workaround would be to store this classes in your pages items and then use something like
{{#each pages}}
<div class="{{this.classname}}">
//do
</div>
{{/each}}
Related
$scope.mainArray = [];
$scope.subArray = [];
var myObject = {}
myObject.task = "write";
myObject.execute = true;
for (var i = 0; i < $scope.mainArray.length; i++) {
$scope.subArray[i] = myObject;
$scope.mainArray[i] = $scope.subArray;
}
How can i show the elements inside my subArray using angular js? I'm using the directive ng-repeat to show my mainArray like: ng-repeat="elements in mainArray"
As per your JS code each element inside your mainArray is basically a subArray. In other words, variable elements from your ng-repeat denotes a subArray. You can iterate over it as shown below and use its value to print content in UI
<div ng-repeat="subArray in mainArray">
<div ng-repeat="item in mainArray">
... do some stuff with item here
</div>
</div>
It would be something like this:
<div ng-repeat="subArray in mainArray">
<div ng-repeat="el in subArray">{{el}}</div>
</div>
Working fiddle
It seems like you have the logic set to make the main array properly
I have a set of items and I want to make a row element around every 4 items, I found this helper here
https://funkjedi.com/technology/412-every-nth-item-in-handlebars/
Handlebars.registerHelper('grouped_each', function(every, context, options) {
var out = "", subcontext = [], i;
if (context && context.length > 0) {
for (i = 0; i < context.length; i++) {
if (i > 0 && i % every === 0) {
out += options.fn(subcontext);
subcontext = [];
}
subcontext.push(context[i]);
}
out += options.fn(subcontext);
}
return out;
});
Which accompanied with the each helper I can do this
{{#grouped_each 4 data}}
<div class="message-row">
{{#each this}}
<div class="message">
[Content here...]
</div>
{{/each}}
</div>
{{/grouped_each}}
Which works well, every 4 items I get a new row element, but the last row will always be one element short, because in the last elements place I want to play a generic "Read more" type of item.
Ideally I would just like something like this
{{#grouped_each 4 data}}
<div class="message-row">
{{#each this}}
<div class="message">
[Content here...]
</div>
{{#if $last}}
<div class="message">
[Content for last item here...]
</div>
{{/if}}
{{/each}}
</div>
{{/grouped_each}}
Where the helper can just make that isLast variable true when it's on the final item, and thus render the final read more item after the final item has been rendered. I'm having trouble understanding how I can pass a variable to the helper that tells me that.
I've seen this work before but I can't seem to tweak the snippets I've seen to work for my scenario.
I think what you want to do, if indeed you want to do it this way, is to append a "last: true" key/value to your subcontext that is output by your helper, instead of trying to pass something into the helper, so that the template gets access to it. This may require re-shaping the data structure of your subcontext a bit. But then you would be able to check
{{#if this.last}}
<somestuff></somestuff>
{{/if}}
Here's a quick shot at how you might do that using your own code from above:
Handlebars.registerHelper('grouped_each', function(every, context, options) {
var out = "", subcontext = {}, i;
if (context && context.length > 0) {
for (i = 0; i < context.length; i++) {
if (i > 0 && i % every === 0) {
out += options.fn(subcontext);
subcontext.list = [];
}
if (i === context.length - 1){
subcontext.last = true;
}
subcontext.list.push(context[i]);
}
out += options.fn(subcontext);
}
return out;
});
then in your template do:
{{#grouped_each 4 data}}
<div class="message-row">
{{#each this.list}}
<div class="message">
[Content here...]
</div>
{{#if this.last}}
<div class="message">
[Content for last item here...]
</div>
{{/if}}
{{/each}}
</div>
{{/grouped_each}}
I am creating an edit screen where I want people to delete items from a list. The list is displayed normally, until the "controller" object goes into edit mode. Then the user can delete items. Items should be flagged for deletion and displayed as such, then when the user saves the edit, they are deleted and the server notified.
I actually have this all working, but the only way I could do it was using literal conditions in the bindings, which looks ugly and I don't really like. Is there a better way of doing it?
Working Fiddle: http://jsfiddle.net/L1e7zwyv/
Markup:
<div id="test">
<a data-bind="visible: IsViewMode, click: edit">Edit</a>
<a data-bind="visible: IsEditMode, click: cancel">Cancel</a>
<hr/>
<ul data-bind="foreach: Items">
<li data-bind="css: CssClass">
<span data-bind="visible: $parent.IsViewMode() || $data._Deleting(), text: Value"></span>
<!-- ko if: $parent.IsEditMode() && !$data._Deleting() -->
<input type="text" data-bind="value: Value"/>
<a data-bind="click: $parent.deleteItem">Del</a>
<!-- /ko -->
</li>
</ul>
</div>
Code:
function ItemModel(val)
{
var _this = this;
this.Value = ko.observable(val);
this._Deleting = ko.observable();
this.CssClass = ko.computed(
function()
{
return _this._Deleting() ? 'deleting' : '';
}
);
}
function ManagerModel()
{
var _this = this;
this.Items = ko.observableArray([
new ItemModel('Hell'),
new ItemModel('Broke'),
new ItemModel('Luce')
]);
this.IsEditMode = ko.observable();
this.IsViewMode = ko.computed(function() { return !_this.IsEditMode(); });
this.edit = function(model, e)
{
this.IsEditMode(true);
};
this.cancel = function(model, e)
{
for(var i = 0; i < _this.Items().length; i++)
_this.Items()[i]._Deleting(false);
this.IsEditMode(false);
};
this.deleteItem = function(model, e)
{
model._Deleting(true);
};
}
ko.applyBindings(new ManagerModel(), document.getElementById('test'));
you could:
wrap another span around to separate the bindings but this would be less efficient.
use both a visible: and if: binding on the same element to achieve the same functionality,
write a function on the itemModel isVisible() accepting the parent as an argument making your binding visible: $data.isVisible($parent).
Afterthought: If this comes up in multiple places you could write a helper function to combine visibility bindings
// reprisent variables from models
var v1 = false;
var v2 = false;
var v3 = false;
// Helper functions defined in main script body - globally accessible
function VisibilityFromAny() {
var result = false;
for(var i = 0; i < arguments.length; i++ ) result |= arguments[i];
return Boolean(result);
}
function VisibilityFromAll() {
var result = true;
for(var i = 0; i < arguments.length; i++ ) result &= arguments[i];
return Boolean(result);
}
// represent bindings
alert(VisibilityFromAny(v1, v2, v3));
alert(VisibilityFromAll(v1, v2, v3));
The third option is the most popular technique with MVVM aficionados like yourself for combining variables in a single binding from what I've seen, it makes sense and keeps all the logic away from the view markup in the view models.
Personally I like the syntax you have at present, (even though I count myself amongst the MVVM aficionado gang as well) this clearly shows in the view markup that the visibility of that element is bound to 2 items rather then hiding these details in a function.
I try to think of view models as a model for my view, not just a place where logic resides. When possible I also try to move complex logic back the view model and use descriptive names for my variables so the code is more readable.
I would suggest adding this to your view model -
var isViewable = ko.computed(function () { return IsViewMode() || _Deleting(); });
var isEditable = ko.computed(function() { return IsEditMode() && !_Deleting(); });
And in your view -
<li data-bind="css: CssClass">
<span data-bind="visible: isViewable, text: Value"></span>
<!-- ko if: isEditable -->
<input type="text" data-bind="value: Value"/>
<a data-bind="click: $parent.deleteItem">Del</a>
<!-- /ko -->
</li>
This cleans the bindings up and allows you to more easily adjust the logic without having to do many sanity checks in your view and view model both. Also I personally name variables that return a boolean such as this as isWhatever to help be more descriptive.
The benefit is that as your view and view model grow larger you can keep the DOM clean of clutter and also your view model becomes testable.
Here is a 'code complete' version of your fiddle with this added -
http://jsfiddle.net/L1e7zwyv/3/
I'm writing an app in Angular and have something like this:
$scope.items = [
{'name':'someName',
'title': 'someTitle',
'filter': function(item){
Filters.setTableTitle(this.title); //cannot get title
...
}
},
{'name':'someName',
'title': 'someTitle',
'filter': function(item){
Filters.setTableTitle(this.title);
...
}
}
];
An array of objects. Part of each object is a function, and inside the function I would like to call a function that grabs the title of that object itself in order to pass it into a greater scope for the rest of the app.
However, I can't grab the title for each object.
How would I access the title in order to use it here?
Thanks.
Update
Here is my HTML that uses (something very similar to) the code above. I'm using the code to create buttons.
<p ng-repeat="link in items">
<block class="button" href="{{link.URL}}" title="{{link.title}}">
<a class="hrefLink" href="{{link.URL}}" ng-click="Filters.setFilter(link.filter, link.title)">
{{link.name}}
</a>
</block>
</p>
The function held within the filter part of each object returns information that is then passed into Filters.setFilter() which updates the DOM.
Filters.setFilter()
service.setFilter = function(filter, title){
service.searchTerm = '';
$spMenu.hide(); //close nav if open
service.selectedFilter = filter;
service.setTableTitle(title); //this does the job
};
I've rearranged the ways these functions work, and now simply pass in the title to the different functions. This gets the job done for what I want, but still could never solve the initial question at hand--how would I access part of an object from inside the object?
I don't know Angular wery well so there could definitely be a much better soultion.
If you call the function yourself, just write it like this:
$scope.items[x].filter(item);
If it's called somewhere else and this doesn't contain the right object, you can enumerate all objects and bind the function to it:
for (var i = 0; i < $scope.items.length; ++i) {
var item = $scope.items[i];
item.filter = item.filter.bind(item);
}
Edit:
You have two choices. First you can just bind the function in HTML:
<p ng-repeat="link in items">
<block class="button" href="{{link.URL}}" title="{{link.title}}">
<a class="hrefLink" href="{{link.URL}}" ng-click="Filters.setFilter(link.filter.bind(link))">
{{link.name}}
</a>
</block>
</p>
Or you can edit the function to do it for you:
<p ng-repeat="link in items">
<block class="button" href="{{link.URL}}" title="{{link.title}}">
<a class="hrefLink" href="{{link.URL}}" ng-click="Filters.setFilter(link)">
{{link.name}}
</a>
</block>
</p>
service.setFilter = function(link){
service.searchTerm = '';
$spMenu.hide(); //close nav if open
service.selectedFilter = link.filter.bind(link);
};
I've got a handlebar template that loads a partial for a sub-element.
I would need to access a variable from the parent context in the calling template, from within the partial. .. doesn't seem to resolve to anything inside the partial.
Simplified code goes like this:
the template
{{#each items}}
{{> item-template}}
{{/each}}
the partial
value is {{value}}
(obviously the real code is more complicated but it's the same principle, within the partial .. appears to be undefined.)
To show it's undefined, I've used a very simple helper whatis like this:
Handlebars.registerHelper('whatis', function(param) {
console.log(param);
});
and updated the above code to this:
updated template
{{#each items}}
{{whatis ..}} <-- Console shows the correct parent context
{{> item-template}}
{{/each}}
updated partial
{{whatis ..}} <-- Console shows "undefined"
value is {{value}}
Is there a way to go around that issue? Am I missing something?
EDIT: There's an open issue relating to this question on handlebars' github project
Just in case anyone stumbles across this question. This functionality exists now in Handlebars.
Do this:
{{#each items}}
{{! Will pass the current item in items to your partial }}
{{> item-template this}}
{{/each}}
Working fiddle (inspired by handlebars pull request #385 by AndrewHenderson)
http://jsfiddle.net/QV9em/4/
Handlebars.registerHelper('include', function(options) {
var context = {},
mergeContext = function(obj) {
for(var k in obj)context[k]=obj[k];
};
mergeContext(this);
mergeContext(options.hash);
return options.fn(context);
});
Here's how you'd setup the parent template:
{{#each items}}
{{#include parent=..}}
{{> item-template}}
{{/include}}
{{/each}}
And the partial:
value is {{parent}}
As of 2.0.0 partials now supports passing in values.
{{#each items}}
{{> item-template some_parent_var=../some_parent_var}}
{{/each}}
Took me awhile to find this, hope it's useful for someone else too!
The easiest way to pass the parent context to the partial is to do the loop inside the partial. This way the parent context is passed by default and when you do the loop inside the partial the {{../variable}} convention can access the parent context.
example fiddle here.
The Data
{
color: "#000"
items: [
{ title: "title one" },
{ title: "title two" },
]
}
The Template
<div class="mainTemplate">
Parent Color: {{color}}
{{> partial}}
</div>
The Partial
<div>
{{#each items}}
<div style="color:{{../color}}">
{{title}}
</div>
{{/each}}
</div>
You can use some of the proposed solutions on the comments from the link to github:
https://github.com/wycats/handlebars.js/issues/182#issuecomment-4206666
https://github.com/wycats/handlebars.js/issues/182#issuecomment-4445747
They create helpers to pass the info to the partial.
I created an each Helper function that includes the parent key/values within the subcontext under the key parentContext.
http://jsfiddle.net/AndrewHenderson/kQZpu/1/
Note: Underscore is a dependency.
Handlebars.registerHelper('eachIncludeParent', function ( context, options ) {
var fn = options.fn,
inverse = options.inverse,
ret = "",
_context = [];
$.each(context, function (index, object) {
var _object = $.extend({}, object);
_context.push(_object);
});
if ( _context && _context.length > 0 ) {
for ( var i = 0, j = _context.length; i < j; i++ ) {
_context[i]["parentContext"] = options.hash.parent;
ret = ret + fn(_context[i]);
}
} else {
ret = inverse(this);
}
return ret;
});
To be used as follows:
{{#eachIncludeParent context parent=this}}
{{> yourPartial}}
{{/eachIncludeParent}}
Access parent context values in your partial using {{parentContext.value}}
I needed dynamic form attributes for something like this...
{{#each model.questions}}
<h3>{{text}}</h3>
{{#each answers}}
{{formbuilder ../type id ../id text}}
{{/each}}
{{/each}}
and a helper like so...
Handlebars.registerHelper('formbuilder', function(type, id, qnum, text, options)
{
var q_type = options.contexts[0][type],
a_id = options.contexts[1].id,
q_number = options.contexts[0][qnum],
a_text = options.contexts[1].text;
return new Handlebars.SafeString(
'<input type=' + q_type + ' id=' + a_id + ' name=' + q_number + '>' + a_text + '</input><br/>'
);
});
Which produces...
<input type="checkbox" id="1" name="surveyQ0">First question</input>
My model is a big blob of arrays and objects mixed together. What's noteworthy is that using '../' like so '../type', passes in the parent model as the context, and without it, such as with 'id', it passes in the current model as the context.
To get specifically the parent of the partial (where you may be several partials deep) then follow the other answers like SeanWM.
If you know that the parent is the main template then you can use #root which resolves to the top-most context no matter how deep you are.
e.g. {{#root.rootObject.rootProperty}}
It is a pity that ../../.. does not go up past a partial.