mako template recursion: AttributeError: 'Undefined' object has no attribute 'id' - recursion

I've made a template for representing a tree structure. Each node of the tree has an id, a name, a list of children (tree_children) and an expanded property.
I arranged a few nodes in a tree structure and then called the following function with the root node:
def print_tree_info(oCat, iOffset=0):
"""
just for testing purposes. print to console
"""
sOffset = ' '*iOffset
if oCat.expanded:
sButton = '-'
else:
if oCat.tree_children:
sButton = '+'
else:
sButton = '.'
print("{0}{1}{2}".format(sOffset,sButton,oCat.name))
if oCat.expanded:
for oChild in oCat.tree_children:
print_tree_info(oChild,iOffset+1)
it printed
-ROOT_NODE
+major1
.base2
which is great.
Now, passing the same node structure into the render function of a mako template (along with the mako template itself) I get the attribute error.
Here's how I render the template:
template = Template(..........)
html = template.render(category=root_node, item_template=template)
Here's the template
%if category is UNDEFINED:
ERROR
%elif category:
<div class="tree_item" category_id="${category.id}">
%if category.expanded:
<a class="collapse_tree_item" category_id="${category.id}">-</a>
%elif category.tree_children:
<a class="expand_tree_item" category_id="${oCategory.id}">+</a>
%endif
<a class="select_tree_item">${category.name}</a>
%if category.expanded:
%for oChild in category.tree_children:
${item_template.render(category=oChild,item_template=item_template)}
%endfor
%endif
</div>
%endif

<a class="expand_tree_item" category_id="${oCategory.id}">+</a>
should be
<a class="expand_tree_item" category_id="${category.id}">+</a>
Lesson learned: be consistent in your naming conventions.

Related

Meteor Blaze To Do App checked condition

(Super new to meteor)
I've gone through the meteor todo app tutorial and on the checking off and deleting tasks part It says that
If you try checking off some tasks after adding all of the above code, you will see that checked off tasks have a line through them. This is enabled by the following snippet:
<li class="{{#if checked}}checked{{/if}}">
With this code, if the checked property of a task is true, the checked
class is added to our list item. Using this class, we can make
checked-off tasks look different in our CSS.
Is there any way to have this apply to a different tag, and not the list? so basically have it say
With this code, if the checked property of a task is true, the updatedstyle
class is added to our img tag. Using this class, we can make
checked-off tasks make images look different in our CSS.
You can put the {{#if}} anywhere you want in your template, not just in the list item. If I understand your specific question:
<li>Some item
<img class="{{#if checked}}updatedstyle{{/if}}" src="/myImg.png">
</li>
Per #blueren, the parameter to the {{#if}} is evaluated as a truthy or falsy value. It can be a key in the current data context (i.e. this.checked) or can be returned by a helper.
If you want a class reactively binded to html tag in the template, you can do it like that:
Template code:
<template name="MyTemplate">
<img src="my/img/path.jpg" class={{checked.img}}/>
<ul>
<li class={{checked.li}}>My List Entry</li>
</ul>
</template>
Helper Code:
Template.MyTemplate.helpers({
checked() {
let liClass = '';
let imgClass = '';
//your checked condition
if(document.getElementById('myCheckBox').checked) {
let liClass = 'checked';
let imgClass = 'updatedstyle';
}
return {
li: liClass,
img: imgClass
};
}
});
That way you can bind multiple classes with different names to the checked input value.

How can I add a unique class and assign unique data to copies of the same template using Meteor, Blaze, Spacebars?

I'm trying to create a dynamic slider that can be reused with different images.
<div id="sly_container" class="sly">
<ul>
{{#each propertyImages}}
<li><img src="{{ImageURL}}"></li>
{{/each}}
</ul>
<button id="gallery_btn-prev"><img class="gallery_arrow"/>prev</button>
<span id="middleSpan"></span>
<button id="gallery_btn-next"><img class="gallery_arrow"/>next</button>
</div>
I populate propertyImages with an httpRequest (I make several of these):
(function(o){
HTTP.call( 'GET', 'https://api.rentcafe.com/rentcafeapi.aspx?requestType=images&type=propertyImages&companyCode=c00000084939&propertyCode='+v+'', {},
function( error, response ) {
if ( error ) {
console.log( error );
} else {
var content = JSON.parse(response["content"]);
obj[p] = content;
if( o == l){
CommunityImages.insert(obj)
Session.set('imagesLoaded',true);
}
}
console.log(v+ ' ' + p + ' ' + o + ' images delivered')
})
}(o++))
and then use this helper:
Template.sly.helpers({
propertyImages: function(){
if(Session.get('property') && CommunityImages.find().fetch()[0]){
return CommunityImages.find().fetch()[0][Session.get('property')]
}
})
After it renders I run some more logic on it to create the slider from the images. It works well when there is one slider per view, since it is dependent on the Session.set('property', 'whatever') but I want to have many on the same page each populated with different images. I can add keys and values to the image objects, so I suppose maybe I can do this with a Spacebars conditional? In the end I'd like to have something like this
<div id="summit-sly">{{> sly}}</div>
<div id="hillcroft-sly">{{> sly}}</div>
<div id="merrit_station-sly">{{> sly}}</div>
with each slider containing it's respective images, OR I'm now seeing that maybe partials could work somehow:
{{>sly summit}}
{{>sly hillcroft}}
{{>sly merrit_station}}
Each slider will basically need it's own class name, so that the logic that runs on render will target each one specifically and not all of them.
Indeed, you can use partials in Blaze spacebars with either:
A single argument that acts as the data context of the called template (as exemplified in Meteor Guide > View > User Interfaces > UI components > Smart components).
Named arguments (as shown in Meteor Guide > View > Blaze > Spacebars > Template inclusion), which are actually assembled into a data object (see below).
{{> subComponent arg1="value-of-arg1" arg2=helperThatReturnsValueOfArg2}}
Another tutorial: http://meteorcapture.com/spacebars/#template-inclusion-ex-2
Reference: Meteor API Docs > Packages > spacebars > Inclusion and Block Arguments
Inclusion tags ({{> foo}}) and block tags ({{#foo}}) take a single data argument, or no argument. Any other form of arguments will be interpreted as an object specification or a nested helper:
Object specification: If there are only keyword arguments, as in {{#with x=1 y=2}} or {{> prettyBox color=red}}, the keyword arguments will be assembled into a data object with properties named after the keywords.
Nested Helper: If there is a positional argument followed by other (positional or keyword arguments), the first argument is called on the others using the normal helper argument calling convention.
Then you retrieve the data context through the templateInstance.data property:
In an onCreated, onRendered or onDestroyed callback, the template instance is directly available in this.
In a helper or in the HTML part of the template, the data context is directly available in this (no need to look for its data child property). In a helper, you can also access the template through Template.instance().
In an event handler, the template instance is passed as the 2nd argument of the listener.

How to add additional classes to django form field

I've just recently learned about:
Form.error_css_class
Form.required_css_class
Docs: https://docs.djangoproject.com/en/dev/ref/forms/api/#django.forms.Form.error_css_class
So by defining 'error_css_class' and 'required_css_class' in forms
class MyForm(forms.Form):
error_css_class = 'error'
required_css_class = 'required'
name = forms.CharField(...)
I can do:
<div class="field-wrapper {{ form.name.css_classes }}">
...
</div>
This will output:
<div class="field-wrapper required">
...
</div>
However I want to add additional classes to the field, e.g I would like to add 'text name' css class for the "name" field. And reading the docs, I think its possible.
https://docs.djangoproject.com/en/dev/ref/forms/api/#django.forms.BoundField.css_classes
After reading the above I tried to do
self.fields['name'].css_classes('name text')
That doesn't work. I get
'CharField' object has no attribute 'css_classes'
I also tried
name = forms.CharField(css_classes='name text')
TypeError
__init__() got an unexpected keyword argument 'css_classes'
I know I can add extra attr to field widget
self.fields['name'].widget.attrs['class'] = 'name text'
But I want to add css classes to field wrapper.
I could write a custom templatetag... to check field name/type and return appropriate css classes... but if there is something builtin .. I would love to keep my templates clean :-).
Also hardcoding css classes per field is not an option .. as the form fields are dynamic.
Any help will be appreciated.
I figured out how to do it using a custom BoundField
from django.forms import forms
class CustomBoundField(forms.BoundField):
def css_classes(self, extra_classes=None):
# logic for determining css_classes has been omitted
extra_classes = ['name', 'text']
return super(CustomBoundField, self).css_classes(extra_classes=extra_classes)
In my forms, I overide getitem
def __getitem__(self, name):
try:
field = self.fields[name]
except KeyError:
raise KeyError('Key %r not found in Form' % name)
return CustomBoundField(self, field, name)

Meteor - query collection inside template

given the following simple template:
<ul>
{{#each customers}}
<li>
{{this.customerName}}
<span class="department">{{this.departmentId}}</span>
</li>
{{/each}}
</ul>
I want to replace departmentId with the department name. But that will require a query to the department collection like:
departments.find({_id: this.departmentId})
which doesnt seem to work inside a template.
How can this be done?
Create a template helper:
Template.yourTemplateNameHere.helpers({
getDepartmentNameFromId: function () {
// 'departments' is a Meteor collection
// 'this' inherits from the same 'this' in your template
var department = departments.findOne(this.departmentId);
if (department != null && department.name != null)
return department.name;
else
return ""; // Or "Unknown Department" or whatever you want
}
});
Then replace your fourth line thus:
<span class="department">{{getDepartmentNameFromId}}</span>
Why do you have to write all this code? Can't you do this within the template? Well, sorry, you can't. That's just how Handlebars is. It's very strict about separating code logic (helpers) from presentation logic (templates).
Note that you also need to make sure your departments collection is populated on the client, at least with the department names you'll need to find.

Handlebars : register helper based on template with 'body content'

I'm trying to implement some kind of 'macro' mechanism in a nodejs application using Handlebars (similar to the #bodyContent system in velocity.)
In my main template, I want to be able to write something like this :
{{#foobar who = user }}
<p>My body content</p>
{{/foobar}}
In a "views/helpers/foobar.html", I would have a file with a template, and some way to reference the "body content"
<p>Hello {{ who }}<p>
{{ bodyContent }}
<p>Bye !</p>
Based on the convention that the templates in "views/helpers" corresponds to a helper called with a single hash parameter, I want to automatically register them ; so I have something like this :
var helpers = "./views/helpers/";
fs.readdirSync(helpers).forEach(function (file) {
var source = fs.readFileSync(helpers + file, "utf8"),
helperName = /(.+)\.html/.exec(file).pop();
var helperTemplate = Handlebars.compile(source);
// We assume all helpers in the folder
// would take a hash as their first param
// We'll provide them with all the required context
Handlebars.registerHelper(helperName, function (options) {
var hash = options.hash || {};
// I want to somehow 'pass' the body Content ;
// The closest I have is 'this', but the markup is
// encoded, so I get a string with '<p>My body content</p>'
hash.bodyContent = options.fn(this);
console.log("Body Content", hash.bodyContent);
// Render the source as an handlebar template
// in the context of a hash
return helperTemplate(hash);
});
});
This does not work, as the tags are escaped, and so bodyContent is a String containing the markup, instead of the markup.
Is there a way I can fix my helper registration, or a built in mechanism in Handlebars to deal with this ?
Thanks
You need to use the {{{triple stashes}}} to unescape the HTML injection. So your template should look like:
<p>Hello {{ who }}<p>
{{{ bodyContent }}}
<p>Bye !</p>
You can read more here

Resources