adding a custom class to the div element - vue.js

i have a custom component where i am passing some data to render some css styles
like
<title :profile="true" :class="true"/>
in my component
i have a div as:
<div class="tabletitle">
i want if my custom class :class is true, i should add a new class called as flexdisplay to it like
<div class="tabletitle flexdisplay">
what i am missing here, i tried passing the value to data as false but it just throwing random errors

Lets assume your parent component is like
<title :profile="true" :showClass="true"/> <!-- modified props name from class to showClass
and in your child component, as you said you have a div like below
<div class="tabletitle">
what you can do is change the above div to the below format
<div :class="['tabletitle', {'flexdisplay': enableClass}]"> <!-- where enableClass will be a computed property
Inside the script tag of your child component, define props and computed properties like below
<script>
export default {
props: {
showClass: {
type: Boolean,
default: false
},
}
computed: { // The reason I am doing with computed is for better reactivity
enableClass() {
return this.showClass;
}
}
}
</script>

You can use class condition syntax
<div :class="{ "active": isActive }"></div>
This will put the active class if the isActive condition is true
Note that you can combine the class and :class attributes to put either class conditionnaly and classic class
<div class="myClass" :class="{ "active": isActive }"></div>
For example in your case if you have the boolean class props your component will look like this :
<template>
<div class="tabletitle" :class={"flexdisplay": isClass}>
...
</div>
</template>
<script>
export default {
props: {
isClass: {
type: Boolean,
default: true
}
}
}
</script>
**Note : ** i've changed the class props by isClass to better understand and do not confuse with the class keywork

Related

How to add class to the parent component when the button is clicked in child in vue

I have a button in my child component and when I click this button I want to add a class in my parent component. I added child component as a slot in parent component.
parent component:
<template>
<div :class="editMode ? 'class-add' : ''">
<slot name="default"></slot>
</div>
</template>
<script>
export default {
props: {
editMode: {
type: Boolean,
required: true,
},
},
};
</script>
child component:
<button #click="addClass">Click Me!!!</button>
addClass() {
this.$emit('edit-abc', true);
},
And here how I am adding the class:
<parent-component :edit-mode="editMode">
<template #default>
<child-component #edit-abc="editAbc($event)" />
</template>
</parent-component>
The problem is as you see, I have several abcs (abcs is an object which includes several abc) to send to child the class only the one which is clicked. So I believe here #edit-abc="editMode = $event", instead of editMode = $event, I need to create a function and filter the one that I want to add the class but my logic is wrong somewhere. Here what I have done as a function.
editAbc(event) {
this.abcs.filter((a) => {
if (a.id) {
this.$nextTick(() => {
return (this.editMode = event);
});
}
});
},
You have to declare the editMode data property to use it in your event handling.
data() {
return {
editMode: false
};
}
If you need to send separate events, then simply use different events.
You intentions with "several abcs" are not really clear. And it looks for me like you have a design flaw.
Please clarify it further.
UPDATE
Here is a stackblitz with the solution.

Vue: All components rerender upon unrelated data property change only if a prop comes from a method that returns an object or array

(Vue 3, options API)
The problem: Components rerender when they shouldn't.
The situation:
Components are called with a prop whose value comes from a method.
The method cannot be replaced with a computed property because we must make operations on the specific item (in a v-for) that will send the value processed for that component.
The method returns an Array. If it returned a primitive such as a String, components wouldn't rerender.
To reproduce: change any parent's data property unrelated to the components (such as showMenu in the example below).
Parent
<template>
<div>
<div id="menu">
<div #click="showMenu = !showMenu">Click Me</div>
<div v-if="showMenu">
Open Console: A change in a property shouldn't rerender child components if they are not within the props. But it does because we call myMethod(chart) within the v-for, and that method returns an array/object.
</div>
</div>
<div v-for="(chart, index) in items" :key="index">
<MyComponent :table="myMethod(chart)" :title="chart.title" />
</div>
</div>
</template>
<script>
import MyComponent from './MyComponent.vue';
export default {
components: {
MyComponent,
},
data: function () {
return {
showMenu: false,
items: [{ value: 1 }, { value: 2 }],
};
},
methods: {
myMethod(item) {
// Remove [brackets] and it doesn't rerender all children
return ['processed' + item.value];
}
}
};
</script>
Child
<template>
<div class="myComponent">
{{ table }}
</div>
</template>
<script>
export default {
props: ['table'],
beforeUpdate() {
console.log('I have been rerendered');
},
};
</script>
<style>
.myComponent {
width: 10em;
height: 4em;
border: solid 2px darkblue;
}
</style>
Here's a Stackblitz that reproduces it https://stackblitz.com/edit/r3gg3v-ocvbkh?file=src/MyComponent.vue
I need components not to rerender. And I don't see why they do.
Thank you!
To avoid this unnecessary rerendering which is the default behavior try to use v-memo directive to rerender the child component unless the items property changes :
<div v-for="(chart, index) in items" :key="index" v-memo="[items]">
<MyComponent :table="myMethod(chart)" :title="chart.title" />
</div>

VUE3 Data, props and v-if wont work together?

I have a component called Filter, the parent sends a prop called showFilters. the value of showFilters is ['plaats']. In the Filter component there is a data object called filters. The value of this Object is.
data: function() {
return {
filters: {
plaats: {
title: 'Plaats',
label: 'Plaats_Naam',
key: 'Plaats_Id',
type: 'default',
active: true,
}
}
}
},
in the html part of the Filters component i do this:
<div v-for="key in showFilters" v-if="filters[key].active" class="col py-8" #mouseover="log(filters[key]);">
<h1>test</h1>
</div>
I get a error when doing this because filters[key] is undefined which is weird because when i remove the v-if the log works with the right data.
What am i missing?
Don't use v-if and v-for in the same element, separate v-for in another element like the virtual one template (not the root template) and in the v-if check also filters[key] :
<template v-for="key in showFilters">
<div v-if="filters[key] && filters[key].active" class="col py-8" #mouseover="log(filters[key]);">
<h1>test</h1>
</div>
</template>

Pass data from blade to vue and keep parent-child in sync?

I know that in Vue parents should update the children through props and children should update their parents through events.
Assume this is my parent component .vue file:
<template>
<div>
<my-child-component :category="category"></my-child-component>
</div>
</template>
<script>
export default {
data: {
return {
category: 'Test'
}
}
}
</script>
When I update the category data in this component, it will also update the category props in my-child-component.
Now, when I want to use Vue in Laravel, I usually use an inline template and pass the value from the blade directly to my components (as for example also suggested at https://stackoverflow.com/a/49299066/2311074).
So the above example my my-parent-component.blade.php could look like this:
#push('scripts')
<script src="/app.js"></script>
#endpush
<my-parent-component inline-template>
<my-child-component :category="{{ $category }}"></my-child-component>
</my-parent-component>
But now my-parent-component is not aware about the data of category. Basically only the child knows the category and there is no communication between parent and child about it.
How can I pass the data from blade without breaking the parent and child communication?
I just had to pass the category to the inline-template component through props like this:
#push('scripts')
<script src="/app.js"></script>
#endpush
<my-parent-component :initcategory="{$category}}" inline-template>
<my-child-component v-model="category"></my-child-component>
</my-parent-component>
In my-parent-component I had to set the props and initialize is using the create method:
export default {
props: {
initcategory: '',
},
data() {
return {
category: '',
};
},
created(){
this.category = this.initcategory;
}
}
Now my my-parent-component is fully aware of the category and it can communicate to the child using props and $emit as usual.
Your reference to this answer is different altogether from what you are looking for!
He's binding the :userId prop of the example component but not the parent component or in simple words: Any template using the example vue can either pass a string prop or bind :userId prop to a string variable. Following is similar:
<example :userId="{{ Auth::user()->id }}"></example>
OR
<example :userId="'some test string'"></example>
So you should rather assign {{ $category }} to a data variable but rather binds to a child component prop which will have no effect on the parent.
In the following snippet you're only binding the string but rather a data key:
<my-child-component :category="{{ $category }}"></my-child-component>
Update
See the following example which will change the h1 title after 3 seconds
// HelloWorld.vue
<template>
<app-name :name="appName" #appNameChanged="appName = $event"></app-name>
</template>
<script>
export default {
props: ['name'],
data() {
return {
appName: null
}
},
mounted() {
// NOTE: since Strings are immutable and thus will assign the value while objects and arrays are copied by reference
// the following is just for the purpose of understanding how binding works
this.appName = this.name;
}
}
</script>
The template which renders the app title or you can say the child component
// AppName.vue
<template>
<h1>{{ name }}</h1>
</template>
<script>
export default {
props: ['name'],
mounted() {
setTimeout(() => {
this.$emit('appNameChanged', 'Change App')
}, 3000);
}
}
</script>
And here's how it is being used in the welcome.blade.php
<div id="app">
<hello-world :name="'Laravel App'"></hello-world>
</div>

Using v-model and refs in a slot in Vue2

I have a component that takes a main <slot> from a form that is generated elsewhere in my application. I'm trying to use v-model on the form inputs but my vue component just spits out a warning about the properties not being defined, when in fact they are.
I admit it's a weird way of doing things, but it seems to be the easiest way for me to do this since my form is being generated by Symfony.
html:
<my-component>
<input ref="start" v-model="start"/>
</my-component>
my component:
<script>
export default {
data() {
start: null
},
mounted() {
console.log(this.$refs) // === {}; expected {"start":{...}}
}
}
</script>
<template>
<div>
<slot/>
... other stuff here
</div>
</template>
console log:
Property or method "start" is not defined on the instance but referenced during render
I cannot use $refs or v-model in the html. Am I doing something wrong? Or is this just not possible.
If you declare v-model="start" in the parent then it belongs to the parent and needs to be declared there. It looks like instead you declare it in the component instead as null.
If you reorder things it should work as you expect:
Parent:
<parent>
<input v-model="start" :start="start"/>
</parent>
<script>
export default {
data() {
start: null // Important to define start here if it's used in this component's html
}
}
</script>
Component:
<template>
<div>
<slot/>
... other stuff here
</div>
</template>
<script>
export default {
props: ['start'], // Receive the prop from the parent
data() {
},
mounted () {
console.log(this.start) // Should echo the value of the prop from the parent
}
}
</script>