Passing variables through handlebars blockhelper - handlebars.js

I want to pass and compile params in my custom block helper. I found out that the params are inside a hash-object, but how can I compile them into the partial?
I want the param flyoutClass to be compiled into my partial. Everything works fine but the place where the output of my param should be stays empty...
handlebars helper
module.exports.register = function (Handlebars, context) {
Handlebars.registerHelper('injectHtml', function(name, options) {
console.log(options.hash); //yeah my param
var partial = Handlebars.partials[name];
var template = Handlebars.compile(partial);
//var template = Handlebars.compile(partial)(options.hash); *
var output = template({"body": options.fn(this)});
return new Handlebars.SafeString(output);
//return new Handlebars.SafeString(output(options.hash)); *
//return new Handlebars.SafeString(partial(output)); *
})
};
I have already tried some things, but I always get that warning...
Warning: string is not a function
.hbs file
<div class="flyout {{flyoutClass}}">
<button>flyout-button</button>
<div class="flyout__content">
{{{body}}}
</div>
</div>
call my blockhelper
{{#injectHtml "flyout" flyoutClass='navigation__item'}}
<div class="wrapper">
<h3>Headline</h3>
<p>some copy</p>
<button>CTA</button>
</div
<div class="wrapper">
<h3>Headline</h3>
<p>some copy</p>
<button>CTA</button>
</div>
{{/injectHtml}}
UPTADE
And is it possible to pass from my blockhelper a param to another partial?
var output = template({
"addClass": options.hash.addClass,
"id": options.hash.id,
"body": options.fn(this)
});
I like to extend this partial with "id"
{{#injectHtml "flyout" flyoutClass='navigation__item'id='myUniqueID'}}
and also use it in my partial button
<div class="flyout {{flyoutClass}}">
{{>button btn="icon-text" id="{{id}}"/*[1]*/ icon="arrow-down"label="klick me"}}
<div class="flyout__content" aria-labelledby="{{id}}"/*[2]*/>
{{{body}}}
</div>
</div>
But at [1] the param isn't compiled, [2] works fine.
<div class="flyout navigation__item">
<a href="#" id="{{id}}"/*[1]*/ aria-expanded="false">
<div class="flyout__content" aria-labelledby="myUniqueID"/*[2]*/>
//html content
</div>
</div>

You can't "compile" the flyoutClass parameter into the partial template because you are intending this parameter to be dynamic. This means it is really just another parameter of your template data:
var output = template({
"flyoutClass": options.hash.flyoutClass,
"body": options.fn(this)
});
UPDATE
... but there is no way to add the params dynamicly??[sic]
If you wish to pass along all of the parameters passed to your helper, you could simply extend a template data object within your helper with the options.hash:
var data = Handlebars.Utils.extend({ body: options.fn(this) }, options.hash);
var output = template(data);

Related

Get first character from string in handlebars.js

I am new to handlebars and trying to get the first letter from a looped string i.e
{{#each message}}
<div class='name'>{{this.user}}</div>
<div class='img'><h1 class="abbr">{{this.user.charAt(0)}}</h1></div>
<div class='text'>
<div>
{{this.text}}
</div>
</div>
{{/each}}
I have tried {{this.text[0]}} and {{this.text.charAt(0)}}
You can use Preparation-Script to achieve this : HandleBars Documents
must register helper and use it:
Handlebars.registerHelper('returnOnlyZeroindex', function () {
return this.user[0]
})
and use like this:
<p>{{returnOnlyZeroindex}}</p>

Meteor Blaze.renderWithData how to InsertAfter is it possible?

I'm having a problem to insert a template after and not before a node. For example:
//Html looks like this
<div class="questions">
<div class="question"></div>
<div class="question"></div>
<div class="question"></div>
</div>
<template name="question">
<div class="question"></div>
</div>
<template name="questionExtraInfo">
<div class="extra"></div>
</template>
I'm trying to get the following:
<div class="questions">
<div class="question"></div>
<div class="extra"></div>
<div class="question"></div>
<div class="question"></div>
</div>
Calling blaze render inside question event
Template.question.events({
'click .more-details': function () {
var instance = Template.instance();
Blaze.renderWithData(Template.questionExtraInfo, {}, document.querySelector('.questions'), instance.find('.question')));
});
I can only figure out how render it before or inside how about after?
<div class="extra"></div>
<div class="question"></div>
<div class="question"><div class="extra"></div></div>
I think a better approach would be to take advantage of reactivity:
Change your questions template to:
<template name="question">
<div class="question"></div>
{{# if shouldIncludeExtra }}
{{> questionExtraInfo }}
{{/if}}
</template>
The above template should be inside an each loop.
Then in your js something like:
Template.question.helpers({
'shouldIncludeExtra': function() {
// replace 'n' with the actual index. I think `this.index` is
// provided within #each blocks, or you can use the new `#each` helper.
var index = n;
return Session.get('shouldIncludeExtra' + index);
}
});
Then, in your click event, you set a session var based on the index to true:
Template.questions.events({
'click .question': function(e, tpl) {
var question = e.currentTarget;
// You can probably come up with something better here..
var index = $(question).parent().find('> .question').index(question);
Session.set('shouldIncludeExtra' + index, true);
}
});
Because of reactivity, you would see the inserts right away when you fire the click event.
I realize this doesn't really answer the headline of your question, but it should get you the desired outcome.

angular-file-upload: additional properties/options to each file in a multi-file upload?

I'm using nervgh's angular-file-upload, https://github.com/nervgh/angular-file-upload/wiki/Module-API.
Is there a way to use the angular-file-upload and allow additional properties to each file when doing a multi-file upload?
I'm using their image sample to start out with: http://nervgh.github.io/pages/angular-file-upload/examples/image-preview/
Trying to add a boolean to each file that the user can set and then I use that on the server side when it's picked up.
You can use formData property shown in Properties section to send to server whatever you need.
formData {Array}: Data to be sent along with the files.
If you're using PHP in server side, I think this post can help you out.
The question is rather old, but as the documentation didn't really help me much, I would like to note down my solution here:
This is how my html looks like (look for "options"):
<div ng-controller="UploadCtrl2" nv-file-drop="" uploader="uploader" filters="customFilter">
<div class="progress progress-xs margin-top-5 margin-bottom-20">
<div class="progress-bar" role="progressbar" ng-style="{ 'width': uploader.progress + '%' }"></div>
</div>
<div class="row">
<div class="col-md-6">
<div ng-show="uploader.isHTML5">
<div class="well my-drop-zone" nv-file-drop="" options="{formData:[{folder:'attachments'}, {recordid:0}]}" uploader="uploader">
Dateien hierher ziehen.
</div>
</div>
</div>
<div class="col-md-6">
<span class="btn btn-primary btn-o btn-file margin-bottom-15"> Dateien auswählen
<input type="file" nv-file-select="" options="{formData:[{folder:'attachments'}, {recordid:0}]}" uploader="uploader" multiple />
</span>
</div>
</div>
</div>
And this is my controller (look for "fileItemOptions"):
app.controller('UploadCtrl2', ['$rootScope', '$scope', 'FileUploader', 'Store',
function ($rootScope, $scope, FileUploader, Store) {
var fileItemOptions = {};
var uploader = $scope.uploader = new FileUploader({
url: $rootScope.app.api.url + '/?c=uploads&a=set&authToken=' + encodeURIComponent(Store.get('X-Xsrf-Token')),
});
// FILTERS
uploader.filters.push({
name: 'customFilter',
fn: function (item/*{File|FileLikeObject}*/, options) {
if(options) fileItemOptions = options;
return this.queue.length < 10;
}
});
uploader.removeAfterUpload = true;
// CALLBACKS
uploader.onAfterAddingFile = function (fileItem, options) {
//console.info('onAfterAddingFile', fileItem);
if(fileItemOptions.formData) {
fileItem.formData = fileItemOptions.formData;
}
};
uploader.onAfterAddingAll = function (addedFileItems) {
setTimeout(function () {
console.log(uploader);
uploader.uploadAll();
}, 500);
};
uploader.onCompleteAll = function () {
$scope.$parent.run.uploadComplete();
fileItemOptions = {}; // cleanup
};
}]);
Whenever a file is added, the custom filter stores the option object in a global variable. The callback "onAfterAddingFile" will read that variable and it to the fileItem object. Quite hacky, but this was the only way I got it running.

Cant get the image to show in Umbraco7 with razor

I have used the media picker as data type for the type, for which the user is going to choose what image they want as the deal image.
But for some reason i can't get the razor syntax to show the image. If I make an If statement to check if the page contains an image then it won't. I think this is a problem that occurs because i have misunderstood something.
My current razor statement:
<img src="#Umbraco.TypedMedia(Model.Content.GetPropertyValue("deal1image")).Url" />
The above code won't show anything.
Hope any of you can guide me to what i do wrong and evt. stuff I'm missing.
This is how my current home.cshtml looks like:
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
#{
Layout = "Master.cshtml";
}
<div class="container">
<div class="col-md-4">
<!--Image here-->
<img src="#Umbraco.Media(CurrentPage.deal1image).Url" />
<div class="thumbnail thumbnailcustom thumbnailbg1">
<h3>#Umbraco.Field("dealtitle1")</h3>
<p>#Umbraco.Field("dealdescription1")</p>
</div>
</div>
<div class="col-md-4">
<!--Image here-->
<div class="thumbnail thumbnailcustom thumbnailbg2">
<h3>#Umbraco.Field("dealtitle2")</h3>
<p>#Umbraco.Field("dealdescription2")</p>
</div>
</div>
<div class="col-md-4">
<!--Image here-->
<div class="thumbnail thumbnailcustom thumbnailbg3">
<h3>#Umbraco.Field("dealtitle3")</h3>
<p>#Umbraco.Field("dealdescription3")</p>
</div>
</div>
</div>
You need to use Umbraco.Media to get the media. So like this
<img src="#Umbraco.Media(Model.Content.GetPropertyValue("deal1image").ToString()).Url" />
Or
<img src="#Umbraco.Media(CurrentPage.deal1image).Url" />
An example of using Umbraco.Media:
var myPage = CurrentPage.AncestorsOrSelf().Where("DocumentTypeAlias == #0", "yourPageAlias").First();
Umbraco.Media(myPage.myImage.ToString()).Url
Link on OUR Umbraco offers two solutions:
Typed:
#if (Model.Content.HasValue("caseStudyImages"))
{
var caseStudyImagesList = Model.Content.GetPropertyValue<string>("caseStudyImages").Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse);
var caseStudyImagesCollection = Umbraco.TypedMedia(caseStudyImagesList).Where(x => x != null);
foreach (var caseStudyImage in caseStudyImagesCollection)
{
<img src="#caseStudyImage.Url" style="width:300px;height:300px" />
}
}
Dynamic:
#if (CurrentPage.HasValue("caseStudyImages"))
{
var caseStudyImagesList = CurrentPage.CaseStudyImages.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
var caseStudyImagesCollection = Umbraco.Media(caseStudyImagesList);
foreach (var caseStudyImage in caseStudyImagesCollection)
{
<img src="#caseStudyImage.Url" style="width:300px;height:300px" />
}
}
Also, double check your media picker data type alias. Typos are rather common in this part.
This may be a bit clunky compared to other answers but this is what I currently have.
var imageId = Model.Content.GetPropertyValue<int>("eventPoster"); // gets node id
var evId = evpId.Id; // gets image id
var evMd = Umbraco.Media(evId); // I believe this turns the id into a string
var evUrl = evMd.Url; // gets the url of the string

Template helper crashing app

My app is crashing my browser after implementing this columnWidth helper method. All I'm trying to do is rotate between col-md-7 and col-md-5 (Bootstrap classes) so that no two consecutive posts are the same width.
columnWidth: function() {
if (Session.get('columnWidth') === 'col-md-7') {
Session.set('columnWidth', 'col-md-5');
} else {
Session.set('columnWidth', 'col-md-7');
}
return Session.get('columnWidth');
}
The post template:
{{#each this}}
<div class="{{columnWidth}}">
<img src="{{image}}" height="350" width="{{imageWidth}}" alt="">
<div class="content">
<h2>{{title}}</h2>
<p>{{content}}</p>
<span class="dateAuthored">{{date}}</span>
</div>
</div>
{{/each}}
this refers to:
data: function() {
return Articles.find();
}
Any ideas why this is happening? I'm not receiving any errors. The browser tab just becomes unresponsive. Thanks.
You are constantly setting the same reactive variable so for example with the first div when the helper is called it will set it to col-md-7, then when it is called for the 2nd row you are changing the same variable to col-md-5 which is problematic for 2 reasons:
a) the template will redraw the first column and so they will both be col-md-5
b) the same helpers will get called again. I believe your browser crashes because you have created an infinite loop. Try console logging something inside your columnWidth helper and see how many times it gets called.
To achieve what you want you need to get the index of the {{#each }} loop and then have the column class dependent on whether it's odd or even. Unfortunately getting the index in meteor handlebars is a little tricky.
Try:
{{#each articles}}
<div class="{{columnWidth index}}">
<img src="{{image}}" height="350" width="{{imageWidth}}" alt="">
<div class="content">
<h2>{{title}}</h2>
<p>{{content}}</p>
<span class="dateAuthored">{{date}}</span>
</div>
</div>
{{/each}}
Then the following helpers:
articles: function() {
//'this' in this context should work.
//if not just replace with Articles.find().map(...
var articles = this.map(function(article, index) {
var i = _.extend(article, {index: index});
return i;
});
return articles;
},
columnWidth: function(index) {
if (index % 2 === 0)
return "col-md-5";
else
return "col-md-7"
}

Resources