How to pass an svg in a ternary operator? - javascript

I have a very expansive custom input and one of the customizations is autocomplete functionality.
In the interest of not sharing too much, I will just share the bit of the code that is for the suggestion drop down on the autocomplete.
I have a suggestions value and a searchHistory value that is based on the cached search history of the user. To differentiate the two I just want to have a clock or some sort of svg in there. I'm able to render an emoji, but that doesn't fit my design theme so I want to use an clock svg from the library I'm using.
How can I pass that SVG in this ternary operator? I've seen people using '<img :src"../something"/>', but that syntax doesn't seem to work for me either.
Any ideas on how/if I can make this happen?
Cheers!
CAutocompleteList.vue
<template>
<ul>
<li
v-for="suggestion in filteredSuggestions"
:key="suggestion"
#click="$emit('selectSuggestion', suggestion)"
>
{{ suggestion }} {{ searchHistory.includes(suggestion) ? '⏱' : '' }}
</li>
</ul>
<!-- <IconPowerSupplyOne theme="outline" size="100%" fill="#4771FA" /> -->
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue'
export default defineComponent({
props: {
filteredSuggestions: { type: Array as PropType<string[]>, required: true },
searchHistory: { type: Array as PropType<string[]>, required: true },
},
emits: ['selectSuggestion'],
setup() {
return {}
},
})
</script>

A ternary shouldn't be used here because HTML elements can't be used in text interpolation.
v-if directive should be used instead:
<svg v-if="searchHistory.includes(suggestion)">
...

Related

Checking for multiple route paths in vue v-bind

I am currently adding a class to a li element based on the route.path using the following code;
<li v-bind:class="{ 'current': $route.path == '/services' }"><nuxt-link to="/services">Services</nuxt-link>
I want to check for multiple paths but i am unsure if that is possible.
Any help would be great.
It's possible to check for mulitple paths by using an array of possible values, and then Array.prototype.includes to check for existence:
<li v-bind:class="{ 'current': ['/services', '/contact', '/about'].includes($route.path) }">
To improve template readability, consider moving that to a computed prop:
<template>
<li v-bind:class="myClasses">
...
</template>
<script>
export default {
computed: {
myClasses() {
return {
const possiblePaths = ['/services', '/contact', '/about']
current: possiblePaths.includes(this.$route.path)
}
}
}
}
</script>

Interpolation in a Vue component that creates HTML

I am building a web app using Vue 2.6.x. In the prototype that was created we have a line like this repeated many times:
<label class="title">On Time</label><span class="score float-right">Score: {{ score.ap_ontime }}
The only part of this whole line that changes is the data within the {{ }}. I would like to turn this into a component that I can call many times. For example:
<MyLabel title="On Time" score="score.ap_ontime" />
...so that I don't have to type this score interpolation over and over in a long HTML file. I know how pass props to the component and add text to the template of the component:
<template>
...
<label class="title">{{title}}</label>
...
</template>
..but what I can't figure out is how to take a string (e.g.score.ap_ontime) and have the template inject something that will render that value from a score plain old JavaScript object that exists in the application and is imported into the template. The original code I show at the top of this post renders fine and reacts to changes in the JavaScript object I just can't figure out how to do this in a component that creates HTML and Vue template syntax based on props.
Your new component should look something like this:
<template>
<label class="title">{{ caption }}</label>
<span class="score float-right">Score: {{ onTime }}</span>
</template>
<script>
export default
{
name: 'MyNewComponent',
props:
{
caption:
{
type: String,
default: ''
},
onTime:
{
type: [String, Number],
default: 0
}
}
}
</script>
Then you can call your component in this way:
<my-new-component :caption="Big title" :on-time="score.ap_ontime" />

How do I use the addMore event in my custom slot components with Vue Formulate?

I am using Vue Formulate and would like to customise the 'add more' button for group repeatable fields. I'm using a custom slot component which is working fine, however I can't figure out what the click event I need to use is so that when my button is clicked it actually adds another field. The same applies to a custom remove button component. I couldn't see anywhere in the docs of how to set this. So far I have this:
<template>
<a :for="context.id" #click="context.addMore()">
{{ context.addLabel }}
</a>
</template>
<script>
export default {
props: {
context: {
type: Object,
required: true
},
}
}
</script>
context.addMore() does not work
You have to add addMore to props, it's not inside the context object.
<template>
<a :for="context.id" #click="addMore">
{{ context.addLabel }}
</a>
</template>
<script>
export default {
props: {
context: {
type: Object,
required: true
},
addMore: {
type: Function
},
}
}
</script>

Linking a text field in a child component to a parent component's props in VueJS

I have a child component sending data via an event to a parent component in VueJS. From the parent component, I am routing the data (or trying to route the data...) to a sibling of the child and create new components with the data sent from the child.
I use a dictionary to group the data for various reasons, then push the dictionary into an array. A v-for loop loops thru the array and populates the previously mentioned new components with data found in that array. I probably don't need to do it this way, but that's how I'm doing it. I am open to alternatives.
Anyway, it doesn't work great. So far I'm only able to get one of the three strings I need to show up where I want it to. I'll explain more after I post the code.
Already tried:
A dozen different versions of the code, including creating a simple v-for in a list to do the job, and various versions with/without a dictionary or array.
In my research for the problem I've gone through the VueJS docs, Googled a few things, and found nothing.
In App.vue (I tried to remove all the irrelevant stuff):
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<TweetDeck v-on:messageFromTweetDeck="msgReceived($event)"/>
<!-- <ul>
<li v-for="(tweet, index) in tweets" :key="index">{{ tweet }}</li>
</ul>-->
<TwitterMsg v-for="(tweet, index) in tweets" :key="index"
:name="tweet.name" :handle="tweet.handle" tsp=3 :msg="tweet.tweet" />
<TwitterMsg name="aaa" handle='aaa'
tsp=50 msg="hey this is a message on twitter"/>
<input type="text" v-model="placeholderText"/>
</div>
</template>
<script>
import TwitterMsg from './components/TwitterMsg.vue'
import TweetDeck from './components/TweetDeck.vue'
export default {
name: 'app',
components: {
TwitterMsg,
TweetDeck
},
data: function() {
return {
tweets: [],
message: "",
placeholderText: ""
}
},
methods: {
msgReceived(theTweet, name, handle) {
this.tweets.push({tweet: theTweet, name: name, handle: handle})
}
}
}
</script>
And in TweetDeck.vue:
<template>
<div>
<input type='text' v-model="yourName">
<input type='text' v-model="yourHandle">
<input type='text' v-model="yourTweet"/>
<button type='button' #click="sendTweet()">Tweet</button>
</div>
</template>
<script>
export default {
name: "TweetDeck",
data: function() {
return {
yourName: "Your name here",
yourHandle: "Your twitter handle",
yourTweet: "What's going on?"
}
},
methods: {
sendTweet() {
this.$emit('messageFromTweetDeck', this.yourTweet, this.yourName, this.yourHandle);
}
}
}
</script>
You can also see the mostly unimportant TwitterMsg.vue here (I am trying to copy Twitter for learning purposes:
<template>
<div>
<h4>{{ name }}</h4>
<span>#{{ handle }}</span>
<span> {{ tsp }}</span> <!-- Time Since Posting = tsp -->
<span>{{ msg }}</span>
<img src='../assets/twit_reply.png'/><span>1</span>
<img src="../assets/twit_retweet.png"/><span>2</span>
<img src="../assets/twit_fave.png"/><span>3</span>
</div>
</template>
<script>
export default {
name: "TwitterMsg",
props: {
name: String,
handle: String,
tsp: String,
msg: String
}
}
</script>
<style>
img {
width: 30px;
height: 30px;
}
</style>
Expected result:
The code populates a new TwitterMsg component with appropriate name, handle and message data each time I click the "Tweet" button.
Actual results:
My code fails to help the name and handle strings make it from the input text box in TweetDeck.vue all the way to their home in TwitterMsg.vue.
I will say that this.yourTweet in TweetDeck.vue DOES manage to make it all the way to its destination, which is good -- though it makes me wonder why the other two pieces of data didn't follow suite.
Totally lost. Also just in my first month of VueJS so it's pretty good that I can even make one string appear where I want it to. \o/
First, you need to remove the $event parameter
<TweetDeck v-on:messageFromTweetDeck="msgReceived"/>
Second, you can optimize the data format passed to the parent component:
sendTweet() {
this.$emit("messageFromTweetDeck",
{ tweet: this.yourTweet, name: this.yourName, handle: this.yourHandle }
);
}
And then modify your msgReceived method:
msgReceived(childData) {
this.tweets.push(childData);
}
Link: codesandbox
Hope to help you:)

How to pass an Object to a prop value in VueJS

I am turning some of my components into re-usable components. I am running into some issues here that I can't figure out. Coming from a React environment, my thoughts are getting jammed up. Basically, I need to be able to make a prop more versatile than just a Boolean or String, or any primitive value. I need to be able to pass "content" to it that could change from page to page depending on what is used for
For example, I have this stateless component:
<template>
<div class="cts-split-grid cts-alt-header">
<div>{{title}}</div>
<div v-if="rightSide" class="cts-split-grid">
<span class="uk-text-small">Pod or station is open</span>
<span class="legend-color"></span>
</div>
</div>
</template>
<script>
export default {
name: "page-alt-header",
props: {
title: {
type: String
},
rightSide: {
type: Boolean
}
},
data() {
return {
value: ""
};
}
};
</script>
That I am using this way
<AltHeader :title="'POD' + currentPodId" rightSide />
As you can see, in the title I am passing an object currentPodId bounded to the component. That was easy since that object only produces a data value.
I want to remove this(below) from the re-usable component and be able to add it in the component using the AltHeader as a rightSide Prop:
<span class="uk-text-small">Pod or station is open</span>
<span class="legend-color"></span>
The reason why is because this component's right side can be anything from an Icon component to a button, to a small block of HTML, etc.
How can I do this? How can I set up rightSide prop to accept anything I pass to it at the component level depending on how I need to use it?
Thanks
You should use slots
<template>
<div class="cts-split-grid cts-alt-header">
<div>{{title}}</div>
<div v-if="rightSide" class="cts-split-grid">
<slot></slot>
</div>
</div>
</template>
and add right Side content as follows :
<AltHeader :title="'POD' + currentPodId" rightSide >
<!-- side right content here -->
</AltHeader>

Categories

Resources