Svelte conditional element class reported as a syntax error - javascript

I am making an if block per the Svelte Guide for if blocks. It seems simple enough, but Svelte thinks it's a syntax error:
[!] (svelte plugin) ParseError: Unexpected character '#'
public\js\templates\works.html
3: <div class="slides js_slides">
4: {#each works as work, index}
5: <div class="js_slide {#if index === currentIndex }selected{/if} {#if index === 0 }first{/if}">
^
6: <img src="/images/work/screenshots/{ works[index].slug }-0.{ works[index].imageExtension }"/>
7: </div>
Why isn't {#if index === currentIndex } considered valid? How can I do a conditional in Svelte?
Not I could create seperate class= blocks for every possible outcome, but that's a massive amount of work.

Blocks ({#if..., {#each... etc) can't be used inside attributes — they can only define the structure of your markup.
Instead, the convention is to use ternary expressions...
<div class="
js_slide
{index === currentIndex ? 'selected' : ''}
{index === 0 ? 'first' : ''}
">
<img src="/images/work/screenshots/{ works[index].slug }-0.{ works[index].imageExtension }"/>
</div>
...or to use a helper:
<!-- language: lang-html -->
<div class="js_slide {getClass(work, index, currentIndex)}">
<img src="/images/work/screenshots/{ works[index].slug }-0.{ works[index].imageExtension }"/>
</div>
Some people prefer to do things like data-selected={index === currentIndex} and data=first={index === 0}, and style based on [data-selected=true] selectors instead.

Since Svelte 2.13 you can also do
<div class:selected={index === currentIndex}>...</div>
See https://svelte.dev/docs#class_name

As stated in https://svelte.dev/docs#class_name , it require extra JS variable to operate if else inside class
here is a example where a each loop iterate from a to b and when if is true the css will be applied
{#each Array.from(Array(b+1).keys()).slice(a) as i }
<div class="{ i===4 ? "border-l-2 border-blue-500" : ""} p-3 space-y-4">
some sample text
</div>
{/each}
example (1 to 15) :
{#each Array.from(Array(15+1).keys()).slice(1) as i }
<div class="{ i===3 ? "border-l-2 border-blue-500" : ""} p-3 space-y-4">
some sample text
</div>
{/each}

Related

Conditional class style binding in Vue.js

I want to make that styles are added to images only under a certain condition.
Here is code:
<div>
<div v-for="(item, index) in productsList" :key="item.id" class="cart-img">
<img
v-if="item.file"
:src="
`https://server.com/${
index === productsListBigIndex ? item.fileWide : item.file
}`
"
:alt="item.altText || item.text"
draggable="false"
/>
<img
v-else
:alt="item.altText || item.text"
:src="'https://server.com/empty.png'"
/>
<p style="font-size: medium">{{item.text}}</p>
</div>
</div>
The style should depend on the index and productsListBigIndex. For example, if the index is 0 or 2, then this style (in particular the size of the picture) will be added, otherwise it will not.
Thanks for answers!
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
Like this you can conditionally add or remove a classname and styles specified,
v-bind:class="getClass()"
methods:{
getClass(){
//logic to check the condition and return the class name
}
}

VueJS Loop remaining

I have a little problem. I would like to create a loop to display my articles the problem is that. I want it to never be empty basically if I have 0 articles but. I want 10 this one will display 10 skeleton if I have 1 item this will remove 1 skeleton to replace it with my item.
I don't know if I was clear enough so here is a little picture.
Basically I have 6 dark gray rectangles but if I have 1 item I would only have 5 if I have 6 I would have no dark gray rectangle at all.
I use
<transition-group name="fade" tag="div" class="badge__grid">
<template v-for="(n, i) in badges.data">
<div :key="n + i" class="badge__item">
<img
class="badge__item--picture"
:src="
'https://images.habbo.com/c_images/album1584/' +
n.badge_code +
'.gif'
"
:alt="n.badge_code"
/>
<span class="badge__item--code">
{{ n.badge_code }}
</span>
</div>
</template>
<div
v-for="i in badges.remaining"
:key="i"
class="badge__item badge__item--empty"
></div>
</transition-group>
It works but I find it too many and I can't simplify my code
You have to use two loops. Using a computed property you can simplify it a bit further:
<div v-for="a in articles" >
<h4>{{a.title}}</h4>
</div>
<div v-for="r in remaining" >
</div>
and the computed property:
remaining(){
let rem = 10 - this.articles.length;
return(rem > 0?rem:0)
}
Code Sandbox: https://codesandbox.io/s/empty-cherry-vy9fw?file=/src/components/Looper.vue:516-599

Loop variables in Vue.js similar to AngularJS

I my vue.js application I want to know the last repeated element in a v-for directive. I know there is something similar in angularjs with the $last loop variable in its ngRepeat directive:
<div ng-repeat="(key, value) in myObj">
<span ng-if="$last">Shows if it is the last loop element</span>
</div>
Is there any equivalent in vue.js which I am not aware of or do I have to implement some own logic?
It works for me :)
<div v-for="(item, index) in items">
<span v-if="index === (items.length-1)">This is the last loop element</span>
</div>
you can also put the logic in a computed property :
<div v-for="(value, key) in myObj">
<span v-if="key === last">This is the last loop element : {{ value }}</span>
</div>
//...
computed: {
last() {
let keys = Object.keys(this.myObj)
return keys.slice(-1)[0]
}
}
fiddle here
There's no equivalent for this in Vue, no.
You can do this instead, check for index if it is last
<div v-for="(value, key, index) in myObj">
<div v-if="index === Object.keys(myObj).length-1"> my content</div>
</div>
You can try this
<div v-repeat="myObj">
<span v-if="$index === (myObj.length-1)">Shows if it is the last loop element</span>
</div>
Based on #Nisha answer I have been doing this:
<template v-for="(obj, index) in originalData">
<template v-if="index == 0 || (index >= 1 && obj.valueA !== originalData[index - 1].valueA)">
{{ obj.valueA }}
</template>
</template>
I want the loop to always happen the first time, but to conditionally check a value for every loop afterwards. I am using <template> tags to avoid outputting unnecessary markup. In my example originalData is an array of objects.

How to check condition inside map without putting a div

I am trying to put a "col-sm-6" inside a row with checking a condition . But when null value is coming it is returning a blank div with ".col-sm-6" class, I need to hide that class when value is null.
<div className="row">
{attribute.attributeValues.map((list, index) =>
<div className="col-sm-6" >
{list.isSelected === 1 ?
<div className="style">
<span><i className="fa fa-check"></i>{list.tag_value}</span>
</div>:''
}
</div>
)}
</div>
this is giving results like:
like:
<div class="row">
<div class="col-sm-6">Some Value</div>
<div class="col-sm-6"></div>
<div class="col-sm-6"></div>
</div>
But I want:
<div class="col-sm-6">Some Value</div>
<div class="col-sm-6">Some Value</div>
<div class="col-sm-6">Some Value</div>
You can make use of React.Fragment like
<div className="row">
{attribute.attributeValues.map((list, index) =>
<React.Fragment key={index}>
{list.isSelected === 1 ?
<div className="style">
<span><i className="fa fa-check"></i>{list.tag_value}</span>
</div>:''
}
</React.Fragment>
)}
</div>
According to the docs:
A common pattern in React is for a component to return multiple
elements. Fragments let you group a list of children without adding
extra nodes to the DOM.
Second method to remove the wrapping {} in the ternary condition
<div className="row">
{attribute.attributeValues.map((list, index) =>
list.isSelected === 1 ?
<div className="style">
<span><i className="fa fa-check"></i>{list.tag_value}</span>
</div>: null
)}
</div>
Actually, you just need to use filter (MDN documentation) before the map. You can do something like:
attribute.attributeValues.filter(list => list.tag_value).map(...
That way, with filter, you return a new Array in which only items with a "real" list.tag_value value.
In JavaScript, everything without a "Value" is False (JavaScript Booleans). That means that the filter above will filter out any list.tag_value equal to: undefined, null, empty String ("") or Number equal to 0.

failed to get img displayed using forEach in react

I want to render image from my api, I first used size of lodash to check the array is empty or not, if it's empty display the upload button, else just render the images.
When I do
{item.photos.toString()} I'm able to see the url but why below code just doesn't work? no error at all in my console.
<div id="photo_upload">
{size(item.photos) > 1 ?
item.photos.forEach(photo =>{
<img src={photo} />
})
: <div><button>Upload</buton>
</div>}
</div>
You use condition size(item.photos) > 1 only so it won't show if you have only 1 photo.
Another thing is you should add key for each element while doing the loop
item.photos.forEach(photo =>{
<img key={photo} src={photo} />
})
forEach doesn't return anything, so that operand in your conditional will have the value undefined. You wanted map (and return inside it). Also note your closing button tag for the Upload button is missing a t (fixed below):
<div id="photo_upload">
{size(item.photos) > 1 ?
item.photos.map(photo =>{
return <img src={photo} />;
})
: <div><button>Upload</button>
</div>}
</div>
You can also use the concise arrow form:
<div id="photo_upload">
{size(item.photos) > 1 ?
item.photos.map(photo => <img src={photo} />)
: <div><button>Upload</button>
</div>}
</div>
Adding in the points raised by Đào Minh Hạt in his answer (> 0 rather than > 1, and key):
<div id="photo_upload">
{size(item.photos) > 0 ?
item.photos.map(photo => <img key={photo} src={photo} />)
: <div><button>Upload</button>
</div>}
</div>

Categories