on click to remove each chip after enter Vuejs - javascript

I would like to remove single chip of my choice when I click on the chip, here is the code. Right now I can only delete chips that already separated by comma together not individually.
I'm not sure how to remove oneChip after the loop
new Vue({
el:'div',
props: {
set: {
type: Boolean,
default: true
}
},
data() {
return {
chips:[],
currentInput: ''
}
},
methods: {
saveChip() {
const {chips, currentInput, set} = this;
((set && chips.indexOf(currentInput) === -1) || !set) && chips.push(currentInput);
this.currentInput = '';
},
deleteChip(index) {
this.chips.splice(index, 1);
},
backspaceDelete({which}) {
which == 8 && this.currentInput === '' && this.chips.splice(this.chips.length - 1);
}
},
template: `
<div class="chip-container">
<div v-for="(chip, i) of chips" :key="chip.label">
<span
class="chip" v-for="oneChip in chip.split(',')" v-text="oneChip"
#click="deleteChip(oneChip)">
</span>
</div>
<input v-model="currentInput" #keypress.enter="saveChip" >
</div>
`
})
span {
border: 1px solid red;
}
.chip-container {
width: 800px;
border: 1px solid #ccc;
min-height: 34px;
display:flex;
flex-wrap: wrap;
align-content: space-between;
.chip {
margin:4px;
background: #e0e0e0;
padding:0px 4px;
border: 1px solid #ccc
border-radius: 3px;
display:flex;
align-items: center;
i {
cursor: pointer;
opacity: .56;
margin-left:8px;
}
}
input {
flex: 1 1 auto;
width:30px;
border: none;
outline: none;
padding:4px;
}
}

Here is my solution for the issue, I have split chips within saveChip function
methods: {
saveChip() {
const {chips, currentInput, set} = this;
if ((set && chips.indexOf(currentInput) === -1) || !set) {
this.chips = this.chips.concat(currentInput.trim(' ').split(','))
}
this.currentInput = '';
},
deleteChip(chip) {this.chips = this.chips.filter(c => c !== chip)}
},
template: `
<div class="chip-container">
<div v-for="(chip, i) of chips" :key="chip.label">
<span
class="chip" v-for="oneChip in chip.split(',')" v-text="oneChip"
#click="deleteChip(oneChip)">
</span>
</div>
<input v-model="currentInput" #keypress.enter="saveChip" >
</div>
`
})

Related

Is there any other way to sort a drag and drop todo list without using the index of the items?

I'm working on a javascript to-do list where you can view all the elements on the list or you can view just the active items or the completed items.
Each of the views has its own array which I sorted out using the index of each element
but when I reorder the list on one of the views, the change is not implemented in the other views.
How do I rectify this?
const dragArea1 = document.querySelector('#task1');
const dragArea2 = document.querySelector('#task2');
const dragArea3 = document.querySelector('#task3');
const addnew = document.querySelector('[name="addnew"]')
const add = document.querySelector('[name="new"]')
const countIt = document.querySelector('#count')
var all = [];
var active = [];
var complete = [];
var lists = document.querySelectorAll('ul');
var views = document.querySelectorAll('.action .views a');
var mobileViews = document.querySelectorAll('#views a');
var list = document.querySelector('.list');
countIt.innerHTML = active.length;
addnew.addEventListener('click', () => {
var newItem
if (addnew.checked == true) {
newItem = {
val: add.value,
checked: false
}
all.push(newItem);
active.push(newItem);
window.setTimeout(() => {
addnew.checked = false;
add.value = '';
}, 300);
displayAll();
count();
}
})
list.addEventListener('click', (ev) => {
if (ev.target.tagName === 'LABEL' || ev.target.tagName === 'P' || ev.target.tagName === 'LI') {
ev.target.classList.toggle('checked');
sortAllList();
if (lists[1].style.display == 'block') {
activeToComplete();
}
if (lists[2].style.display == 'block') {
completeToActive();
}
sortActive();
sortComplete();
}
if (all.length == 0) {
var htmlCode = `<em style="text-align: center; width: 100%; padding: 20px;">add a todo item</em>`;
lists[0].innerHTML = htmlCode;
}
if (active.length == 0) {
var htmlCode = `<em style="text-align: center; width: 100%; padding: 20px;">add a todo item</em>`;
lists[1].innerHTML = htmlCode;
}
if (complete.length == 0) {
var htmlCode = `<em style="text-align: center; width: 100%; padding: 20px;">complete a todo item</em>`;
lists[2].innerHTML = htmlCode;
}
// console.log(ev.target.tagName);
})
function count() {
// to keep count of active items
countIt.innerHTML = active.length;
}
views[0].classList.add('view')
mobileViews[0].classList.add('view')
function displayAll() {
sortActive();
sortComplete();
var htmlCode = "";
if (all.length !== 0) {
for (let i = 0; i < all.length; i++) {
htmlCode += `
<li draggable="true">
<div class="check">
<input type="checkbox" name="listItem" id="item${i}">
<label for="item${i}"></label>
</div>
<p class="itemdesc">${all[i].val}</p>
<span onclick="del(${i})">╳</span>
</li>
`
}
lists[0].innerHTML = htmlCode;
}
lists[0].style.display = 'block';
lists[1].style.display = 'none';
lists[2].style.display = 'none';
views[0].classList.add('view')
views[1].classList.remove('view')
views[2].classList.remove('view')
mobileViews[0].classList.add('view')
mobileViews[1].classList.remove('view')
mobileViews[2].classList.remove('view')
count()
keepChecked();
}
function sortActive() {
// to add active items to the active array
var fit
fit = all.filter(el => el.checked == false)
active = fit
count();
}
function sortComplete() {
//to add completed items to the complete array
var com
com = all.filter(el => el.checked == true)
complete = com
// console.log('complete', complete);
}
function sortAllList() {
// to sort the items into active and completed
const items = document.querySelectorAll('#task1 li');
for (let i = 0; i < all.length; i++) {
if (items[i].classList.contains('checked') == true) {
all[i].checked = true
} else {
all[i].checked = false
}
}
}
function activeToComplete() {
let newA
const items = document.querySelectorAll('#task2 li')
for (let i = 0; i < active.length; i++) {
if (items[i].classList.contains('checked') == true) {
active[i].checked = true;
// active.splice(i,1);
// console.log(active.splice());
} else {
active[i].checked = false
}
}
newA = active.filter(el => el.checked !== true)
console.log(newA);
active = newA;
}
function keepChecked() {
// to keep the completed items checked afetr changing views
const allItems = document.querySelectorAll('#task1 li');
for (let i = 0; i < all.length; i++) {
if (all[i].checked == true) {
allItems[i].classList.add('checked')
}
}
}
function completeToActive() {
const items = document.querySelectorAll('#task3 li')
for (let i = 0; i < complete.length; i++) {
if (items[i].classList.contains('checked') == true) {
complete[i].checked = true;
} else {
complete[i].checked = false
complete.splice(i, 1);
console.log(complete.splice());
}
}
}
function displayActive() {
sortAllList();
sortActive();
var htmlCode = "";
if (active.length !== 0) {
for (let i = 0; i < active.length; i++) {
htmlCode += `
<li draggable="true">
<div class="check">
<input type="checkbox" name="listItem" id="item${i}">
<label for="item${i}"></label>
</div>
<p class="itemdesc">${active[i].val}</p>
<span onclick="del(${i})">╳</span>
</li>
`
}
lists[1].innerHTML = htmlCode;
}
lists[1].style.display = 'block';
lists[0].style.display = 'none';
lists[2].style.display = 'none';
views[1].classList.add('view')
views[0].classList.remove('view')
views[2].classList.remove('view')
mobileViews[1].classList.add('view')
mobileViews[0].classList.remove('view')
mobileViews[2].classList.remove('view')
count()
}
function displayCompleted() {
let unique = [...new Set(complete)]
// console.log(unique[0].val);
var htmlCode = "";
if (unique.length !== 0) {
for (let i = 0; i < unique.length; i++) {
htmlCode += `
<li draggable="true" class="checked">
<div class="check">
<input type="checkbox" name="listItem" id="item${i}">
<label for="item${i}"></label>
</div>
<p class="itemdesc">${unique[i].val}</p>
<span onclick="del(${i})">╳</span>
</li>
`
}
lists[2].innerHTML = htmlCode;
}
lists[2].style.display = 'block';
lists[1].style.display = 'none';
lists[0].style.display = 'none';
views[2].classList.add('view')
views[0].classList.remove('view')
views[1].classList.remove('view')
mobileViews[2].classList.add('view')
mobileViews[1].classList.remove('view')
mobileViews[0].classList.remove('view')
count()
}
function clearComplete() {
var htmlCode = `<em style="text-align: center; width: 100%; padding: 20px;">complete a todo item</em>`;
complete = [];
lists[2].innerHTML = htmlCode;
}
function del(theIndex) {
let i = theIndex;
if (lists[0].style.display == 'block') {
all.splice(i, 1);
displayAll();
}
if (lists[1].style.display == 'block') {
active.splice(i, 1);
let removeFromAll = all.find(el => {
el == active.splice()
})
all.splice(removeFromAll, 1);
displayActive();
}
if (lists[2].style.display == 'block') {
complete.splice(i, 1);
let removeFromAll = all.find(el => {
el == complete.splice()
})
all.splice(removeFromAll, 1);
displayCompleted();
}
sortActive();
sortComplete();
}
new Sortable(dragArea1, {
animation: 350
})
new Sortable(dragArea2, {
animation: 350
})
new Sortable(dragArea3, {
animation: 350
})
:root {
--blue: hsl(220, 98%, 61%);
/* vd -> Very Drak */
--vdblue: hsl(235, 21%, 11%);
--vdDesaturatedblue: hsl(235, 24%, 19%);
--lightgrayblue: hsl(234, 39%, 85%);
--lightgrayblueh: hsl(236, 33%, 92%);
--darkgrayblue: hsl(234, 11%, 52%);
--vdGrayishblueh: hsl(233, 14%, 35%);
--vdGrayishblue: hsl(237, 14%, 26%);
--checkbg: linear-gradient(rgba(87, 221, 255, .7), rgba(192, 88, 243, .7));
--font: 'Josefin Sans', sans-serif;
font-size: 18px;
}
* {
padding: 0;
margin: 0;
font-family: var(--font);
/* font-weight: 700; */
}
*,
*::after,
*::before {
box-sizing: border-box;
}
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
textarea:-webkit-autofill,
textarea:-webkit-autofill:hover,
textarea:-webkit-autofill:focus,
select:-webkit-autofill,
select:-webkit-autofill:hover,
select:-webkit-autofill:focus {
border: none;
-webkit-text-fill-color: white;
background-color: transparent !important;
-webkit-box-shadow: 0 0 0px 1000px #00000000 inset;
transition: background-color 5000s ease-in-out 0s;
}
input:focus, input:active, input:visited, textarea:focus, textarea:active, textarea:visited{
background-color: transparent;
border: none;
outline: none;
}
a, em, span{
display: inline-block;
cursor: pointer;
}
a{
text-decoration: none;
display: inline-block;
}
header, main, footer{
width: 100%;
max-width: 30rem;
padding: 10px;
}
main {
display: flex;
flex-direction: column;
gap: 30px;
align-items: center;
}
main #new,
li {
display: flex;
align-items: center;
gap: 20px;
padding: 1rem;
width: 100%;
}
main section,
main #views {
width: 100%;
}
main section,
main #new,
main #views {
border-radius: 5px;
}
main .list {
min-height: 2.5rem;
max-height: 20rem;
/* height: 10rem; */
position: relative;
overflow-y: auto;
}
main .list ul {
/* position: absolute; */
/* top: 20px; */
width: 100%;
display: none;
}
main .list ul:nth-child(1) {
display: block;
}
main #new input[name="new"] {
padding: 10px;
height: inherit;
}
input {
background-color: transparent;
width: calc(100% - 70px);
border: none;
font-size: 1rem;
}
li {
justify-content: flex-start;
}
li .check {
position: relative;
}
main #new .check input,
li .check input {
display: none;
}
main #new .check label,
li .check label {
width: 30px;
height: 30px;
border-radius: 30px;
display: inline-block;
}
main #new .check input:checked~label,
li.checked .check label {
background-image: var(--checkbg), url(images/icon-check.svg);
background-position: center center;
background-repeat: no-repeat;
}
li p {
width: 85%;
}
li.checked label {
background-color: #66666696;
}
li.checked p {
text-decoration: line-through;
}
li span {
/* justify-self: flex-end; */
display: none;
}
li:hover span {
display: flex;
}
main .action {
display: flex;
justify-content: space-between;
/* gap: 2rem; */
padding: 1.1rem;
font-size: .8rem;
}
.views a,
#views a {
font-weight: 700;
}
.action a.view {
color: var(--blue);
}
main #views {
padding: .8rem;
text-align: center;
font-size: .8rem;
display: none;
}
#views a.view {
color: var(--blue);
}
main #views+p {
font-size: .7rem;
}
li,
em {
border-bottom: 1px solid var(--darkgrayblue);
}
li,
li p,
main .action a:hover {
color: var(--lightgrayblue);
}
a,
em,
li.checked p,
p,
span,
input,
li span {
color: var(--darkgrayblue);
}
header img {
content: url("images/icon-sun.svg");
}
main #new {
background-color: var(--vdDesaturatedblue);
}
main #new .check label,
li .check label {
border: 1px solid var(--vdGrayishblue);
}
main #new .check label:hover,
li .check label:hover {
border: 1px solid var(--vdGrayishblue);
}
main section,
main #views {
background-color: var(--vdDesaturatedblue);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.15.0/Sortable.min.js"></script>
<main role="main">
<div id="new">
<div class="check">
<input type="checkbox" name="addnew" id="addnew">
<label for="addnew"></label>
</div>
<input type="text" name="new" placeholder="Create a new todo...">
</div>
<section>
<div class="list">
<ul id="task1">
<em style="text-align: center; width: 100%; padding: 20px;">add a todo item</em>
</ul>
<ul id="task2">
<em style="text-align: center; width: 100%; padding: 20px;">add a todo item</em>
</ul>
<ul id="task3">
<em draggable="true" style="text-align: center; width: 100%; padding: 20px;">complete a todo item</em>
</ul>
</div>
<div class="action">
<p>
<span id="count"></span> items left
</p>
<div class="views">
All
Active
Completed
</div>
Clear Completed
</div>
</section>
<div id="views">
All
Active
Completed
</div>
<p>Drag and drop to reorder list</p>
</main>

Function removes items from localStorage only if run manually

I'm looking for advice/tips on how to fix a function that is supposed to remove items from localStorage. I'm following a tutorial by John Smilga that I found on Youtube. Although I've modeled my code on his, apparently, I have missed something.
This function works perfectly well if I run it manually from the console and pass in the id of the item that I want to remove from localStorage.
function removeFromLocalStorage(id) {
console.log(id);
let storageItems = getLocalStorage();
console.log(storageItems);
storageItems = storageItems.filter(function(singleItem) {
if (singleItem.id !== id) {
return singleItem;
}
})
console.log(storageItems);
localStorage.setItem("list", JSON.stringify(storageItems));
}
However, when this function is triggered by the deleteItem() function, it refuses to remove the item from localStorage. It still works, there are a bunch of console.logs in it that track its execution, and I can check that it receives the correct item id as the argument, but for some reason it doesn't filter out the item that needs to be removed. I am completely lost and have no idea how to identify the problem. I can't debug it with console.logs as I usually do. I will be very grateful if you help me find the problem. Any advice will be appreciated.
In case the entire code is needed, please find it below.
const form = document.querySelector(".app__form");
const alert = document.querySelector(".app__alert");
const input = document.querySelector(".app__input");
const submitBtn = document.querySelector(".app__submit-btn");
const itemsContainer = document.querySelector(".app__items-container");
const itemsList = document.querySelector(".app__items-list");
const clearBtn = document.querySelector(".app__clear-btn");
let editElement;
let editFlag = false;
let editId = "";
form.addEventListener("submit", addItem);
clearBtn.addEventListener("click", clearItems);
function addItem(e) {
e.preventDefault();
const id = Math.floor(Math.random() * 9999999999);
if (input.value && !editFlag) {
const item = document.createElement("div");
item.classList.add("app__item");
const attr = document.createAttribute("data-id");
attr.value = id;
item.setAttributeNode(attr);
item.innerHTML = `<p class='app__item-text'>${input.value}</p>
<div class='app__item-btn-cont'>
<button class='app__item-btn app__item-btn--edit'>edit</button>
<button class='app__item-btn app__item-btn--delete'>delete</button>
</div>`
const editBtn = item.querySelector(".app__item-btn--edit");
const deleteBtn = item.querySelector(".app__item-btn--delete");
editBtn.addEventListener("click", editItem);
deleteBtn.addEventListener("click", deleteItem);
itemsList.appendChild(item);
displayAlert("item added", "success");
addToLocalStorage(id, input.value);
setBackToDefault();
itemsContainer.classList.add("app__items-container--visible");
} else if (input.value && editFlag) {
editElement.textContent = input.value;
// edit local storage
editLocalStorage(editId, input.value);
setBackToDefault();
displayAlert("item edited", "success");
} else {
displayAlert("empty field", "warning");
}
}
function setBackToDefault() {
input.value = "";
editFlag = false;
editId = "";
submitBtn.textContent = "Submit";
submitBtn.className = "app__submit-btn";
}
function displayAlert(text, action) {
alert.textContent = text;
alert.classList.add(`app__alert--${action}`);
setTimeout(function() {
alert.textContent = "";
alert.classList.remove(`app__alert--${action}`);
}, 700)
}
function clearItems() {
const items = document.querySelectorAll(".app__item");
if (items.length > 0) {
items.forEach(function(singleItem) {
itemsList.removeChild(singleItem);
})
itemsContainer.classList.remove("app__items-container--visible");
displayAlert("items cleared", "cleared");
setBackToDefault();
}
}
function editItem(e) {
const item = e.currentTarget.parentElement.parentElement;
editElement = e.currentTarget.parentElement.previousElementSibling;
editId = item.dataset.id;
editFlag = true;
input.value = editElement.textContent;
submitBtn.textContent = "Edit";
submitBtn.classList.add("app__submit-btn--edit");
input.focus();
}
function deleteItem(e) {
const item = e.currentTarget.parentElement.parentElement;
const itemId = item.dataset.id;
removeFromLocalStorage(itemId);
displayAlert("item removed", "cleared");
setBackToDefault();
itemsList.removeChild(item);
if (itemsList.children.length === 0) {
itemsContainer.classList.remove("app__items-container--visible");
}
}
function addToLocalStorage(id, value) {
const itemsObj = {id: id, value: input.value};
let storageItems = getLocalStorage();
storageItems.push(itemsObj);
localStorage.setItem("list", JSON.stringify(storageItems));
}
function removeFromLocalStorage(id) {
console.log(id);
let storageItems = getLocalStorage();
console.log(storageItems);
storageItems = storageItems.filter(function(singleItem) {
if (singleItem.id !== id) {
return singleItem;
}
})
console.log(storageItems);
localStorage.setItem("list", JSON.stringify(storageItems));
}
function editLocalStorage(id, value) {
}
function getLocalStorage() {
return localStorage.getItem("list") ? JSON.parse(localStorage.getItem("list")) : [];
}
* {
margin: 0;
padding: 0;
}
.app {
width: 70%;
max-width: 600px;
margin: 75px auto 0;
}
.app__title {
text-align: center;
/* color: #1B5D81; */
margin-top: 20px;
color: #377FB4;
}
.app__alert {
width: 60%;
margin: 0 auto;
text-align: center;
font-size: 20px;
color: #215884;
border-radius: 7px;
height: 23px;
transition: 0.4s;
text-transform: capitalize;
}
.app__alert--warning {
background-color: rgba(243, 117, 66, 0.2);
color: #006699;
}
.app__alert--success {
background-color: rgba(165, 237, 92, 0.4);
color: #3333ff;
}
.app__alert--cleared {
background-color: #a978da;
color: white;
}
.app__input-btn-cont {
display: flex;
margin-top: 30px;
}
.app__input {
width: 80%;
box-sizing: border-box;
font-size: 20px;
padding: 3px 0 3px 10px;
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
border-right: none;
border: 1px solid #67B5E2;
background-color: #EDF9FF;
}
.app__input:focus {
outline: transparent;
}
.app__submit-btn {
display: block;
width: 20%;
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
border-left: none;
background-color: #67B5E2;
border: 1px solid #67B5E2;
cursor: pointer;
font-size: 20px;
color: white;
transition: background-color 0.7s;
padding: 3px 0;
}
.app__submit-btn--edit {
background-color: #95CB5D;
}
.app__submit-btn:active {
width: 19.9%;
padding: 0 0;
}
.app__submit-btn:hover {
background-color: #377FB4;
}
.app__submit-btn--edit:hover {
background-color: #81AF51;
}
.app__items-container {
visibility: hidden;
/* transition: 0.7s; */
}
.app__items-container--visible {
visibility: visible;
}
.app__item {
display: flex;
justify-content: space-between;
margin: 20px 0;
}
.app__item:hover {
background-color: #b9e2fa;
border-radius: 10px;
}
.app__item-text {
padding-left: 10px;
font-size: 20px;
color: #1B5D81;
}
.app__item-btn-cont {
display: flex;
}
.app__item-btn-img {
width: 20px;
height: 20px;
}
.app__item-btn {
border: none;
background-color: transparent;
cursor: pointer;
display: block;
font-size: 18px;
}
.app__item-btn--edit {
margin-right: 45px;
color: #2c800f;
}
.app__item-btn--delete {
margin-right: 15px;
color: rgb(243, 117, 66);
}
.app__clear-btn {
display: block;
width: 150px;
margin: 20px auto;
border: none;
background-color: transparent;
font-size: 20px;
color: rgb(243, 117, 66);
letter-spacing: 2px;
cursor: pointer;
transition: border 0.3s;
border: 1px solid transparent;
}
.app__clear-btn:hover {
border: 1px solid rgb(243, 117, 66);
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8" name="viewport" content="width=device-width,
initial-scale=1">
<link rel="stylesheet" href="list.css">
<title>To Do List App</title>
</head>
<body>
<section class="app">
<form class="app__form">
<p class="app__alert"></p>
<h2 class="app__title">To Do List</h2>
<div class="app__input-btn-cont">
<input class="app__input" type="text" id="todo" placeholder="do stuff">
<button class="app__submit-btn">Submit</button>
</div>
</form>
<div class="app__items-container">
<div class="app__items-list">
</div>
<button class="app__clear-btn">Clear Items</button>
</div>
</section>
<script src="list.js"></script>
</body>
</html>
Your code is fine you just used the wrong comparison operator.
In your case you are comparing 2 IDs (operands) to see if they match up, so you should use normal operators such as (==, !=), but instead in your case, you have used strict operators which are used to compare the operand type and the operand itself.
You can learn more about Comparison Operators here.
Ultimatly,
In your function removeFromLocalStorage(id), you have an extra equal sign in your if function.
Instead of:
if (singleItem.id !== id) {
return singleItem;}
It should be:
if (singleItem.id != id) {
return singleItem;}
Hope this helps.

Element UI Vue js is slow in render

I have over two hundred record looping through v-for when we change any value it takes nearly 2 sec to rerender the changed value below is the sample code. when I check the value it takes nearly 2 sec to the checkboxes. Is there any way to improve the rendering the speed this is really slow for 200 records. Thanks.
sandbox link https://codesandbox.io/s/focused-mahavira-9cc9s
<template>
<div class="selection">
<div class="check-group">
<el-checkbox
#change="handleCheckAllChange"
:indeterminate="isIndeterminate"
v-model="checkAll"
></el-checkbox>
</div>
<div v-for="row in selections" :key="row.id" class="check">
<el-checkbox v-model="row.checked" :disabled="row.disabled" :value="row.id" :key="row.id"></el-checkbox>
</div>
</div>
</template>
<script>
export default {
name: 'MultiCheckBox',
created() {
this.selections = this.rows
this.totalEnabledRows = this.rows.filter(v => !v.disabled).length
},
props: {
rows: {
type: Array,
required: true
}
},
data() {
return {
checkAll: false,
isIndeterminate: false,
selectedRows: [],
selections: [],
totalEnabledRows: 0
}
},
watch: {
rows(val) {
this.selections = val
this.totalEnabledRows = val.filter(v => !v.disabled).length
},
selections: {
handler(val) {
let selected = val.filter(v => v.checked).map(v => v.id)
this.isIndeterminate = this.totalEnabledRows > 0 && selected.length > 0 && this.totalEnabledRows > selected.length
this.checkAll = this.totalEnabledRows === selected.length
this.$emit('checked', selected)
},
deep: true
}
},
methods: {
handleCheckAllChange() {
this.selections.forEach(s => {
!s.disabled && (s.checked = this.checkAll)
})
}
}
}
</script>
<style lang="scss" scoped>
.selection {
position: sticky;
left: 0;
z-index: 103;
background: white;
}
.check-group {
height: 40px;
padding: 10px 10px 10px 25px;
width: 50px;
border-top: 1px solid #dde2eb;
border-bottom: 1px solid #dde2eb;
flex-wrap: nowrap;
background: white;
position: sticky;
top: 50px;
left: 0;
z-index: 103;
}
.check {
height: 48px;
padding: 10px 10px 10px 25px;
width: 50px;
}
</style>

How to combine html, javascript and css files into one .Vue component, grid component with external data

I saw this example on Vue.js website. This is an example of creating a reusable grid component and using it with external data.(If you want to see how it functions, here is the link : https://v2.vuejs.org/v2/examples/grid-component.html?) I have a basic understanding of how single file component works. I know <template> <script> <style> are accordingly for html, javascript and css parts. But I don't know how to make this grid component with external data fit in the skeleton. I didn't find any online tutorials about this. Please show how to fit this three files into one .vue file. Thank you.
HTML:
<!-- component template -->
<script type="text/x-template" id="grid-template">
<table>
<thead>
<tr>
<th v-for="key in columns"
#click="sortBy(key)"
:class="{ active: sortKey == key }">
{{ key | capitalize }}
<span class="arrow" :class="sortOrders[key] > 0 ? 'asc' : 'dsc'">
</span>
</th>
</tr>
</thead>
<tbody>
<tr v-for="entry in filteredHeroes">
<td v-for="key in columns">
{{entry[key]}}
</td>
</tr>
</tbody>
</table>
</script>
<!-- demo root element -->
<div id="demo">
<form id="search">
Search <input name="query" v-model="searchQuery">
</form>
<demo-grid
:heroes="gridData"
:columns="gridColumns"
:filter-key="searchQuery">
</demo-grid>
</div>
JavaScript:
// register the grid component
Vue.component('demo-grid', {
template: '#grid-template',
props: {
heroes: Array,
columns: Array,
filterKey: String
},
data: function () {
var sortOrders = {}
this.columns.forEach(function (key) {
sortOrders[key] = 1
})
return {
sortKey: '',
sortOrders: sortOrders
}
},
computed: {
filteredHeroes: function () {
var sortKey = this.sortKey
var filterKey = this.filterKey && this.filterKey.toLowerCase()
var order = this.sortOrders[sortKey] || 1
var heroes = this.heroes
if (filterKey) {
heroes = heroes.filter(function (row) {
return Object.keys(row).some(function (key) {
return String(row[key]).toLowerCase().indexOf(filterKey) > -1
})
})
}
if (sortKey) {
heroes = heroes.slice().sort(function (a, b) {
a = a[sortKey]
b = b[sortKey]
return (a === b ? 0 : a > b ? 1 : -1) * order
})
}
return heroes
}
},
filters: {
capitalize: function (str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
},
methods: {
sortBy: function (key) {
this.sortKey = key
this.sortOrders[key] = this.sortOrders[key] * -1
}
}
})
// bootstrap the demo
var demo = new Vue({
el: '#demo',
data: {
searchQuery: '',
gridColumns: ['name', 'power'],
gridData: [
{ name: 'Chuck Norris', power: Infinity },
{ name: 'Bruce Lee', power: 9000 },
{ name: 'Jackie Chan', power: 7000 },
{ name: 'Jet Li', power: 8000 }
]
}
})
CSS:
body {
font-family: Helvetica Neue, Arial, sans-serif;
font-size: 14px;
color: #444;
}
table {
border: 2px solid #42b983;
border-radius: 3px;
background-color: #fff;
}
th {
background-color: #42b983;
color: rgba(255,255,255,0.66);
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
td {
background-color: #f9f9f9;
}
th, td {
min-width: 120px;
padding: 10px 20px;
}
th.active {
color: #fff;
}
th.active .arrow {
opacity: 1;
}
.arrow {
display: inline-block;
vertical-align: middle;
width: 0;
height: 0;
margin-left: 5px;
opacity: 0.66;
}
.arrow.asc {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-bottom: 4px solid #fff;
}
.arrow.dsc {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid #fff;
}
My .vue file right now:
<template>
<!-- component template -->
<script type="text/x-template" id="grid-template">
<table>
<thead>
<tr>
<th v-for="key in columns"
#click="sortBy(key)"
:class="{ active: sortKey == key }">
{{ key | capitalize }}
<span class="arrow" :class="sortOrders[key] > 0 ? 'asc' : 'dsc'">
</span>
</th>
</tr>
</thead>
<tbody>
<tr v-for="entry in filteredHeroes">
<td v-for="key in columns">
{{entry[key]}}
</td>
</tr>
</tbody>
</table>
</script>
<!-- demo root element -->
<div id="demo">
<form id="search">
Search <input name="query" v-model="searchQuery">
</form>
<demo-grid
:heroes="gridData"
:columns="gridColumns"
:filter-key="searchQuery">
</demo-grid>
</div>
</template>
<script>
export default {
data: function () {
var sortOrders = {}
this.columns.forEach(function (key) {
sortOrders[key] = 1
})
return {
sortKey: '',
sortOrders: sortOrders
}
},
computed: {
filteredHeroes: function () {
var sortKey = this.sortKey
var filterKey = this.filterKey && this.filterKey.toLowerCase()
var order = this.sortOrders[sortKey] || 1
var heroes = this.heroes
if (filterKey) {
heroes = heroes.filter(function (row) {
return Object.keys(row).some(function (key) {
return String(row[key]).toLowerCase().indexOf(filterKey) > -1
})
})
}
if (sortKey) {
heroes = heroes.slice().sort(function (a, b) {
a = a[sortKey]
b = b[sortKey]
return (a === b ? 0 : a > b ? 1 : -1) * order
})
}
return heroes
}
},
filters: {
capitalize: function (str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
},
methods: {
sortBy: function (key) {
this.sortKey = key
this.sortOrders[key] = this.sortOrders[key] * -1
}
}
}
</script>
<style scoped>
body {
font-family: Helvetica Neue, Arial, sans-serif;
font-size: 14px;
color: #444;
}
table {
border: 2px solid #42b983;
border-radius: 3px;
background-color: #fff;
}
th {
background-color: #42b983;
color: rgba(255,255,255,0.66);
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
td {
background-color: #f9f9f9;
}
th, td {
min-width: 120px;
padding: 10px 20px;
}
th.active {
color: #fff;
}
th.active .arrow {
opacity: 1;
}
.arrow {
display: inline-block;
vertical-align: middle;
width: 0;
height: 0;
margin-left: 5px;
opacity: 0.66;
}
.arrow.asc {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-bottom: 4px solid #fff;
}
.arrow.dsc {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid #fff;
}
</style>
I don't know what to do with the javascript file in <script>, obviously I can't just copy it and paste. But I am confused about what changes I should make.
Assuming you've set up your build pipeline properly your SFC doesn't look correct. Taking a deeper look into the guide about SFC you have to wrap each part of your code into its own block, meaning.
HTML (template)
JavaSCript (script)
CSS (style)
Your SFC would then have to look like this.
<template>
<!-- HTML markup here -->
</template>
<script>
// JavaScript code here
</script>
<style>
/* CSS stylings here */
</style>
In your very example, you're mixing the template and script block which is not supposed to look like that.

React - How to format phone number as user types

I need to format a 10 digit string to this format: '(123) 456-7890'.
However, I need this to happen as the user types. So if the user has inputted only 3 digits, the input should display: '(123)'.
If they've inputted 5 digits the input should display: '(123) 45'
With my current code, the formatting only takes place after the 10th character is entered. I'd like it so that it formats it from the third character onwards.
const phoneRegex = /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/
const handleInput = (value) => {
return (
value.replace(phoneRegex, '($1) $2-$3')
)
}
class FindASubscriber extends React.Component {
constructor(props) {
super(props)
this.state = {
value: ''
}
}
render() {
const { label, placeholder, feedback } = this.props
const { value} = this.state
return (
<div className="find__a__subscriber">
<FlexGrid>
<FlexGrid.Col>
<FlexGrid.Row>
<Input
feedback={feedback}
label={label}
type="text"
pattern="[0-9]*"
placeholder={placeholder}
value={handleInput(value)}
maxLength="10"
onChange={
(event) => this.setState({value: event.target.value})
}
/>
</FlexGrid.Row>
</FlexGrid.Col>
</FlexGrid>
</div>
)
}
}```
You can normalize the input like so
the value is up-to-date in relation to event.target.value
previousValue is what has already been validated and set to state
This is structured in a way to prevent invalid characters from updating the input and also limits the input to 10 numbers.
Click the Run code snippet button below for a working example.
const normalizeInput = (value, previousValue) => {
// return nothing if no value
if (!value) return value;
// only allows 0-9 inputs
const currentValue = value.replace(/[^\d]/g, '');
const cvLength = currentValue.length;
if (!previousValue || value.length > previousValue.length) {
// returns: "x", "xx", "xxx"
if (cvLength < 4) return currentValue;
// returns: "(xxx)", "(xxx) x", "(xxx) xx", "(xxx) xxx",
if (cvLength < 7) return `(${currentValue.slice(0, 3)}) ${currentValue.slice(3)}`;
// returns: "(xxx) xxx-", (xxx) xxx-x", "(xxx) xxx-xx", "(xxx) xxx-xxx", "(xxx) xxx-xxxx"
return `(${currentValue.slice(0, 3)}) ${currentValue.slice(3, 6)}-${currentValue.slice(6, 10)}`;
}
};
const normalizeInput = (value, previousValue) => {
if (!value) return value;
const currentValue = value.replace(/[^\d]/g, '');
const cvLength = currentValue.length;
if (!previousValue || value.length > previousValue.length) {
if (cvLength < 4) return currentValue;
if (cvLength < 7) return `(${currentValue.slice(0, 3)}) ${currentValue.slice(3)}`;
return `(${currentValue.slice(0, 3)}) ${currentValue.slice(3, 6)}-${currentValue.slice(6, 10)}`;
}
};
const validateInput = value => {
let error = ""
if (!value) error = "Required!"
else if (value.length !== 14) error = "Invalid phone format. ex: (555) 555-5555";
return error;
};
class Form extends React.Component {
constructor() {
super();
this.state = { phone: "", error: "" };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleReset = this.handleReset.bind(this);
}
handleChange({ target: { value } }) {
this.setState(prevState=> ({ phone: normalizeInput(value, prevState.phone) }));
};
handleSubmit(e) {
e.preventDefault();
const error = validateInput(this.state.phone);
this.setState({ error }, () => {
if(!error) {
setTimeout(() => {
alert(JSON.stringify(this.state, null, 4));
}, 300)
}
});
}
handleReset() {
this.setState({ phone: "", error: "" });
};
render() {
return(
<form className="form" onSubmit={this.handleSubmit}>
<div className="input-container">
<p className="label">Phone:</p>
<input
className="input"
type="text"
name="phone"
placeholder="(xxx) xxx-xxxx"
value={this.state.phone}
onChange={this.handleChange}
/>
{this.state.error && <p className="error">{this.state.error}</p>}
</div>
<div className="btn-container">
<button
className="btn danger"
type="button"
onClick={this.handleReset}
>
Reset
</button>
<button className="btn primary" type="submit">Submit</button>
</div>
</form>
);
}
}
ReactDOM.render(
<Form />,
document.getElementById('root')
);
html {
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
font-size: 16px;
font-weight: 400;
line-height: 1.5;
-webkit-text-size-adjust: 100%;
background: #fff;
color: #666;
}
.btn {
color: #fff;
border: 1px solid transparent;
margin: 0 10px;
cursor: pointer;
text-align: center;
box-sizing: border-box;
padding: 0 30px;
vertical-align: middle;
font-size: .875rem;
line-height: 38px;
text-align: center;
text-decoration: none;
text-transform: uppercase;
transition: .1s ease-in-out;
transition-property: color,background-color,border-color;
}
.btn:focus {
outline: 0;
}
.btn-container {
text-align: center;
margin-top: 10px;
}
.form {
width: 550px;
margin: 0 auto;
}
.danger {
background-color: #f0506e;
color: #fff;
border: 1px solid transparent;
}
.danger:hover {
background-color: #ee395b;
color: #fff;
}
.error {
margin: 0;
margin-top: -20px;
padding-left: 26%;
color: red;
text-align: left;
}
.input {
display: inline-block;
height: 40px;
font-size: 16px;
width: 70%;
padding: 0 10px;
background: #fff;
color: #666;
border: 1px solid #e5e5e5;
transition: .2s ease-in-out;
transition-property: color,background-color,border;
}
.input-container {
width: 100%;
height: 60px;
margin-bottom: 20px;
display: inline-block;
}
.label {
width: 25%;
padding-top: 8px;
display: inline-block;
text-align: center;
text-transform: uppercase;
font-weight: bold;
height: 34px;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
background: rgb(238, 238, 238);
}
.primary {
background-color: #1e87f0;
}
.primary:hover {
background-color: #0f7ae5;
color: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'>
</div>
Or... have 3 separate inputs and combine them when done.
const validateInput = value => {
let error = ""
if (!value) error = "Required!"
else if (value.length !== 14) error = "Invalid phone format. ex: (555) 555-5555";
return error;
};
const initialState = {
areaCode: "",
prefix: "",
suffix: "",
error: ""
};
class Form extends React.Component {
constructor() {
super();
this.state = initialState;
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleReset = this.handleReset.bind(this);
this.setInputRef = this.setInputRef.bind(this);
}
handleChange({ target: { name, value } }) {
let valueChanged = false;
this.setState(prevState => {
const nextValue = value.replace(/[^\d]/g, '');
const changedValue = prevState[name];
if (changedValue.length !== nextValue.length) valueChanged = true;
return { [name]: nextValue }
}, () => {
if(valueChanged) this.handleFocus(name)
});
};
setInputRef(name, element) {
this[name] = element;
}
handleFocus(name){
const { areaCode, prefix, suffix } = this.state;
const areaCodeFilled = areaCode.length === 3;
const prefixFilled = prefix.length === 3;
if(areaCodeFilled && name === "areaCode") {
this.prefix.focus();
this.prefix.selectionEnd = 0;
} else if(prefixFilled && name === "prefix") {
this.suffix.focus();
this.suffix.selectionEnd = 0;
}
}
handleSubmit(e) {
e.preventDefault();
const { areaCode, prefix, suffix } = this.state;
const phoneNumber = `(${areaCode}) ${prefix}-${suffix}`
const error = validateInput(phoneNumber);
this.setState({ error }, () => {
if(!error) {
setTimeout(() => {
alert(phoneNumber);
}, 300)
}
});
}
handleReset() {
this.setState(initialState);
};
render() {
return(
<form className="form" onSubmit={this.handleSubmit}>
<div className="input-container">
<div className="label">
Phone:
</div>
<div className="parenthesis" style={{ marginLeft: 10, marginRight: 2}}>(</div>
<input
className="input area-code"
type="text"
name="areaCode"
placeholder="xxx"
value={this.state.areaCode}
onChange={this.handleChange}
maxLength="3"
/>
<div className="parenthesis" style={{ marginRight: 2}}>)</div>
<input
ref={node => this.setInputRef("prefix", node)}
className="input prefix"
type="text"
name="prefix"
placeholder="xxx"
value={this.state.prefix}
onChange={this.handleChange}
maxLength="3"
/>
<div className="dash">-</div>
<input
ref={node => this.setInputRef("suffix", node)}
className="input suffix"
type="text"
name="suffix"
placeholder="xxxx"
value={this.state.suffix}
onChange={this.handleChange}
maxLength="4"
/>
</div>
<p className="error">{this.state.error}</p>
<div className="btn-container">
<button
className="btn danger"
type="button"
onClick={this.handleReset}
>
Reset
</button>
<button className="btn primary" type="submit">Submit</button>
</div>
</form>
);
}
}
ReactDOM.render(
<Form />,
document.getElementById('root')
);
html {
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
font-size: 16px;
font-weight: 400;
line-height: 1.5;
-webkit-text-size-adjust: 100%;
background: #fff;
color: #666;
}
.btn {
color: #fff;
border: 1px solid transparent;
margin: 0 10px;
cursor: pointer;
text-align: center;
box-sizing: border-box;
padding: 0 30px;
vertical-align: middle;
font-size: .875rem;
line-height: 38px;
text-align: center;
text-decoration: none;
text-transform: uppercase;
transition: .1s ease-in-out;
transition-property: color,background-color,border-color;
}
.btn:focus {
outline: 0;
}
.btn-container {
text-align: center;
margin-top: 10px;
}
.form {
width: 550px;
margin: 0 auto;
}
.danger {
background-color: #f0506e;
color: #fff;
border: 1px solid transparent;
}
.danger:hover {
background-color: #ee395b;
color: #fff;
}
.error {
margin: 0;
height: 24px;
margin-top: -20px;
padding-left: 26%;
color: red;
text-align: right;
}
.input {
display: flex;
height: 40px;
font-size: 16px;
width: 33%;
padding: 0 3px;
background: #fff;
color: #666;
outline: none;
border: 0;
}
.area-code,.prefix {
width: 27px;
}
.suffix {
width: 38px;
}
.dash,.parenthesis {
display: flex;
}
.input-container {
width: 100%;
margin-bottom: 20px;
display: flex;
flex-direction: row;
align-items: center;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
border: 1px solid #e5e5e5;
transition: .2s ease-in-out;
transition-property: color,background-color,borde
}
.label {
height: 100%;
background: rgb(238, 238, 238);
width: 25%;
padding-top: 8px;
display: flex;
text-transform: uppercase;
justify-content: space-around;
font-weight: bold;
height: 34px;
}
.primary {
background-color: #1e87f0;
}
.primary:hover {
background-color: #0f7ae5;
color: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'>
</div>
It's all about formatting. Any key that prints a character
should cause a rewrite of the input field.
This way the user only sees valid formatted field, no matter what he does.
The regex is simple ^\D*(\d{0,3})\D*(\d{0,3})\D*(\d{0,4})
function getFormattedPhoneNum( input ) {
let output = "(";
input.replace( /^\D*(\d{0,3})\D*(\d{0,3})\D*(\d{0,4})/, function( match, g1, g2, g3 )
{
if ( g1.length ) {
output += g1;
if ( g1.length == 3 ) {
output += ")";
if ( g2.length ) {
output += " " + g2;
if ( g2.length == 3 ) {
output += " - ";
if ( g3.length ) {
output += g3;
}
}
}
}
}
}
);
return output;
}
console.log( getFormattedPhoneNum("") );
console.log( getFormattedPhoneNum("2") );
console.log( getFormattedPhoneNum("asdf20as3d") );
console.log( getFormattedPhoneNum("203") );
console.log( getFormattedPhoneNum("203-44") );
console.log( getFormattedPhoneNum("444sg52asdf22fd44gs") );
console.log( getFormattedPhoneNum("444sg526sdf22fd44gs") );
console.log( getFormattedPhoneNum("444sg526sdf2244gs") );
console.log( getFormattedPhoneNum(" ra098 848 73653k-atui ") );
You could even get fancier and show underscores where a character
should be at any given time.
Like
(___) ___ - ____
(20_) ___ - ____
(123) 456 - ____
etc... (let me know if you want this)
Your current code's regex only matches when ten digits are entered (3, 3, then 4). You could update the regex to accept a range of digits, such as:
^\(?([0-9]{0,3})\)?[-. ]?([0-9]{0,3})[-. ]?([0-9]{0,4})$
Or you could have the regex simply make sure that 0-10 digits are entered ([0-9]{0,10}) and then split the string yourself into substrings of length 3, 3, and 4. Doing it the latter way seems better since you only want to show certain characters depending on how many digits the user has entered:
1 -> (1
123 -> (123)
1234567 -> (123) 456-7
1234567890 -> (123) 456-7890
You would have to handle each of these cases which a simple replace won't do.
I have used this library: https://www.npmjs.com/package/libphonenumber-js it has a AsYouType function, that you can use on your inputs
perhaps, something as the following will work for someone:
onChange={(e) => {
if (e.target.value.length < 13) {
var cleaned = ("" + e.target.value).replace(/\D/g, "");
let normValue = `${cleaned.substring(0, 3)}${
cleaned.length > 3 ? "-" : ""
}${cleaned.substring(3, 6)}${
cleaned.length > 6 ? "-" : ""
}${cleaned.substring(6, 11)}`;
handleChange(normValue);
}
}}
Use this package https://www.npmjs.com/package/react-number-formatter
this input component formats number and returns just number.

Categories

Resources