Add flags to Alpine.js UI - dynamic class name - javascript

I'm trying to add:
https://github.com/lipis/flag-icon-css
to my app so that when someone clicks on a mapbox country I can show the flag
Is there a way that you would recommend how to do this?
I tried the following but I get TypeError: (void 0) is undefined
<span class="flag-icon" x-bind:class="{ [`flag-icon-${$store.ui.clicked.flag}`]: true }">
<h3 class="pt-3 mb-3 ml-5 text-lg " x-text="$store.ui.clicked.name">Country / City name</h3>
</span>
Spruce.store('ui', {
clicked: {
name: 'Welcome!',
u: 'US',
flag: (this.region_ident ?? this.u).toLowerCase().slice(0, 2),
},
})
edit: inside my on map click function I have this:
Spruce.store('ui').clicked = {
...Spruce.store('ui').clicked,
city: e.features[0].properties,
name,
}
Maybe I could try:
$el.addClass(`flag-icon-${$store.ui.clicked.flag}`)
But I am not sure where to put that.
This works but I think an Alpine.js solution would be more succinct.
document.getElementById('flag-show-bottom').removeAttribute('class')
document
.getElementById('flag-show-bottom')
.classList.add(
'flag-icon',
`flag-icon-${e.features[0].properties.u.toLowerCase().slice(0, 2)}`,
)

I finally figured it out. x-bind: takes an expression. You can use it on anything. it is really versatile. You don't have to use :class={ className: bool } format.
You can just use strings:
<img src="../images/blank.gif"
x-bind:class="`flag ${ $store.ui.clicked.country
? 'flag-' + $store.ui.clicked.country.region_ident.toLowerCase().slice(0, 2)
: $store.ui.clicked.city
? 'flag-' + $store.ui.clicked.city.u.toLowerCase().slice(0, 2)
: '' }`" />
(I switched to using a different flag library but the logic is the same)

Related

Conditionally render a badge for active/inactive User

I am trying to render conditionally a badge, based on the logged condition of a user. If the props coming from the server is true, then the badge is green, else grey.
I have tried various solutions, from basic if/else in the JSX, to classNames but the badge isn't being render.
My Code:
{user.loggedIn ? (
<div
className={classNames('badge badge-pill', {
'badge-success': user.loggedIn,
'badge-danger': !user.loggedIn
})}
/>
I don't see anything wrong with the code. I mean the item should have rendered, with multiple solutions. Any ideas, what I am doing wrong.
<span
className={
user.loggedIn ? 'badge badge-pill badge-success' : 'badge badge-pill badge-muted'
}
/>
I have tried this as well.
I can see the element in the React-Dev-Tools, being render correctly, witht the right prop, but I cannot see it render in the screen.
Your contents will only get rendered if user is loggedIn. Also the div tag must be closed inside the condition { isLoggedIn }
you should try something like this:
{user.loggedIn ? (
<div className={'badge badge-pill badge-success'}>ENTER YOUR CONTENT HERE</div>) : (
<div className={'badge badge-pill badge-danger'}>ENTER YOUR CONTENT HERE</div>
)}
but since the div is self-closing and has no contents, it doesn't display it, so add some content too
The badge for logged out user will never be displayed as you put a rendering condition around all the div with user.loggedInd ? (yourComponent...)
If user.loggedIn is boolean, then you can just write
<div
className={
classNames('badge badge-pill', {
'badge-success': user.loggedIn,
'badge-danger': user.loggedIn
})
}
/>
I think it's better to avoid conditional in jsx directly. You can do something like this:
let attachClass = ['badge', 'badge-pill']
if(user.loggedIn){
attachClass = [...attachClass, 'badge-success'].join(' ');
}else{
attachClass = [...attachClass, 'badge-danger'].join(' ');
}
Than you can just return one div with className attach class:
<div className={attachClass}></div>
The issue look into the user.loggedIn is not defined or return false
const loggedIn = true;
<span className={loggedIn ? 'badge-success' : 'badge-muted'}>
Css Change </span>
Codepen

Conditional V-HTML rendering

Just wondering if there is something I'm missing here:
<span v-html="(shouldParseHTMLBool ? HTMLContent : '')">
{{ contentWithoutParsedHTML }}
</span>
doens't appear to work.
I would like to have this span interpreting HTML only if shouldParseHTMLBool is set to true.
Is it something possible, or should I use a v-if? In this trivial example it's not a big deal, but with my components I'll have to duplicate ~30 lines for each condition.
It's better to make if condition away from template as much as possible.
You should create a computed object instead.
[template]
<span v-html="computedContent"></span>
[script]
...
computed: {
computedContent: function () {
return this.shouldParseHTMLBool ? this.HTMLContent : ''
}
},
...
The v-html directive replaces the innerHTML of the element. In your case, the {{ contentWithoutParsedHTML }} will be replaced with the value of (shouldParseHTMLBool ? HTMLContent : '')
You can do something like
<template>
<span v-html="conditionalParse"></span>
</template>
<script>
methods: {
conditionalParse: function () {
return this.shouldParseHTMLBool ? this.HTMLContent : ''
}
</script>
try this
<span v-if="shouldParseHTMLBool" v-html="HTMLContentForIfCondition" >All</span>
<span v-else v-html="HTMLContentForElseCondition" ></span>
You can use two spans, One for v-if is true and other for v-else

Proper way to express switch statement with Vue data bindings

I have a simple use case for a range input that changes a button's text whenever the range's value changes. I'm using Vue.js to bind data from an object that is simply storing the range value, and spitting it back out to the button (and the counter, for easier debugging).
In the following fiddle, when the range value is greater than 0, the text reads "Buy", or else it reads "Sell".
Fiddle: http://jsfiddle.net/svwa79pe/1/
What I want to do is show three button states depending on whether the range value is positive, negative, or zero. I can use Vue handlebar / mustache syntax to create a ternary expression, but I can't figure out how cover the third state. What I need is closer to a switch statement than ternary, but I can't seem to find an analog to that within Vue's documentation.
Does Vue contain a tool for this kind of logic that I don't know about?
Should I just handle this with a custom method that fires on the range change?
Am I just using the Vue template incorrectly? Is there a better way to do this with attributes or something?
HTML
<div id="app">
<span>
<button disabled="true">{{ item.delta }}</button>
</span>
<input type="range" v-model="item.delta" value="0" v-bind:max="item.stock" v-bind:min="0 - item.loot">
<span class="exchange">
<button>
{{ (item.delta > 0) ? "Buy" : "Sell" }}
</button>
</span>
</div>
JS
var stats = {
item : {
price : 10,
stock : 20,
loot : 5,
delta : 0
}
}
var app = new Vue({
el: '#app',
data: stats
});
Typically you want to remove complex logic from the template. In this case you want a value based on some other data so this is an ideal use case for a computed property.
computed:{
btnText(){
if (this.item.delta > 0) return "Buy"
if (this.item.delta < 0) return "Sell"
return "Nothing"
}
}
Here I'm just using simple if statements, but if you wanted to use a switch you certainly could. Used in your template like this:
<button>
{{ btnText }}
</button>
Here is a working example.
var stats = {
item : {
price : 10,
stock : 20,
loot : 5,
delta : 0
}
}
var app = new Vue({
el: '#app',
data: stats,
computed:{
btnText(){
if (this.item.delta > 0) return "Buy"
if (this.item.delta < 0) return "Sell"
return "Nothing"
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>
<div id="app">
<span>
<button disabled="true">{{ item.delta }}</button>
</span>
<input type="range" v-model="item.delta" value="0" v-bind:max="item.stock" v-bind:min="0 - item.loot">
<span class="exchange">
<button>
{{ btnText }}
</button>
</span>
</div>

JXS if else class doesn't work

{item.status}
<div className="{item.status !== '' ? 'hide' : ''} pull-right">Content</div>
Why above jsx has no effect? My item.status value is string. But in my DOM I can't see hide class been added.
This is because you have wrapped your ternary operator in " so it is treated as string. Try this instead:
{item.status}
<div class={"pull-right "+(item.status !== '' ? 'hide' : '')}>Content</div>
Beside what #rahul-pratap-singh said, consider using nice, tiny package called classnames - link here so you can use it like:
import classes from 'classnames';
// later on
<div className={classes({
"pull-right": true,
"hide": item.status !== '',
})} />
I think it leads to much cleaner code than joining string.
Or at least use es6 string interpolation - link here
// instead of
"pull-right "+(item.status !== '' ? 'hide' : '')
// with string interpolation
`pull-right ${item.status.length ? '' : 'hide'}`;

Have both JSX and normal className together

I have a css style class that needs to be in JSX form as it depends on the state, but I also need to use style class the ordinary way for styling. How do I combine these together?
I've tried
<span className={statusColor} className="uppercase">
Which only takes into account the last class one
<span className={statusColor uppercase}>
Which obviously looks for a variable called uppercase and errors out
<span className="uppercase {statusColor}">
Neither of them work
Below is a dummied-down version of my code. What is the correct way to do this?
const Component = React.createClass({
return {
completed: false
}
},
render () {
let statusColor;
this.state.completed === true ? statusColor = "green" : statusColor = "red";
return (
<div className="status-banner">
<span className="kata-text-status">Status: <span className={statusColor} className="uppercase">{this.state.completed.toString()}</span></span>
</div>
)
}
})
Try this:
<span className={`uppercase ${statusColor}`}>
This will use an ES6 template string (`uppercase ${statusColor}`) and interpolate statusColor with ${...}.
<span className={'uppercase' + ' ' + statusColor}>
You can do it in several ways.
Firstly you can use + to concat more than one string variables and generate dynamic strings
<span className={"uppercase " + statusColor}>
Or you can use npm modules classnames.
The brackets {} translate to normal JS. So you can just use + to concatenate.
<span className={"uppercase " + statusColor}>

Categories

Resources