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>
Related
I have a multi-tier, multi-column menu that has been built using https://github.com/kontentino/react-multilevel-dropdown. The problem I am having is that the submenu items keep appearing underneath the main items. I have tried multiple attempts using z-index but nothing seems to be helping.
I am including my menu component, my CSS for this page as well as an image of the problem.
If possible I want to avoid trying to roll a custom menu using lists, but am willing to entertain another multi-level menu package.
MenuBarComponent.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Col from 'react-bootstrap/Col';
import Row from 'react-bootstrap/Row';
import Container from 'react-bootstrap/Container';
import { NavLink } from 'react-router-dom';
// import { CurrentPage_Update } from '../../redux/ActionCreators';
import Dropdown from 'react-multilevel-dropdown';
import { Loading } from './LoadingComponent';
import '../../shared/styles/menu.css'
const mapStateToProps = (state) => {
return {
siteMap: state.siteMap
}
}
const mapDispatchToProps = (dispatch) => {
return {
// CurrentPage_Update: (page) => { dispatch(CurrentPage_Update(page)) }
}
}
class MenuBar extends Component {
constructor(props) {
super(props);
this.state = {
openCount: 1,
isOpen: false
}
}
drawLink = (item) => {
return (
<NavLink
to={'/page_' + item.pageId}
className={`menu menu-link`}
key={"DDNavLink_" + item.pageId}
onClick={() => this.setState({ openCount: -1 })}
>{item.title}</NavLink>
);
}
NavListItem = (item, level, roles) => {
}
dropdownItem = (item, level, roles) => {
let brk = true;
//TODO: remove next line for production
item.roles = "*," + item.roles; //Make sure page is shown in development
if ((item.roles.indexOf("*") < 0) && (item.roles.indexOf("*") < 0)) {
var itemRoles = item.roles.toLowerCase().split(',');
for (var i = 0; i < itemRoles.length; i++) {
if (roles.indexOf(itemRoles[i]) > -1) {
//if found
brk = false;
break;
}
}
if (brk) return;
}
if (item.children.length > 0) {
//if any visible children show dropdown
//only endpoints are clickable
if (item.children.find((child) => { return child.visible !== "false" })) {
return (
<Dropdown.Item key={'Dropdown_' + item.pageId} className={`menu menu-item-container`}>
AA{item.title}
<Dropdown.Submenu position='right' className={`menu-UncontrolledDropdown sub${level + 1}`} >
{item.children.map((listItem) => {
return (this.dropdownItem(listItem, level + 1, roles))
})
}
</Dropdown.Submenu>
</Dropdown.Item>
)
}
else { //otherwise if no visible children show only the item
if (item.visible !== "false") {
return (
<Dropdown className={`sub${level + 1} menu menu-item-container`
} key={"DDItem_" + item.pageId} >
BB{level} { this.drawLink(item)}
</Dropdown >
)
}
}
}
else { //if no children show only item
return (
<Dropdown.Item className={`sub${level} menu menu-item-container`} key={"DDItem" + item.pageId}>
CC{level}
{this.drawLink(item, level)}
</Dropdown.Item>
)
}
}
render() {
// const setIsOpen = (value) => { this.setState({ isOpen: value }) }
// const toggle = () => { setIsOpen(!this.state.isOpen) };
if (this.props.siteMap.errMess) {
return (
<div>
{this.props.siteMap.errMess}
</div>
)
}
else if (this.props.siteMap.isLoading) {
return <Loading title="Site Menu" />
}
else {
var roles = this.props.siteMap.siteMapData.userRoles.toLowerCase();
var siteMap = this.props.siteMap.siteMapData.siteMap;
return (
<Container>
<Row>
<Col className="col-2 p-3 menu">
<NavLink to='/'>
<div className="align-top met-logo"></div>
</NavLink>
</Col>
<Col md={{ span: 9, offset: 1 }} className=" menu ">
{
siteMap.map((link, index) => {
return <div
key={index}
className='p-1 column menu menu-item-container menu-head'>
{this.dropdownItem(link, 0, roles)}
</div>
})
}
{/* https://github.com/kontentino/react-multilevel-dropdown#readme */}
</Col>
</Row>
</Container>
);
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(MenuBar);
menu.css
/*Necessary to force dropdown items into a straight line */
nav>ul>li>a+div>ul>li>a {
text-indent: 15px;
/* Can use padding-left as well */
}
.met-logo {
background-image: url('../images/METLogo.jpg');
background-size: 100%;
background-repeat: no-repeat;
/* width: 128px;
height: 91px; */
width: 100px;
height: 65px;
/* border: 1px solid black; */
top: -5px;
padding: 3px;
}
.menu {
color: #000;
position: sticky;
top: 5px;
/* z-index: 1000000; */
/* border:solid 1px black; */
}
.menu-collapse {
position: absolute;
top: 10px;
}
.menu-dropdown-toggle {
color: #000;
}
.menu-item-container {
color: #000;
background-color: #ccc;
border: solid 1px #555;
white-space: pre-wrap;
}
.menu-item-container :hover {
background-color: #aaa;
}
.menu-link {
color: #000;
cursor: pointer;
}
.menu-bar {
color: #000;
}
/* .menu-toggler {
float: right;
margin-right: 10px;
} */
.menu-dropdown-container {
color: #000;
padding-top: 0px;
padding-bottom: 0px;
margin-top: 15px;
overflow: wrap;
}
.menu-dropdown-container ul {
box-sizing: content-box;
background-color: #fff !important;
border: 0px;
overflow: wrap;
}
.menu-link {
color: #000;
}
.menu-dropdown-title {
color: #000;
}
.menu-dropdown-body {
color: #000;
}
.menu-head {
z-index: 9000!important;
}
.menu-UncontrolledDropdown {
color: #000;
background-color: #fff!important;
border: solid 1px #555;
transform: translate(-10%, 15px);
/* z-index: 99999!important; */
/* position: absolute; */
}
.sub0 {
z-index: 12000;
}
.sub1 {
z-index: 13000;
}
.sub2 {
z-index: 14000;
}
.sub3 {
z-index: 15000;
}
.sub4 {
z-index: 16000;
}
.menu-UncontrolledDropdown :hover {
background-color: #bbf;
}
.menu-UncontrolledDropdown>div {
background-color: #fff;
}
/* .fullWidth {
width: 100vw!important;
margin: 2px 2px 2px 2px;
}*/
.column {
display: inline-block;
padding: 8px;
font: 20px;
font-weight: 500;
}
----------
And here is the example of what the problem is.
I am not using JQuery in this project but React-Bootstrap is used.
After going through the code I finally removed all positioning and z-index from my CSS sheet, leaving in the translates for correct positioning. Leaving the implementation to the component code rather than my own has solved the problems including overlay on the menu column next to the dropdown.
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>
`
})
im newbie with vue so i have a couple of cuestions, im showing a table with some data that i get from a web-service, the thing is , that i dont always get the same amount of data, the data comes in an array, for example like this
var todos = [
{name : "dexter" , color : "orange"},
{name : "jaime", color : "green" },
{name : "stack", color : "yellow" },
{name : "overflow", color : "black" }
]
but as i say, I dont alway's get a response with 2 items in it,
right now i have to declare what item in te array im calling to, and i want to call all the items in the array to show them
var todos = [
{
name: "dexter",
color: "orange"
},
{
name: "jaime",
color: "green"
},
{
name: "stack",
color: "yellow"
},
{
name: "overflow",
color: "black"
}
]
var i = 0;
var sixthTable = new Vue({
el: '#sevenTable',
data: {
currentPage: 1,
elementsPerPage: 3,
ascending: false,
sortColumn: '',
rows: [
{
name: todos[0].name,
color: todos[0].color
},
{
name: todos[1].name,
color: todos[1].color
},
{
name: todos[2].name,
color: todos[2].color
},
{
name: todos[3].name,
color: todos[3].color
}
]
},
methods: {
"sortTable": function sortTable(col) {
if (this.sortColumn === col) {
this.ascending = !this.ascending;
} else {
this.ascending = true;
this.sortColumn = col;
}
var ascending = this.ascending;
this.rows.sort(function(a, b) {
if (a[col] > b[col]) {
return ascending ? 1 : -1
} else if (a[col] < b[col]) {
return ascending ? -1 : 1
}
return 0;
})
},
"num_pages": function num_pages() {
return Math.ceil(this.rows.length / this.elementsPerPage);
},
"get_rows": function get_rows() {
var start = (this.currentPage - 1) * this.elementsPerPage;
var end = start + this.elementsPerPage;
return this.rows.slice(start, end);
},
"change_page": function change_page(page) {
this.currentPage = page;
}
},
computed: {
"columns": function columns() {
if (this.rows.length == 0) {
return [];
}
return Object.keys(this.rows[0])
}
}
});
table {
font-family: 'Open Sans', sans-serif;
width: 750px;
border-collapse: collapse;
border: 3px solid #44475C;
margin: 10px 10px 0 10px;
}
table th {
text-transform: uppercase;
text-align: left;
background: #44475C;
color: #FFF;
cursor: pointer;
padding: 8px;
min-width: 30px;
}
table th:hover {
background: #717699;
}
table td {
text-align: left;
padding: 8px;
border-right: 2px solid #7D82A8;
}
table td:last-child {
border-right: none;
}
table tbody tr:nth-child(2n) td {
background: #00B4BB;
}
table {
font-family: 'Open Sans', sans-serif;
width: 750px;
border-collapse: collapse;
border: 3px solid #44475C;
margin: 10px 10px 0 10px;
}
table th {
text-transform: uppercase;
text-align: left;
background: #44475C;
color: #FFF;
cursor: pointer;
padding: 8px;
min-width: 30px;
}
table th:hover {
background: #007b80;
}
table td {
text-align: left;
padding: 8px;
border-right: 2px solid #7D82A8;
}
table td:last-child {
border-right: none;
}
table tbody tr:nth-child(2n) td {
background: #D4D8F9;
}
.pagination {
font-family: 'Open Sans', sans-serif;
text-align: right;
width: 750px;
padding: 8px;
}
.arrow_down {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB8AAAAaCAYAAABPY4eKAAAAAXNSR0IArs4c6QAAAvlJREFUSA29Vk1PGlEUHQaiiewslpUJiyYs2yb9AyRuJGm7c0VJoFXSX9A0sSZN04ULF12YEBQDhMCuSZOm1FhTiLY2Rky0QPlQBLRUsICoIN/0PCsGyox26NC3eTNn3r3n3TvnvvsE1PkwGo3yUqkkEQqFgw2Mz7lWqwng7ztN06mxsTEv8U0Aam5u7r5EInkplUol/f391wAJCc7nEAgE9Uwmkzo4OPiJMa1Wq6cFs7Ozt0H6RqlUDmJXfPIx+qrX69Ti4mIyHA5r6Wq1egND+j+IyW6QAUoul18XiUTDNHaSyGazKcZtdgk8wqhUKh9o/OMvsVgsfHJy0iWqVrcQNRUMBnd6enqc9MjISAmRP3e73T9al3XnbWNjIw2+KY1Gc3imsNHR0YV4PP5+d3e32h3K316TySQFoX2WyWR2glzIO5fLTSD6IElLNwbqnFpbWyO/96lCoai0cZjN5kfYQAYi5H34fL6cxWIZbya9iJyAhULBHAqFVlMpfsV/fHxMeb3er+Vy+VUzeduzwWC45XA4dlD/vEXvdDrj8DvURsYEWK3WF4FA4JQP9mg0WrHZbEYmnpa0NxYgPVObm5teiLABdTQT8a6vrwdRWhOcHMzMzCiXlpb2/yV6qDttMpkeshEzRk4Wo/bfoe4X9vb2amzGl+HoXNT29vZqsVi0sK1jJScG+Xx+HGkL4Tew2TPi5zUdQQt9otPpuBk3e0TaHmMDh1zS7/f780S0zX6Yni+NnBj09fUZUfvudDrNZN+GkQbl8Xi8RLRtHzsB9Hr9nfn5+SjSeWUCXC7XPq5kw53wsNogjZNohYXL2EljstvtrAL70/mVaW8Y4OidRO1/gwgbUMvcqGmcDc9aPvD1gnTeQ+0nmaInokRj0nHh+uvIiVOtVvt2a2vLv7Ky0tL3cRTXIcpPAwMDpq6R4/JXE4vFQ5FI5CN+QTaRSFCYc8vLy1l0rge4ARe5kJ/d27kYkLXoy2Jo4C7K8CZOsEBvb+9rlUp1xNXPL7v3IDwxvPD6AAAAAElFTkSuQmCC')
}
.arrow_up {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAaCAYAAACgoey0AAAAAXNSR0IArs4c6QAAAwpJREFUSA21Vt1PUmEYP4dvkQ8JFMwtBRocWAkDbiqXrUWXzU1rrTt0bdVqXbb1tbW16C9IBUSmm27cODdneoXjputa6069qwuW6IIBIdLvdaF4OAcOiGeDc87zPs/vd57P96WpFq7p6enbGo1mjKZpeTabjU1MTCRagGnOZHFxcXxtbe1XKpUq7+zslJeXl//Mz8+Hy+Uy3RxSE9qTk5M3otFooVQqgef4Wl9f343FYoEmoISrxuNxFX5f9vb2jhn/PxUKhfLS0tIPfFifUESRUMV8Pv/M6XReRm5rTGQyGeXxeGxYe1ezeBpBOBx2rKysbO7v79d4Wy3Y2Nj4GQqFbgnhaugxwiuGJx99Pp9FLBbXxYTXvTqd7v3MzIy6riIWGxJnMpl7AwMD14xGYyMsSq1WUyQdUqn0eSPlusQIsbGrq+vl4OCgvhFQZd1utyv1en0gEolcqsi47nWJlUrlG5fLZVcoFFy2nDKSDpIWlUoVTCQSEk4lCHmJMZ2GTCbTiMVikfIZ88l7enoos9l8dXt7+z6fDicxSJUokqDX6xXcl2wCROoc0vQCWL3sNfLOSdzR0fHY4XC4tVotl40gmVwup9xuN4OQv+UyqCFGH9rg7SOGYVRcBs3IEG4J0nVnamrqOtvuBDGGgQg9+wHFcVEi4a0LNkbdd6TrPKo8ODc311mteIIYjT/a398/jK+s1jnVM0kXoufCFvq0GuiIGEVgQIhfoygM1QrteEa9dAL7ITiYCt4RMabOK5AyKKzKWtvupLcRciu8D5J0EuDDPyT/Snd39yh6VtY2NhYQSR9G79Ds7OxdskRjEyAufvb7/cPoO5Z6e1+xtVKrq6vfcFzyi/A3ZrPZ3GdNSlwgo5ekE4X2RIQGf2C1WlufFE0GBeGWYQ8YERWLxQtnUVB830MKLZfL9RHir8lkssCn2G751tZWEWe03zTKm15YWPiEiXXTYDB0Ig/t7yd8PRws4EicwWHxO4jHD8/C5HiTTqd1BwcHFozKU89origB+y/kmzgYpgOBQP4fGmUiZmJ+WNgAAAAASUVORK5CYII=')
}
.arrow {
float: right;
width: 12px;
height: 15px;
background-repeat: no-repeat;
background-size: contain;
background-position-y: bottom;
}
.number {
display: inline-block;
padding: 4px 10px;
color: #000000;
border-radius: 4px;
background: #00B4BB;
margin: 0px 5px;
cursor: pointer;
}
.number:hover,
.number.active {
background: #ccfdff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.js"></script>
<div id="sevenTable">
<table>
<thead>
<tr>
<th v-for="col in columns" v-on:click="sortTable(col)">{{col}}
<div class="arrow" v-if="col == sortColumn" v-bind:class="[ascending ? 'arrow_up' : 'arrow_down']"></div>
</th>
</tr>
</thead>
<tbody>
<tr v-for="row in get_rows()">
<td v-for="col in columns">{{row[col]}}</td>
</tr>
</tbody>
</table>
<div class="pagination">
<div class="number" v-for="i in num_pages()" v-bind:class="[i == currentPage ? 'active' : '']" v-on:click="change_page(i)">{{i}}</div>
</div>
here is my full code i know my array its not actually a web-servce response i put in that way to "simplify" de code
thanks in advice
You can just assign the web-service-provided data right into your data like
rows: todos
I want to create a custom select component in Vue.js. Since I need specific options styling, I need to create 'select' made of div's etc that looks and acts like a real html select.
Currently I have something like this:
Vue.component('child', {
template: `<div class="component-container" #click="showOptions = !showOptions">
<div class="component__select">
<span class="component__select--name">Select Fruit</span>
<span class="c-arrow-down" v-if="!showOptions"></span>
<span class="c-arrow-up" v-if="showOptions"></span>
</div>
<ul class="component__select-options" v-if="showOptions" >
<li class="select--option" v-for="option in options">
<label> <input type="checkbox" :value="option"/> {{option.name}}</label>
</li>
</ul>
</div>`,
methods: {
selectOption(option) {
this.$emit('option', option)
}
},
data: () => ({
showOptions: false,
}),
props: ['options']
});
var vm = new Vue({
el: '#app',
data: () => ({
options: [
{id: 0, name: 'Apple'},
{id: 1, name: 'Banana'},
{id: 2, name: 'Orange'},
{id: 2, name: 'Strawberry'},
],
selectedFruit: ''
}),
})
.component__select {
height: 38px;
background-color: #F5F7FA;
border: 1px solid #dddddd;
line-height: 38px;
display: grid;
max-width: 500px;
grid-template-columns: 10fr 1fr;
}
.component__select--name {
font-size: 0.8rem;
padding: 0 0 0 25px;
cursor: pointer;
}
.c-arrow-down {
justify-self: end;
}
.component__select-options {
max-height: 180px;
border: 1px solid #dddddd;
border-top: none;
overflow: auto;
position: absolute;
z-index: 1500;
max-width: 500px;
width: 500px;
margin: 0;
padding: 0;
}
.select--option {
height: 35px;
display: grid;
align-content: center;
padding: 0 0 0 25px;
background-color: #f5f5fa;
border-bottom: 1px solid #dddddd;
}
.select--option:last-child {
border-bottom: none;
}
.select--option:nth-child(2n) {
background-color: #ffffff;
}
.select--option input{
display: none;
}
.single-option {
height: 55px;
background-color: #2595ec;
font-size: 0.8rem;
border: 1px solid red;
}
.cust-sel {
width: 200px;
height: 38px;
background-color: #f5f5fa;
border: 1px solid #dddddd;
}
.cust-sel:focus {
outline-width: 0;
}
<html>
<head>
<title>An example</title>
</head>
<body>
<div id="app">
<span> This is parent component</span>
<p>I want to have data from select here: "{{selectedFruit}}"</p>
<child :options="options" v-model="selectedFruit"></child>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</body>
</html>
But my problem is now how to return data from child to parent component using v-model on child component.
(I know I could emit data from child component and do:
<custom-select :options="someOptions" #selected="setSelectedOption"/>
but I need it to be reusable and writing more and more methods to retrieve data from every select in parent component is not exactly how it should work I think.)
Also I need to have an entire object returned, not only ID. (that's why i've got :value="option")
Any ideas?
As Vue Guide said:
v-model is essentially syntax sugar for updating data on user input
events, plus special care for some edge cases.
The syntax sugar will be like:
the directive=v-model will bind value, then listen input event to make change like v-bind:value="val" v-on:input="val = $event.target.value"
So for your use case, you need to create one prop=value, then emit the selected option with event=input.
Like below demo (bind/emit the whole option object):
Vue.config.productionTip = false
Vue.component('child', {
template: `<div class="component-container" #click="showOptions = !showOptions">
<div class="component__select">
<span class="component__select--name">{{value ? value.name : 'Select Fruit'}}</span>
<span class="c-arrow-down" v-if="!showOptions"></span>
<span class="c-arrow-up" v-if="showOptions"></span>
</div>
<ul class="component__select-options" v-if="showOptions" >
<li class="select--option" v-for="option in options" #click="selectOption(option)">
<label> <input type="checkbox" :value="option"/> {{option.name}}</label>
</li>
</ul>
</div>`,
methods: {
selectOption(option) {
this.$emit('input', option)
}
},
data: () => ({
showOptions: false
}),
props: ['options', 'value']
});
var vm = new Vue({
el: '#app',
data: () => ({
options: [
{id: 0, name: 'Apple'},
{id: 1, name: 'Banana'},
{id: 2, name: 'Orange'},
{id: 2, name: 'Strawberry'},
],
selectedFruit: ''
}),
})
.component__select {
height: 38px;
background-color: #F5F7FA;
border: 1px solid #dddddd;
line-height: 38px;
display: grid;
max-width: 500px;
grid-template-columns: 10fr 1fr;
}
.component__select--name {
font-size: 0.8rem;
padding: 0 0 0 25px;
cursor: pointer;
}
.c-arrow-down {
justify-self: end;
}
.component__select-options {
max-height: 180px;
border: 1px solid #dddddd;
border-top: none;
overflow: auto;
position: absolute;
z-index: 1500;
max-width: 500px;
width: 500px;
margin: 0;
padding: 0;
}
.select--option {
height: 35px;
display: grid;
align-content: center;
padding: 0 0 0 25px;
background-color: #f5f5fa;
border-bottom: 1px solid #dddddd;
}
.select--option:last-child {
border-bottom: none;
}
.select--option:nth-child(2n) {
background-color: #ffffff;
}
.select--option input{
display: none;
}
.single-option {
height: 55px;
background-color: #2595ec;
font-size: 0.8rem;
border: 1px solid red;
}
.cust-sel {
width: 200px;
height: 38px;
background-color: #f5f5fa;
border: 1px solid #dddddd;
}
.cust-sel:focus {
outline-width: 0;
}
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<span> This is parent component</span>
<p>I want to have data from select here: "{{selectedFruit}}"</p>
<child :options="options" v-model="selectedFruit"></child>
</div>
When using v-model on custom component all you need is to declare a prop named 'value' and when you need the component to chance it emit an 'input' event.
Something like this:
<template>
<form #submit.prevent="$emit('onSearch',val)" class="form-perfil">
<div class="form-group col-md-12">
<input v-model="val" #input="$emit('input',val)"
placeholder="filtrar resultados" class="form-control">
</div>
</form>
</template>
<script>
module.exports = {
name: "CaixaFiltro",
props: ["value"],
data: _ => ({ val: "" }),
created() {
this.val = this.value
}
}
</script>
Then you can use (after register the component) it like this:
<caixa-filtro v-model="textoBusca" #onSearch="listar"></caixa-filtro>
You can find more detailed info there:
When we select multiple filter options in React Data Grid, the column headers are getting messed up.
Is there a way to fix this issue so that the selected filter options appear above a line in a
dropdown instead of getting added to column header. The column header size is growing as new filter keys get added to the list.
Reference: https://github.com/adazzle/react-data-grid/issues/885
```
import React from 'react';
var ExecutionEnvironment = require('exenv');
if (ExecutionEnvironment.canUseDOM) {
var ReactDataGrid = require('react-data-grid');
var { Toolbar, Filters: { NumericFilter, AutoCompleteFilter, MultiSelectFilter, SingleSelectFilter }, Data: { Selectors } } = require('react-data-grid-addons');
}
export default class ReactGrid extends React.Component {
constructor(props, context) {
const { Toolbar, Filters: { NumericFilter, AutoCompleteFilter, MultiSelectFilter, SingleSelectFilter }, Data: { Selectors } } = require('react-data-grid-addons');
super(props, context);
this._columns = [
{
key: 'prd_family_name',
name: 'Product Family Name',
width: 190,
filterable: true,
filterRenderer: MultiSelectFilter,
sortable: true
},
{
key: 'prd_family_mkt',
name: 'Market',
width: 70,
filterable: true,
filterRenderer: AutoCompleteFilter,
sortable: true
}
];
console.log(`gridvalue${this.props.gridValues}`);
this.state = { filters: {} };
}
componentWillReceiveProps(nextProps) {
this.setState({ rows: nextProps.gridValues });
}
getRandomDate = (start, end) => new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())).toLocaleDateString();
rowGetter = index => Selectors.getRows(this.state)[index];
rowsCount = () => Selectors.getRows(this.state).length;
handleFilterChange = (filter) => {
const newFilters = Object.assign({}, this.state.filters);
if (filter.filterTerm) {
newFilters[filter.column.key] = filter;
} else {
delete newFilters[filter.column.key];
}
this.setState({ filters: newFilters });
};
getValidFilterValues = (columnId) => {
const values = this.state.rows.map(r => r[columnId]);
return values.filter((item, i, a) => i === a.indexOf(item));
};
handleOnClearFilters = () => {
this.setState({ filters: {} });
};
handleGridSort = (sortColumn, sortDirection) => {
const comparer = (a, b) => {
if (sortDirection === 'ASC') {
return (a[sortColumn] > b[sortColumn]) ? 1 : -1;
} else if (sortDirection === 'DESC') {
return (a[sortColumn] < b[sortColumn]) ? 1 : -1;
}
};
const rows = sortDirection === 'NONE' ? this.state.originalRows.slice(0) : this.state.rows.sort(comparer);
this.setState({ rows });
};
render() {
return (
<ReactDataGrid
enableCellSelect
columns={this._columns}
rowGetter={this.rowGetter}
rowsCount={this.rowsCount()}
onGridSort={this.handleGridSort}
minHeight={450}
toolbar={<Toolbar enableFilter />}
onAddFilter={this.handleFilterChange}
getValidFilterValues={this.getValidFilterValues}
onClearFilters={this.handleOnClearFilters}
/>);
}
}
```
The above piece of code is being used for rendering the data grid.
I saw the exact same issue. I've sorted a bunch of CSS that breaks the filter selects out of the header when you mouse over.
When you mouse out, it goes back to horizontal layout with the overflow clipped. Not 100% perfect, but it seems to do the job quite well for me.
You'll need to adjust a few values in the CSS to work with your font / sizes, but that should be trivial.
.react-grid-Container
{
font-size : 10px;
font-family: Roboto Mono, sans-serif;
}
div.react-grid-HeaderCell .input-sm {
height: 30px;
padding: 5px 10px;
font-size: 12px;
line-height: 1.5;
border-radius: 3px;
max-width : 100%;
border : 1px solid #ddd;
}
div.react-grid-HeaderCell
{
font-family: Raleway, sans-serif;
font-weight: 700;
font-size: 11px;
}
div.react-grid-HeaderCell:hover
{
z-index:9999;
overflow:visible;
}
div.react-grid-HeaderCell:hover input:not(.Select-input)
{
max-width: 250px;
width: 250px;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
div.react-grid-HeaderCell div.input-sm
{
display : none;
}
div.react-grid-HeaderCell:hover .Select div.Select-Value
{
clear:both;
}
div.react-grid-HeaderCell .Select-control
{
width: 100%;
}
div.react-grid-HeaderCell:hover .Select .Select-control,
div.react-grid-HeaderCell:hover .Select .Select-menu-outer
{
width: max-content;
min-width: 250px;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
div.react-grid-HeaderCell:hover .Select .Select-control .Select-value
{
float: left;
clear: left;
margin-right : 20px;
}
div.react-grid-HeaderCell:hover .Select .Select-multi-value-wrapper
{
padding : 5px;
padding-top : 40px;
}
div.react-grid-HeaderCell:hover .Select div.Select-input
{
position : absolute;
left : 0;
width:max-content;
min-width : 220px;
margin-top : -35px;
margin-right : 20px;
border : 1px solid #ddd;
padding-right: 5px;
}
div.react-grid-HeaderCell:hover .Select--multi .Select-arrow-zone,
div.react-grid-HeaderCell:hover .Select--multi .Select-clear-zone
{
margin-left: 5px;
position : absolute;
}
div.react-grid-HeaderCell:hover .Select--multi .Select-arrow-zone
{
right : -3px;
top : 7px;
}
div.react-grid-HeaderCell:hover .Select--multi .Select-clear-zone
{
right : 3px;
bottom : 3px;
}
div.react-grid-HeaderCell:hover .Select--multi .Select-control .Select-value
{
max-height : 22px;
}