Some time ago I had to plunge into Meteor. Despite the fact that I do not like javascript, coffeescript is even more, and node.js infuriates me, Meteor very quickly gained my recognition and love. I did not work with it until version 1.0+, as far as I know, there were a lot of horrors there, but now, for me personally, this is a very convenient tool for small interactive projects. Therefore, I really want to share something that can facilitate the use of this tool to other people.
My experience with Meteor is not so big to take and start something difficult to put on the shelves. However, I ran into some things, smacked my head about them and found some solutions. And in this article I want to share my observations / tips on working with jade in Meteor. Unfortunately, with him everything is not as cool as with a clean jade.
I use the mquandalle: jade package in my projects, and it has a number of unpleasant features. Custom template helpers do not work exactly as expected. Standard syntax implies that we simply take and call the helper with a regular string if it is used by itself. Or we frame it in # {} if it is used in a string with something. Here is an example (all coffeescript code):
Helpers:
')
Template.Some.helpers( ifHelper: -> return true plainTextHelper: -> return 'Just some value' )
Template:
template(name='Some') if ifHelper .some-class #{someHelper}
Everything works fine, until you try to pass the value to the helper, to get the value of the string. And this is required, and quite often. If you write # {someHelper someval}, you will get the output '# {someHelper someval}'. Maybe someone will decide right away that if the Jade option does not work, then the standard template syntax should be used. But, for example, I extremely dislike the use of non-native things in the syntax, and therefore I have long and stubbornly searched for how to make the design I need work on the jade syntax. The answer is no way. At least at the time of this writing, I have never found a recipe. I had to sacrifice the principle and write is not very nice {{someHelper someVal}}.
However, if you need to return the text from the html helper (and this happens), then you need to use triple curly braces {{{someHelper someVal}}}. Otherwise, the html-code will be secure, i.e. it will be displayed as '& code;'
If not instead, you should use unless. But there are no normal js-conditions inherent in this template-maker in free life in the meteor edition. Therefore, pretty quickly I had a file in which I add the functionality I need for the template engine, and I drag it between projects. While it looks like this:
UI.registerHelper 'print', (obj)-> console.log('TEMPLATE DEBUG: type:', typeof(obj), 'val:', obj) UI.registerHelper 'equal', (obj1, obj2)-> return obj1 == obj2 UI.registerHelper 'notequal', (obj1, obj2)-> return !(obj1 == obj2) UI.registerHelper 'collectionIsEmpty', (col)-> error_flag = false try col.fetch() catch error obj = col error_flag = true if !error_flag obj = col.fetch() return !obj.length
If someone does not know, then the UI.registerHelper function registers the global helper, which is available in all templates. As for the functions themselves, the first is used to debug template variables. Sometimes it is useful to understand what lies in a variable and why it does not work the way I expect. Just write:
{{print someVar}}
… or:
{{print someHelper}}
... and get the output to the console, where we see the type and value.
The second and third helpers are an adaptation of simple template conditions from Django, my favorite web framework (well, where is it without ads?)
The last helper is used to test the ability to iterate (iterate) the collection. In order not to doubt whether I returned .fetch () from the collection, or whether it was in its pure form, the helper is slightly complicated.
Also, in working with templates, I have long beaten about the each syntax. When we call each, we change the context of the template, and this begins to point not to the scope of the template, but to the scope of the object being iterated.
.some-class #{this.title} each MyBlogPostCollection h1 #{this.title}
In the first case, .title will either be a helper template with that name, or a property from the data object if you are using iron-router. In the second, the .title will be taken from the MyBlogPostCollection collection object. By the way, this can not be written to access the properties, simply # {title}
But it's all pretty simple, and now actually what I was fighting with. If in each we need to refer to an object from the parent context, then it is used ... (two points), and there is no need to write this, its parser will not devour. The result is such here, not always obvious constructions:
{{someHelper ../../..}}
Within the context of the parent context, if required, you can also directly access the properties. i.e .:
{{someHelper ../../title}}
I also pretty well beat my head trying to dynamically set a class for an element. Although, perhaps, it is only up to me that some things get so tight.
.contacts(class="{{#if isPdf}} border-radius-pdf {{/if}}")
Well, if the same is required with a helper, then:
.contacts(class="{{#if isPdf}} {{border-radius-pdf someVal}} {{/if}}")
And yes, a small note, the whole if-else construction looks like this: {{#if <...>}} {{else}} {{/ if}} i.e. else has no # that, in my a glance is not logical.
Pure jade if-else looks simple:
if .some value else .another value
But remember that you can’t add a condition to if.
From the subtleties of using jade, this is probably all that I can now tell. When I sat down to write an article, I wanted to share several tricks not only in working with templates, but also with processing the form data, loading images, but writing about jade, I decided not to throw everything into the general pile. I also solved a nontrivial task the other day - I learned how to make a screenshot of an application with user data. Perhaps some of this will be interesting - leave comments, if there is a demand - I will write.