Is there a better way to do this in handlebars? - handlebars.js

Im using handlebars to spit out some static pages using a partial like so:
{{> component/card size="small" title="Card Title" }}
Now depending on the "size" of the card required, i need to set some different tailwind classes. Im currently doing it like so, but there must be a better way? Adding the class to the container and writing css is not an option.
{{setVariable "additionalHeadingClass" "text-5 md:text-6 mb-4"}}
{{#ifEquals size "small"}}
{{setVariable "additionalHeadingClass" "text-4 mb-1"}}
{{/ifEquals}}
{{#ifEquals size "large"}}
{{setVariable "additionalHeadingClass" "text-4 sm:text-5 md:text-8 mb-4"}}
{{/ifEquals}}
<h3 class="text-primary font-bold {{#root.additionalHeadingClass}}">{{title}}</h3>
and heres the helper im using:
Handlebars.registerHelper("setVariable", function (varName, varValue, options) {
if (!options.data.root) {
options.data.root = {};
}
options.data.root[varName] = varValue;
});

My opinion is that there is too much code in your template for what it actually does. Despite the intimidating number of lines, we really just want to map a size to a string of class names. I would also advise against the setVariable because I find it harder to think about when we creating a side-effect by manipulating a variable on our context object. I would much prefer a more functional-style helper, where we just give it the size and it returns the class names string.
I would create such a helper using a simple switch:
Handlebars.registerHelper('additionalClasses', function(size) {
switch (size) {
case 'large':
return 'text-4 sm:text-5 md:text-8 mb-4';
case 'small':
return 'text-4 mb-1';
default:
return 'text-5 md:text-6 mb-4';
}
});
And then we may reduce our template to the much simpler:
<h3 class="text-primary font-bold {{additionalClasses size}}">{{title}}</h3>
I have created a fiddle for reference.

Related

How to assign dynamic variables to CSS content in Vue.js?

Apologies if this shows how much of a novice I am, but I'd like to know more about dynamic variables and CSS in Vue. I'd like to create a system where each time a button is pressed, the letters of the button label become further apart.
Inside a component is it possible to use a counter script such as:
<script>
export default {
name: 'Counter',
data() {
return {
count: 3,
}
},
methods: {
intrement() {
this.count += 1;
}
}
}
</script>
And then use the count integer value to change CSS text spacing for example?
So that in the template, I could use:
<template>
<header>
<div>
<button class="header_button" style="letter-spacing: `v-bind(count) + ch`;">MYBUTTON</button>
</div>
</header>
</template>
I appreciate this is a strange and specific example, but if anyone could give me some feedback as to why this doesn't work, as well as suggestions on how I could achieve this I'd be super appreciative.
In that case, you can directly use the following
<button :style="`letter-spacing: ${count}ch;`">
Here is a playground.
PS: :style is a shorthand for v-bind:style as explained here.
v-bind for CSS (mixing script + style) is also a thing.
Here, you're only using script + template combo, so an interpolation is enough.

How to dynamically add classes to NextJS component using class names from CMS?

I am looking for a solution that will allow some styling options in a CMS that can dynamically change the classes of specific components in my NextJS app. My code looks like this:
pages/index.js:
...
import client from "../lib/client";
const Home = ({ headerConfig }) => {
return (
<>
<Header headerConfig={headerConfig} />
...
</>
);
};
export const getServerSideProps = async () => {
const headerConfig = await client.getDocument("headerConfig");
return {
props: { headerConfig },
};
};
export default Home;
components/Header.jsx:
const Header = ({ headerConfig }) => {
return (
<nav className={`relative ... ${headerConfig.bgColour}`}>
...
</nav>
);
}
export default Header
However, the styling does not apply and the background colour remains unchanged although the class does seem to be injected into the class attribute on the browser.
I know my current method is incorrect but I am clueless as to how to fix this. Could someone help point me in the right direction?
I assume that you are using tailwind. If so, you cannot inject classnames into an html element. This is because tailwind only includes classes that are explicitly declared somewhere within your code (it will find classes within any string in your project depending on the configuration). You can get around this problem by adding classes to the safelist array in your tailwind.config.js file. You can also safelist classes with regex to allow all variants of certain utilities.
However, safelisting only works if there are a specific set of classes that could potentially be injected. One option, which will be guaranteed to work but NOT RECOMMENDED, is to add a <link> in your html to the tailwind cdn. However this will include every single tailwind class in your css bundle, making it MUCH larger and your website slower.
Another solution is to use inline styles which are calculated with javascript depending on the classes you need to inject. If you are dealing with only simple parts of tailwind (like padding, margin, or other sizing units), this may be a good approach. For example a class like p-4 would get converted to padding: 1rem in your inline styles.
Depending on the needs of your application, one of these three approaches is probably the way to go. Hope this helps!

Applying css to some part of typescript variable through interpolation in Angular

Let's say I have a variable named data in app.component.ts which is of type :string.
In app.component.html I am showing the value of data to the UI using string interpolation like {{data}}.
Now my question is while displaying the value in a UI, I need to apply some css to some specific letters present in a data variable.
For example:
app.component.ts
data : string = "stack overflow"
app.component.html
<p>{{data}}</p>
How to highlight the background color of the word overflow using css?. And I hear that Pipes can be used to modify the value. But here I am in a need of applying css.
And one more constraint is there, initially the value will be displayed to the browser; the word to be highlighted will be coming from input box.
You could use something among the lines of:
.ts
highlightKeyWord(sentence: string, keyWord: string) {
sentence = sentence.replace(keyWord,
`<span style="background-color: #35a5f8;">${keyWord}</span>`);
return this.sanitizer.bypassSecurityTrustHtml(sentence);
}
.html
<p [innerHTML]="highlightKeyWord('hello world', 'world')"></p>
One solution is use pipe to extract given word into separate <span> elements:
#Pipe({
name: 'letterByLetter'
})
export class LetterByLetter implements PipeTransform {
transform(value: string): string {
return value
.split('')
.map((letter) => {
return `<span>${letter}</span>`;
})
.join('');
}
}
Then in component there is possibility to use the pipe in this way <div [innerHTML]="data | letterByLetter"></div>. Notice i've used innerHtml but you can use DomSanitizer instead - which should be better)
After that you are able to decide how the span element should looks. You can set either the class or style directly.
Good luck!

Calling a Stylus {block} with a variable in an iteration

I'm trying to generate classes with Stylus {block} insertions while on an iteration with that code:
// Blocks
flexbox =
display flex
inline-flexbox =
display inline-flex
// Properties collection
props = {
'flexbox': 'flex',
'inline-flexbox': 'inline-flex'
}
// Generating classes
for kProp, vProp in props
.u-{vProp}
{kProp}
{kProp} is supposed to output {flexbox} and {inline-flexbox} but I guess there's some kind of syntax conflict between calling a Stylus {block} and calling the iteration variable.
So basically with this code, I got no output at all.
I also tried to escape the { } but no luck.
Does anyone know a workaround?
No proper solution but some workarounds I found:
You can replace {blocks} by extending $placeholders but be aware that with Stylus you can't extend inside a Media Query (it just ignore the MQ part)
You can simply replace {blocks} by mixins() (and it works inside Media Queries) which is the one I chose.
So basically now my code looks like this:
// Blocks
flexbox()
display flex
inline-flexbox()
display inline-flex
// Properties collection
props = {
'flexbox': 'flex',
'inline-flexbox': 'inline-flex'
}
// Generating classes
for kProp, vProp in props
.u-{vProp}
{kProp}()

Is it possible to apply dynamic style as string in Angular?

I have a <span> that I want to apply dynamic style to.
Style is stored in a css-like string variable and can be arbitrary e.g.
myStyle = 'color: white; font-weight: bold;'
or
myStyle = 'background-color: red;'
I expected it to work like
<span style="{{myStyle}}">
but it didn't.
I tried different options but none seem to work for me for different reasons:
I can't put styles in a .css file and use class names because style is coming from server in the form of aforementioned string
Using [style.color] etc. doesn't suit me because I don't know what the style can be
Using [ngStyle] doesn't suit me because it expects object like {'color': 'red', 'font-weight': 'bold'} and I only have string
The reason I have a style stored in a string is because I need to apply it in HTML generated on the server where I simply pass that string to a placeholder variable in a velocity template.
I am almost confident that it can't be done the way I want but probably I am overlooking some solution.
All you need is DomSanitizer and bypassSecurityTrustStyle
Component side :
import { DomSanitizer } from '#angular/platform-browser';
constructor(private doms : DomSanitizer) {}
newStyle = 'background-color:red';
safeCss(style) {
return this.doms.bypassSecurityTrustStyle(style);
}
Template side :
<p [style]="safeCss(this.newStyle)">
Start editing to see some magic happen :)
</p>
WORKING DEMO
Angular provides the DomSanitizer service which can convert strings into style objects. I think this is exactly your case.
constructor(private sanitizer: DomSanitizer) {
}
sanitizeStyle() {
return this.sanitizer.bypassSecurityTrustStyle('background-color: red');
}
<span [style]="sanitizeStyle()">
I think I will go the way of converting the incoming css string into a style object and then applying it to <span> using [ngStyle]

Resources