How to use multiple condition passing in props and running block of code - vue.js

I am trying to use if elseif else condition in my method which I want to trigger depending on what I dropdown I select. I don't know why is it not working or if is it possible? Here is an example to demonstrate
<HelloWorld :msg="msg"/>Select number
<HelloWorld :msg="hi"/>
validate(val) {
if (val === "msg") {
alert("msg");
} else if (val === "hi") {
alert("hi");
}

You are passing a variable on the props, not string, change :msg="msg" to :msg="'msg'" or msg="msg", and :msg="hi" to :msg="'hi'" or msg="hi"
on HelloWorld component, you are supposed to to check if (this.msg === "msg") instead of if (val === "msg")
Demo: https://codesandbox.io/s/wispy-water-dxmuz

Related

Why cant I add two methods to a class using v-bind

Using vue.js 2 I need to dynamically add classes to a <tr> element.
What works (single method call)
:tbody-tr-class="urgentEnquiryMixin_rowColour"
What doesn't work (two method calls, one a mixin)
Adding an additional method to the v-bind
:tbody-tr-class="urgentEnquiryMixin_rowColour applyUnreadClass"
What i have tried
:tbody-tr-class="[applyUnreadClass, urgentEnquiryMixin_rowColour]"
:tbody-tr-class="{applyUnreadClass(), urgentEnquiryMixin_rowColour}"
Additional code for info
applyUnreadClass(item, type) {
if (!item || type !== 'row') {
return '';
}
if (item.read === false) {
return 'unread-email-row';
}
return '';
}
urgentEnquiryMixin_rowColour(item, type) {
if (!item || type !== 'row') { return ''; }
if (item.isUrgent === true) { return 'tr-urgent'; }
return '';
}
<b-table id="assigned-enquiries-table" class="index-grid" headVariant="light" hover
:items="enquiriesData" :fields="columns" :current-page="page" :per-page="rowsPerPage"
show-empty :tbody-tr-class="applyUnreadClass, urgentEnquiryMixin_rowColour"
#filtered="onFiltered" :busy="isBusy"
>
Errors
'v-bind' directives require an attribute value
Parsing error: Unexpected token ','.
Parsing error: Unexpected token ','.eslint-plugin-vue
You can use class directly for optional classes.
You can even use class as well to have classes that always work:
<div
class="static"
:class="{ active: isActive, 'text-danger': hasError }"
></div>
So create props for these classes (which are booleans) and pass them like that to you.
However if you want to add them dynamically. Create ONE string that you paste inside class. This will contain all classes in that string. (eg. 'Class1 Class2 Class3')
https://v2.vuejs.org/v2/guide/class-and-style.html

Event only firing as inline JS statement

I have the following code in a Nuxtjs app in SSR mode.
<Component
:is="author.linkUrl ? 'a' : 'div'"
v-bind="!author.linkUrl && { href: author.linkUrl, target: '_blank' }"
#click="author.linkUrl ? handleAnalytics() : null"
>
The click event in case it's an a tag, will only fire if it's written as handleAnalytics(), but handleAnalytics will not work.
Don't get me wrong the code is working, but I don't understand why.
With classical event binding (#click="handleAnalytics), Vue will auto bind it for you because it sees it's a function.
But when provided a ternary condition, it's not auto binded but wrapped into a anonymous function instead. So you have to call it with parenthesis otherwise you're just returning the function without executing it.
To be clearer, you can write it this way: #click="() => author.linkUrl ? handleAnalytics() : null"
Note: when having a dynamic tag component, I'd suggest to use the render function instead.
This is an advanced technique, but this way you won't bind things to an element that doesn't need it (without having the kind of hack to return null).
Example:
export default {
props: {
author: { type: Object, required: true },
},
render (h: CreateElement) {
const renderLink = () => {
return h('a', {
attrs: {
href: author.linkUrl,
target: '_blank',
},
on: {
click: this.handleAnalytics
},
)
}
const renderDiv = () => {
return h('div')
}
return this.author.linkUrl ? renderLink() : renderDiv()
}
}
Documention: Vue2, Vue3
In javascript functions are a reference to an object. Just like in any other language you need to store this reference in memory.
Here are a few examples that might help you understand on why its not working:
function handleAnalytics() { return 'bar' };
const resultFromFunction = handleAnalytics();
const referenceFn = handleAnalytics;
resultFromFunction will have bar as it's value, while referenceFn will have the reference to the function handleAnalytics allowing you to do things like:
if (someCondition) {
referenceFn();
}
A more practical example:
function callEuropeanUnionServers() { ... }
function callAmericanServers() { ... }
// Where would the user like for his data to be stored
const callAPI = user.preferesDataIn === 'europe'
? callEuropeanUnionServers
: callEuropeanUnionServers;
// do some logic
// ...
// In this state you won't care which servers the data is stored.
// You will only care that you need to make a request to store the user data.
callAPI();
In your example what happens is that you are doing:
#click="author.linkUrl ? handleAnalytics() : null"
What happens in pseudo code is:
Check the author has a linkUrl
If yes, then EXECUTE handleAnalytics first and then the result of it pass to handler #click
If not, simply pass null
Why it works when you use handleAnalytics and not handleAnalytics()?
Check the author has a linkUrl
If yes, then pass the REFERENCE handleAnalytics to handler #click
If not, simply pass null
Summary
When using handleAnalytics you are passing a reference to #click. When using handleAnalytics() you are passing the result returned from handleAnalytics to #click handler.

Vue + Vue-Paginate: Array will not refresh once empty

I am using vue-paginate in my app and I've noticed that once my array is empty, refreshing its value to an array with contents does not display.
<paginate
name="recipes"
:list="recipes"
:per="16"
class="p-0"
>
<transition-group name="zoom">
<div v-for="recipe in paginated('recipes')" :key="recipe.id">
<recipe class=""
:recipe="recipe"
:ref="recipe.id"
></recipe>
</div>
</transition-group>
</paginate>
This is how things get displayed, and my recipe array changes depending on a search. If I type in "b" into my search, results for banana, and bbq would show. If I typed "ba" the result for bbq is removed, and once I backspace the search to "b" it would re-appear as expected.
If I type "bx" every result is removed and when I backspace the search to "b", no results re-appear.
Any idea why this might happen?
UPDATE
When I inspect the component in chrome I see:
currentPage:-1
pageItemsCount:"-15-0 of 222"
Even though the list prop is:
list:Array[222]
Paginate needs a key in order to know when to re-render after the collection it's looking at reaches a length of zero. If you add a key to the paginate element, things should function as expected.
<paginate
name="recipes"
:list="recipes"
:per="16"
class="p-0"
:key="recipes ? recipes.length : 0" // You need some key that will update when the filtered result updates
>
See "Filtering the paginated list" is not working on vue-paginate node for a slightly more in depth answer.
I found a hacky workaround that fixed it for my app. First, I added a ref to my <paginate></paginate> component ref="paginator". Then I created a computed property:
emptyArray () {
return store.state.recipes.length == 0
}
then I created a watcher that looks for a change from length == 0 to length != 0:
watch: {
emptyArray: function(newVal, oldVal) {
if ( newVal === false && oldVal === true ) {
setTimeout(() => {
if (this.$refs.paginator) {
this.$refs.paginator.goToPage(page)
}
}, 100)
}
}
}
The timeout was necessary otherwise the component thought there was no page 1.
Using :key in the element has certain bugs. It will not work properly if you have multiple search on the table. In that case input will lose focus by typing single character. Here is the better alternative:
computed:{
searchFilter() {
if(this.search){
//Your Search condition
}
}
},
watch:{
searchFilter(newVal,oldVal){
if ( newVal.length !==0 && oldVal.length ===0 ) {
setTimeout(() => {
if (this.$refs.paginator) {
this.$refs.paginator[0].goToPage(1)
}
}, 50)
}
}
},

Vuejs append data-attribute conditionally

I try to add conditionally a data attribute value to my vue list on loop and I try the following
<ul data-parent="{{model.parent_id !== null ? model.parent_id : 0}}"></ul>
but in this case the list do not renders anymore, if dump out outside html tag {{model.parent_id !== null ? model.parent_id : 0}} than I see the correct output
Use : before that and I would create a computed property like this.
computed: {
parentId() {
if (this.model.parent_id !== null)
return this.model.parent_id
return 0;
}
}
<ul :data-parent="parentId"></ul>
The right Syntax
<ul :data-parent="{{(model.parent_id !== null) ? model.parent_id : 0}}"></ul>

A complex condition inside v-if

I want to create a complex condition to pass to the v-if directive.
I have tried the following.
<div v-if="(order.order_products[key].statuses[0].id) != 3 || order.status != 3" >
Can I add a complex condition in Vue's v-if? This is not working.
I also tried with && but that wasn't working, either. I haven't found anything in the documentation about this.
Firstly, to answer your question.
Can I add a complex condition in Vue's v-if?
You can pass an arbitrary JavaScript expression to the v-if directive in Vue, including a complex boolean expression which contains operators || or &&.
You can test this on your own. For example, try having the following template.
<div v-if="true && false">I am not visible!</div>
Of course, you might try out something less trivial, too:
<div v-if="1 == 2 || (1 + 2 == 3 && 4 == 4)">I am visible!</div>
Your expression looks good, but based on the provided information it's impossible to deduce what exactly is wrong.
Your problem is something else: maybe the data is not in the format you thought it would be, or maybe your logic has a flaw in it.
Yes, you can set complex condition. I suggest you to use Vue computed fields, so you will have better highlight (through Vue Devtools) of variables which use in v-if expression. I suppose that order is data field, so you can set computed fields like this:
computed: {
orderStatusId: function () {
// Write some checks is variable defined/undefined
return this.order.order_products[key].statuses[0].id
},
orderStatus: function () {
return this.order.status
}
}
and v-if expression should look like this:
<div v-if="orderStatusId !== 3 || orderStatus !== 3" >
In this approach you can review values of variables in your v-if expression.
Yes, you can use any JavaScript expresion inside v-if quotes.
But I recommend you to create a function or computed function and to call it inside your if statement, for better readability.
ex:
computed: {
shouldDisplay: function () {
return this.order.order_products[key].statuses[0].id) !== 3 || this.order.status !== 3;
}
...
}
<div v-if="shouldDisplay"> ... </div>
v-if="(segmento != 4) && (segmento != 2) && (segmento != 8)"
Works like a breeze for me!