<DataTable
class="text-slate-800 "
style="width: 91%"
ref="table"
:options="{
pageLength: 1000,
columnDefs: [
{
targets: -1,
data: 'Edit',
defaultContent: `
<button
class='bg-blue-500 text-white pl-3 pr-4 py-2 rounded-lg flex items-center justify-center'
>
<PlusIcon class='w-5 h-5 mr-1' #click='editItem' />
Edit
</button>
`,
},
],
}"
:columns="columns"
:data="data"
>
<thead
class="bg-slate-300"
></thead>
</DataTable>
<script setup lang="ts">
const editItem = async () => {
console.log("edit!!");
};
</script>
so the arrow functioneditItem looks like is declared but its value is never read,
I tried to use the button out of the data table, and the function is readable, only inside this datatable, I get no action!
I have data table, with VueJS, I need to edit each row content by pressing on edit button, or if there any eaiser method to do it, I checked the documantation of the datatables.net, there are no prepared feature like in PHP
Related
I am making a timeslot-picker component and want to toggle the class of each individual timeslot-item so it changes color and also add the timeslot information to an array of objects. If I update the state array with an onClick function the classList.toggle function doesn't seem to work, as none of the timeslots change color. I would appreciate any help on how to combine both functionalites. This is the part of my component required for the functionality:
<ul className="w-[180px] h-[400px] overflow-y-scroll flex flex-col gap-1 pt-4 relative">
<div className="text-center ">{day.day}</div>
{timeslots.map((timeslot) => (
<li key={Math.random()} className="mt-3">
<button
onClick={(e) => {
e.currentTarget.classList.toggle('bg-violet-500');
setRequestedTimeslots([
...requestedTimeslots,
{
day: day.day,
start: timeslot.start,
end: timeslot.end,
},
]);
}}
className="px-4 py-2 bg-[#F5F5F5] rounded-xl w-full text-center"
>
{timeslot.start} - {timeslot.end}
</button>
</li>
))}
</ul>
I'm using vue 3 and tailwind css to build an interface and I'm trying to add two different input fields, after clicking the add button, and remove the two fields after clicking the remove button. Something like this Two dynamic input fields with one button click
Currently, I have it working, but it is showing a wrong format. When I click the add button, it shows two input keys in the first row and two input values in the second row.
Wrong Input format.
When I click on the add button, I want it to show one input key and one input value in the first row, and the same in the next row.
This is the form
<div v-show="type == 'dropdown'" class="grid gap-4 md:grid-cols-5 items-center">
<div v-for="(value, index) in keys" :key="index" class="col-span-2">
<input v-model="value.key" type="text" class="block p-2 w-full text-gray-700 bg-gray-50 rounded-sm border border-gray-300" placeholder="Input key" />
</div>
<div v-for="(value, index) in input_values" :key="index" class="col-span-2 row-span-1">
<input v-model="value.input_value" type="text" class="block p-2 w-full text-gray-700 bg-gray-50 rounded-sm border border-gray-300" placeholder="Input value" />
</div>
<div class="col-span-1 row-span-1">
<span><fa icon="square-plus" class="w-6 h-6 text-green-500 cursor-pointer" #click="add" /></span>
<fa v-show="index != 0" icon="square-minus" class="w-6 h-6 px-2 text-red-500 cursor-pointer" #click="remove(index)" />
</div>
</div>
This the script section
export default {
name: "App",
data() {
return {
keys: [{ key: "" }],
input_values: [{ input_value: "" }],
}
},
add() {
this.keys.push({ key: "" })
this.input_values.push({ input_value: "" })
},
remove(index) {
this.keys.splice(index, 1)
this.input_values.splice(index, 1)
},
}
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>
I'm trying to make a table column editable inline using Alpine.js. The idea is to have an "edit in place" functionality so when a row is double-clicked allows for the content to be editable. The issue I'm having is when a cell is clicked it activates all rows.
The ideal behavior is only the clicked row should be editable, all others should remain uneditable.
I have a preview of the issue here, https://codepen.io/ezeagwulae/pen/ZEKeYGQ
<div x-data="data()" class="p-4">
<div class="uppercase font-bold">shopping items</div>
<template x-for="item in items">
<div>
<a #click.prevent #dblclick="toggleEditingState" x-show="!isEditing" x-text="item.item" class="select-none cursor-pointer underline font-lg text-blue-500"></a>
<input type="text" x-model="text" x-show="isEditing" #click.away="toggleEditingState" #keydown.enter="disableEditing" #keydown.window.escape="disableEditing" class="bg-white focus:outline-none focus:shadow-outline border border-gray-300 rounded-lg py-2 px-4 appearance-none leading-normal w-128" x-ref="input">
</div>
</template>
</div>
In your JS file make sure to get the double-clicked input field with e.target.
In your HTML x-model should be set to item.item.
Here's a working example.
HTML
<div x-data="data()" class="p-4">
<div class="uppercase font-bold">shopping items</div>
<template x-for="item in items">
<div>
<a #click.prevent #dblclick="toggleEditingState" x-show="!isEditing" x-text="item.item" class="select-none cursor-pointer underline font-lg text-blue-500"></a>
<input type="text" x-model="item.item" x-show="isEditing" #click.away="toggleEditingState" #keydown.enter="disableEditing" #keydown.window.escape="disableEditing" class="bg-white focus:outline-none focus:shadow-outline border border-gray-300 rounded-lg py-2 px-4 appearance-none leading-normal w-128" x-ref="input">
</div>
</template>
</div>
JS
function data() {
return {
text: "Double click to edit",
isEditing: false,
toggleEditingState(e) {
const el = e.target
this.isEditing = !this.isEditing;
el.focus()
},
disableEditing() {
this.isEditing = false;
},
items: [
{ id: 1, item: "apple" },
{ id: 2, item: "eggs" },
{ id: 3, item: "milk" }
]
};
}
Any suggestions to make only the clicked row editable and not all rows? For instance, if "eggs" the input field should be shown for this row and the other rows should remain as is
For example like this:
<link href="https://unpkg.com/tailwindcss#^2/dist/tailwind.min.css" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine#v2.x.x/dist/alpine.js"></script>
<div
x-data="{
items: [
{ id: 1, item: 'apple', edit: false },
{ id: 2, item: 'eggs', edit: false },
{ id: 3, item: 'milk', edit: false },
]
}"
class="p-4"
>
<div class="uppercase font-bold">shopping items</div>
<template x-for="(item, index) in items">
<div>
<a
#click.prevent
#dblclick="
item.edit = true;
$nextTick(() => $refs[item.id].focus());
"
#click.away="item.edit = false"
x-show="!item.edit"
x-text="item.item"
class="
select-none
cursor-pointer
underline
font-lg
text-blue-500
"
></a>
<input
type="text"
x-model="item.item"
x-show="item.edit"
#click.away="item.edit = false"
#keydown.enter="item.edit = false"
#keydown.window.escape="item.edit = false"
class="
bg-white
focus:outline-none focus:shadow-outline
border border-gray-300
rounded-lg
py-2
px-4
appearance-none
leading-normal
w-128
"
:x-ref="item.id"
/>
</div>
</template>
</div>
I'm trying to build a responsive menu with Tailwind CSS and Vue.js. Currently I have this template:
<template>
<nav class="flex items-center justify-between flex-wrap bg-pink-100 p-6">
<div class="flex items-center flex-shrink-0 mr-6">
<span class="font-semibold text-xl tracking-tight">Pixie</span>
</div>
<div class="block md:hidden" >
<button #click='clickMenu' class="flex items-center px-3 py-2 border rounded" >
<svg class="fill-current h-3 w-3" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title>Menu</title><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z"/></svg>
</button>
</div>
<div class="w-full flex-grow md:flex md:items-center md:w-auto" v-if="menuVisible">
<div class="text-sm md:flex-grow">
<a href="#responsive-header" class="block mt-4 md:inline-block md:mt-0 hover:text-white mr-4">
Features
</a>
<a href="#responsive-header" class="block mt-4 md:inline-block md:mt-0 hover:text-white mr-4">
Pricing
</a>
<a href="#responsive-header" class="block mt-4 md:inline-block md:mt-0 hover:text-white">
Blog
</a>
</div>
<div>
Sign Up
Log In
</div>
</div>
</nav>
</template>
With this Javascript:
<script>
export default {
data: function() {
return {
menuVisible: true
}
},
methods: {
clickMenu: function() {
this.menuVisible = !this.menuVisible
}
}
}
</script>
All I want to do is initially hide the mobile menu when the breakpoint reaches 'sm' on Tailwind. This would mean the user would have to click the menu button to see the menu, which I think is the expected behavior on mobile devices.
I don't want to build 2 separate menus which get shown on different breakpoints as I want to avoid duplicating code. Is there a way to access the current breakpoint for Tailwind in Vue.js? This would mean I could set the menuVisible to a computed property which only allows it to be visible if the breakpoint is desktop or tablet, or if the user has clicked the menu.
Or is there another better way to do this?
Thanks for any help!
you can write a plugin for it in you app and import it, im using nuxt and this worked for me
export default (context, inject) => {
const burger = () => {
const menu = document.querySelector("#menu");
if (menu.classList.contains("hidden")) {
menu.classList.remove("hidden");
} else {
menu.classList.add("hidden");
}
};
inject("burger", burger);
context.$burger = burger;
};
One way of achieving this could be to configure the TailwindCSS-breakpoints in your tailwind.config.js and to then reuse that file to import the breakpoint-values into your Menu-component.
Here we are setting TailwindCSS breakpoints according to the TailwindCSS documentation. We are actually just setting the default TailwindCSS breakpoint values, but setting them makes them accessible via the file.
//tailwind.config.js
module.exports = {
theme: {
screens: {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px'
}
}
}
Now, in your Menu.vue, you can import the breakpoint from your TailwindCSS-config and write the necessary function, to check if the current window-size is smaller than the md-breakpoint. If it's not, you can simply return true. If it is, you can check, if the menu was toggled open.
// Menu.vue
<script>
const tailwindConfig = require('tailwind.config.js')
export default {
data() {
return {
windowWidth: 0,
menuOpen: false,
mdBreakpoint: Number(tailwindConfig.theme.screens.md.replace('px', ''))
}
},
computed: {
menuVisible() {
return this.windowWidth > mdBreakpoint ? true : this.menuOpen
}
},
methods: {
updateWindowSize() {
this.windowWidth = window.innerWidth
},
clickMenu() {
this.menuOpen = !this.menuOpen
}
},
mounted() {
this.updateWindowSize()
window.addEventListener('resize', this.updateWindowSize)
},
beforeDestroyed() {
window.removeEventListener('resize', this.updateWindowSize)
}
}
</script>