V-model on div tag component - javascript

There's an issue when using v-model to div tags. Apparently, div tags doesn't allow v-model and I've decided to create my comment section as a component. I needed to assign this div text area as is because of UI/UX reasons. textarea, input, etc tags, as to my knowledge, these tags are not compatible with contenteditable="true"; I need to expand the height of the input field as a user types in their comments. Below is the vue component that I imported in my parent view.
<!-- CommentSection.vue -->
<template>
<div id="chatId" contenteditable="true" placeholder="Leave a message" class="overflow-hidden block mx-4 text-left p-2.5 w-full text-sm text-gray-900 bg-white rounded-2xl border border-gray-300 focus:ring-blue-500 focus:border-blue-500"/>
</template>
<style>
#chatId[contenteditable="true"]:empty:not(:focus):before {
content: attr(placeholder)
}
</style>
On my view file, I imported it and used v-model into it, just like this.
<!--MainPage.vue-->
<template>
...
...
<CommentSection v-model="comment"/>
<button #click="submitPost()"> Submit </button>
...
...
...
</template>
<script>
import CommentSection from '#/components/CommentSection.vue'
export default{
name: 'MainPage',
data(){
return{
comment: '',
}
},
components: { CommentSection },
methods:{
submitPost(){
console.log(this.comment);
},
},
}
</script>
However, when I check my console, it gives me the value "null" or just nothing. Is there's a way to fix this? Or is it the way I implemented it that causes the problem.
EDIT: Here's the running code in codesandbox.

I solved your problem and the code is as follows. I hope I have helped
By adding # to the div tag, we can see the changes in the content of the tag in the change method. And in that method, use emit$ to share its value with other components
<!-- CommentSection.vue -->
<template>
<div id="chatId" #input="chanage" contenteditable="true" placeholder="Leave a message" class="overflow-hidden block mx-4 text-left p-2.5 w-full text-sm text-gray-900 bg-white rounded-2xl border border-gray-300 focus:ring-blue-500 focus:border-blue-500"/>
</template>
<script>
export default {
methods: {
chanage (e) {
this.$emit('value-div', e.target.textContent)
}
}
}
</script>
<style>
#chatId[contenteditable="true"]:empty:not(:focus):before {
content: attr(placeholder)
}
</style>
And here we have the props created by $emit, whose value we initialize in the comment variable. Actually, it has a function similar to v-model.
<!--MainPage.vue-->
<template>
...
...
<CommentSection #value-div="(value)=>comment = value"/>
<button #click="submitPost()"> Submit </button>
...
...
...
</template>
<script>
import CommentSection from '#/components/CommentSection.vue'
export default{
name: 'MainPage',
data(){
return{
comment: '',
}
},
components: { CommentSection },
methods:{
submitPost(){
console.log(this.comment);
},
},
}
</script>

Related

Vue accessing a child components computed property

So I'm using a third party vue component called 'vue-tree-list' here's the link -> https://github.com/ParadeTo/vue-tree-list
in the component it has a computed property that basically analyzes the tree structure to find the right place for a new leaf/node to be inserted.
In my parent component I did this:
<template>
<div class="py-8 px-5" style="min-height: calc(100vh - (112px + 2.75rem))">
<div class="flex flex-col w-full">
<button class="cursor-pointer relative flex flex-row items-center h-10 focus:outline-none "
>
<span class="text-sm tracking-wide truncate ml-6">Add Node</span>
</button>
</div>
<VueTreeList
#click="onClick"
#change-name="onChangeName"
#delete-node="onDel"
#add-node="onClick"
ref="tree"
:model="data"
default-tree-node-name="New Depot"
default-leaf-node-name="New Driver"
v-bind:default-expanded="false"
>
<template v-slot:leafNameDisplay="slotProps">
<a class="text-orange-primary mr-4">
<span>{{ slotProps.model.name }}</span>
</a>
</template>
<span class="icon" slot="addTreeNodeIcon">📂</span>
<span class="icon" #click.stop="test()" slot="addLeafNodeIcon">+</span>
^INSTEAD OF CALLING THE DEFAULT EVENT 'add-child' WHICH IMMEDIATELY INSERTS A NODE I DIVERTED IT INSTEAD SINCE I WANT THE USER TO INPUT THEIR DATA BEFORE INSERTING INSIDE THE TREE
<span class="icon" slot="editNodeIcon">📃</span>
<span class="icon" slot="delNodeIcon">✂️</span>
<span class="icon" slot="leafNodeIcon">🍃</span>
<span class="icon" slot="treeNodeIcon">📂</span>
</VueTreeList>
<Modal ref="modal" :title="modalTitle" :size="modalSize" :height="modalHeight">
<div v-if="modalContent == 'new'">
<DriverLookUp />
<VehicleLookUp />
</div>
</Modal>
</div>
</template>
<script>
import { VueTreeList, Tree, TreeNode } from 'vue-tree-list'
import { DriverLookUp, VehicleLookUp } from '#/components/forms/depot'
export default { ONLY ADDED THE RELEVANT FUNCTIONS SINCE IT WOULD BE VERY LONG
components: {
VueTreeList, Modal, DriverLookUp, VehicleLookUp
},
test(){
this.$refs.tree.rootNode() <--- the computed method that I want to access
},
}...
The problem with this is that the computed property for some reason throws an error on missing properties which doesn't make sense since it has already been rendered. Is there a way to trigger a child components computed property?
https://github.com/ParadeTo/vue-tree-list/blob/master/src/VueTreeList.vue <--- here's the link of the child component that I'm working with
That component's computed prop walks up its parent tree until it finds a component with a prop named model, containing name of "root". It assumes all parents have this prop, and fails otherwise, leading to the error you observed.
A workaround is to declare that property in your component before reading the computed prop:
export default {
props: {
👇
model: {
type: Object,
default: () => ({ name: 'root' }),
},
},
methods: {
test() {
const rootNode = this.$refs.tree.rootNode
console.log({ rootNode })
},
}
}
demo

Vue prevent enter strange behaviour

I have a Vue3 app that has a form in it. The app also uses the vue router. In the form, I have a textarea. I've added a #submit.prevent to the form, but then I hit enter in the textarea the page redirects to its parent component. When I click the button it behaves correctly. Only when I hit the enter key in the textarea the redirect occurs.
A minimal example looks like this:
<template>
<div class="">
<form #submit.prevent="validate()" class="flex flex-col">
<textarea
class="mt-0 block w-full px-0.5 bg-white text-black border-0 border-b-2 border-primary focus:ring-0 focus:border-other"
rows="2"
>
</textarea>
<button type="submit">Test</button>
</form>
</div>
</template>
<script>
export default {
setup() {
const validate = () => {
console.log(`check if validated`);
};
return {
validate,
};
},
};
</script>

Lozad lazy load with v-for in Vue.js

I'm using lozad.js for lazy loading my images. In my vue.js project I have
made a custom component that's globally registered it looks basically like this:
<template>
<img :alt="alt" :data-src="lazySrc" />
</template>
<script>
import lozad from 'lozad';
export default {
props: {
lazySrc: {
type: String,
default: null,
},
alt: {
type: String,
default: ''
}
},
mounted() {
const observer = lozad(this.$el);
observer.observe();
},
};
</script>
When I try this in a .html file:
#foreach($recommendedFriends as $friend)
<a href="{{ route('profile.show', $friend->slug) }}">
<lozad class="lozad bg-cover bg-gray-200 border-4 border-white w-20 h-20 rounded-full shadow-lg z-50 m-2 zoom"
lazy-src="{{ $friend->thumb_image }}">
</lozad>
</a>
#endforeach
The images are being lazy loaded and it works great! But when I try it in a .vue file:
<div v-for="breed in breeds">
<lozad class="lozad rounded-lg"
:lazy-src="breed.full_overview_image">
</lozad>
</div>
Nothing is being lazy loaded. I don't get an error and the images do show up but they are not lazy loaded.
How can I fix this? Am I missing something?

Toggling Visibility in Vue not working properly

I'm working on a project where I am receiving data from a websocket and then parsing it into an appropriate UI. The problem I'm running into is when I try and toggle the "Components" of a specific "Agent", the show/hide functionality isn't working properly. Right now I have an "AgentsButton" component that lives inside of the "Config" component, when I applied the same logic in the Config (parent) component it worked fine, but for some reason the child component is not doing what I want it to do. Here is the code I have for the child ("AgentsButton") Component.
<template>
<div>
<b-row id="agentRow">
<b-col v-for="agent in agents" md="auto">
<b-button class="agentButton" #click="compVisible = true">{{ agent.name }}</b-button>
<b-container v-if="compVisible" id="componentsDiv">
<h3>Component-Types</h3>
<div v-for="item in agent.componentTypes">
<b-row>
<b-col md="12">
<b-button type="button" class="componentItem" #click="openModal(item)">
{{ item }}
</b-button>
</b-col>
</b-row>
</div>
</b-container>
</b-col>
</b-row>
</div>
</template>
<script>
export default {
name: 'AgentButtons',
components: {},
props: ['agents', 'components'],
data() {
return {
compVisible: false,
};
},
methods: {
clickEvent() {
this.$emit('clicked');
console.log('clickEvent');
},
showComponents() {
this.compVisible = true;
console.log(`compVisible: ${this.compVisible}`);
},
},
};
</script>
Any help on this issue would be greatly appreciated. Thanks!

Vuejs toggle class to the body on button click in a components

I want to toggle a class to the body or to the root element("#app") if I click on the button inside the header component
Header.vue :
<template lang="html">
<header>
<button class="navbar-toggler navbar-toggler align-self-center" type="button" #click="collapedSidebar">
<span class="mdi mdi-menu"></span>
</button>
</header>
</template>
<script>
export default {
name: 'app-header',
data: {
isActive: false
},
methods: {
collapedSidebar() {
}
}
}
</script>
App.vue :
<template lang="html">
<div id="app" :class="{ active: isActive }">
...
</div>
</template>
! Please correct me if I'm in the wrong direction. I'm new in Vuejs :)
You can emit an event inside your header and maybe catch it in the mounted of app component like this:
In your sidebar or other component:
on_some_btn_click: function (){
this.$emit('toggle_root_class');
}
In your app component:
mounted: function(){
var _this = this;
this.$on('toggle_root_class', function(){
_this.active = !_this.active;
});
}
Vue may restrict event from being observed in sibling components. So I use EventBus in my projects to handle sending events easily.
the problem lies in your component scope. You tried to access data in Header.vue in App.vue which is impossible by the structure in showed in your code. Consider moving isActive data to App.vue or use Vuex.
You can use jquery to toggle class for an element which is not inside the Vue template.
You can call a function on click of a button and inside it, you can toggle class in body tag using jquery.
<template lang="html">
<header>
<button class="navbar-toggler navbar-toggler align-self-center" type="button" :class="{ active: isActive }" #click="activeLink">
<span class="mdi mdi-menu"></span>
</button>
</header>
</template>
<script>
export default {
name: 'app-header',
data: {
isActive: false
},
methods: {
activeLink() {
$('body').toggleClass('.class-name');
}
}
}
</script>

Categories

Resources