I want Handlebar {{#if}} logic inside of a Ember.Handlebars.helper - javascript

I am converting someone else's code to Handlebars.js and I'm stuck on converting this tag to its {{#handle-bar}}{{/handle-bar}} counterpart.
The previous coder used an {{#ifCond}} to toggle what 'selected'. This is my component.
{{#dropdown-item }}
{{unbound this.itemName}}
{{/dropdown-item}}
Here is the div i want converted to my component
<div class="dropdownItem" {{bind-attr value=formField_DropdownItemID}}{{#ifCond formField_DropdownItemID value}} selected{{/ifCond}} >
{{unbound this.itemName}}
</div>
My first thought was to just pop the div's logic into the the component, like the next example, but this gave me an error.
{{#dropdown-item bind-attr value=formField_DropdownItemID {{#ifCond formField_DropdownItemID value}} selected{{/ifCond}} }}
{{unbound this.itemName}}
{{/dropdown-item}}
Any suggestions?

You can set those properties to compute. The syntax would be:
{{#dropdown-item selected=computedProperty value=formField_DropdownItemID}}
computedProperty can deal with your conditional logic. The whole idea is to pull that out of handlebars anyways. :)

Related

Angular: Cloning ng-content elements and it's functionality

I'm trying to clone the ng-content items of a component along with any functionality added on the HTML of that content. For example, the markup using the component might look like this:
<custom-component>
<button (click)="doAThing();">A button</button>
</custom-component>
Then I set up my template for custom-component like so:
<ng-template #content>
<ng-conent></ng-content>
</ng-template>
<ng-template *ngTemplateOutlet="content"></ng-template>
<div class="second-area>
<ng-template *ngTemplateOutlet="content"></ng-template>
</div>
My expectation would be that the ng-content would get duplicated into both ngTemplateOutlet areas. What happens is that it pushes to the last outlet and ignores the first. This markup will duplicate normal markup just fine, but ng-content only move to one outlet.
Is this not possible with this technique, am I missing something obvious, or it there another way to clone the contents of ng-content along with any events attached to it?
I found this solution that worked for me. First the HTML, you'll need a directive to wrap the content in so you can reference it. You'll need to use asterisk with directive so it can be duplicated.
<custom-component>
<ng-container *customDirective>
<button (click)="doAThing();">A button</button>
</ng-container>
</custom-component>
The directive doesn't require any extra code. We just need it for a reference.
In your custom-component, you'll need to create a reference to the diretive via #ContentChild like so:
#ContentChild(CustomDirective, { read: TemplateRef }) transcludeTemplate;
Then we use the following for our custom-component HTML avoiding using ng-content tags all together:
<ng-template *ngTemplateOutlet="transcludeTemplate"></ng-template>
<div class="second-area>
<ng-template *ngTemplateOutlet="transcludeTemplate"></ng-template>
</div>
So this isn't really the same as duplicating <ng-content>, but it gives us a similar function. Apparently ng-content not multiplying is intended behavior. So this might be the best way to achieve a similar goal.

VueJs Conditional handlebars

I'm trying to use VueJs conditional rendering using handlebars in vueJs 2.0 as per their documentation but eslint is coming back with and error:
- avoid using JavaScript keyword as property name: "if" in expression {{#if ok}}
- avoid using JavaScript keyword as property name: "if" in expression {{/if}}
VueJs does not seem to be rendering it.
<!-- Handlebars template -->
{{#if ok}}
<h1>Yes</h1>
{{/if}}
If you are trying to use Vue.js syntax, the documentation outlines just a few lines down what's done for Vue.js. You would use the v-if directive.
<h1 v-if="ok">Yes</h1>
If like you mentioned, you're wanting to use Handlebars alongside Vue.js, note that both of them use the same {{ curly braces in templates. You may need to change Vue's use of the curly braces like so...
Vue.config.delimiters = ['<%', '%>'];
Either:
Using v-if to conditionally render
<h1 v-if="isVisible"> Yes </h1>
or using v-show to add a hidden attribute to that element style
<h1 v-show="isVisible"> Yes </h1>
either can be used but be careful with v-if since the element won't be in the DOM if the condition is not met.
I believe that is simply to document that the conditional does not go on a parent tag, but rather it is placed directly on the node that you want to conditionally display.
In other words its simply a comparison not part of Vue.js markup, but rather part of Handlebars.
Vue conditional rendering syntax
<h1 v-if="ok">Yes</h1>
<h1 v-show="ok">Yes</h1>
Details in original docs.
https://v2.vuejs.org/v2/guide/conditional.html#v-if-vs-v-show
Firstly, You should look at the vue documentation .https://v2.vuejs.org/v2/guide/conditional.html#v-if-vs-v-showjs and by the way, you can use "v-if" and "v-show"attributes, in flowing related to
examples.
<h1 v-if='isShow'>Test</h1>
<h1 v-show='isShow'>Test</h1>
For anyone coming here from a search trying to conditionally render inside {{ }} braces, you could always use a computed property:
import { computed } from 'vue';
<script setup>
const submitButtonText = computed(() => {
return props.formObject ? 'Save' : 'Create';
});
</script>
<template>
<form>
<button type="submit">
{{ submitButtonText }}
</button>
</form>
</template>
v-if and v-if-else work perfect for large elements, but this is great for simple one-line conditional text.

How can I use *ngFor current object outside of the ngFor?

The title might seem weird but what im talking about is basically what this link is doing.
Im look for a way to use the current iterated person in a <tr> outside of the *ngFor scope.
In the link he used ng-repeat-start and ng-repeat-end to include multiple tag withing the ng-repeat. How can i achieve the same behavior withing Angular2 using *ngFor?
I had to use the <template> tag, which made the iterated objected accessible within it.
<ng-template let-person ngFor [ngForOf]="people">
<tr>
<td>{{person.name}}</td>
<td>{{person.gender}}</td>
</tr>
<div>
<span>{{person.details}}</span>
</div>
</ng-template>
Hope it'll help somebody
The * in *ngFor (and really any angular directive) is shorthand that tells angular to surround the element in an ng-template, which is just the angular implementation of . You can read about how that happens at https://angular.io/guide/structural-directives, but I'll reproduce the relevant example below.
If your code contains
<div *ngIf='object'>{{object.property}}</div>
as part of the render process, angular transposes that to
<ng-template [ngIf]='object'>
<div>{{object.property}}</div>
</ng-template>

Render HTML Tag Vue.js

I am trying to add a render a template using the push mutation method. I want to push a section component, but instead of the template content I get the raw output of <vsection></vsection'. Can anyone help me render the actual template content and not the raw tags? I included a jsbin below.
http://jsbin.com/wurofatuve/1/edit?html,js,output
You're thinking about this a little oddly. What I think you'd be better off doing is putting a v-for on a <vsection> component.
<vsection v-for="section in sections">
{{ section.content }}
</vsection>
This way, when you push content to sections it'll out put another one. You'll also have to adjust your section component so you can use the content.
<template id="section-template">
<div class="section">
<slot></slot>
</div>
</template>
Here it is working like I think you want: http://jsbin.com/suhadidobe/1/edit?html,js,output

define a variable inside a jsrender template

I need to keep a "colcounter" variable inside the loop that will be used to fill a jsrender template.
Here is my template code
<script id="datascapeTemplate" type="text/x-jsrender">
<div id="dsViewport">
<div class="ds-column" style="width:{{:(name.length*100)}}px;">
<h1 id="datascapeName">{{:name}}</h1>
<div><span id="dsToggle">toggle</span></div>
</div>
{{=colcounter}}
{{for sections}}
<div class="ds-section">
<h3>{{:label}}</h3>
<div class="ds-column" id="start">
{{for items}}
{{* if (colcounter > 4){
colcounter = 1;
}}
</div>
<div class="ds-column" id="start">
{{* } }}
{{*
if ( data.selected || datascape.showInvisible) { }}
<div class="ds-item {{* if (data.featured){ }} nowActive {{*} }} {{* if (data.active){ }} nowActiveRed {{*} }}" background="{{:background}}" bgcolor="#000000" fgcolor="#FFFFFF">
<div class="ds-item-container">
<h4>{{:title}}<br/>{{:time}}</h4>
<p><a item="{{:id}}" href="{{:url}}" class="itemLink">view file {{:colcounter}}</a></p>
</div>
</div>
{{* colcounter++; }}
{{* } }}
{{/for}}
</div>
{{* colcounter=1; }}
</div>
{{/for}}
{{* colcounter=1; }}
</div>
</script>
Unfortunately, it prints, on the very first iteration of the loop "Error: colcounter is not defined.". Afterwards it works.
It seems the way i initialise my colcounter variable is not working but i fail to find the correct way. var colcounter =0 does not work.
UPDATE
jsfiddle: http://jsfiddle.net/ZX6Mk/
colcounter works now. I declared it in the global scope. But I have an issue with datascape.showInvisible. It also triggers the error
Error: Cannot read property 'showInvisible' of undefined.
Thank you for your time,
a.
I took your fiddle and made a few changes. http://jsfiddle.net/johnpapa/bLSkz/
The toggleButton was being referred to in jQuery without the #. So I added that.List item, otherwise the click was not being captured.
Your fiddle did not reference jQuery nor JsRender, though you were using both, so I added them. (I assume you never ran the fiddle)
There was no datascape.showInvisible property, so I created one.
I passed showInvisible to the inner for loop using a parameter, so it could be accessed in its context.
{{for sections ~showIt=showInvisible}}
{{if (editorspick_amount > 0 || ~showIt)}}
The template you were trying to render did not exist, so I changed the rendering code to use the script tag you created. This also sets the allowCode=true, which is required to safely turn on the allowCode feature.
$.templates("myTmpl", {markup: "#datascapeTemplate", allowCode: true });
$('#toggleButton').click(function(){
if(!rendered){
rendered = true;
$("#datascape").html(
$.render.myTmpl( datascape.json )
).show();
}
});
I changed one place where you used {{* }} to instead use an {{if}} block since there was no need to use allow code.
This allowed all of the code to run and the template to render, though I admittedly did not follow all of what you were trying to do.
Hope this helps.
One suggestion ... the allowCode feature makes for really ugly templates and hard to maintain and read. I highly recommend replacing it with helper functions (or other constructs). For example, you used allowCode to create the styling for some elements. You could have used a custom tag for this instead, and moved the logic to javascript and simplified your template. The colcounter could be moved to a helper function. It's just much more readable to move the logic to javascript, and keep the template/html clean. Just my 2 cents :)

Categories

Resources