Fixed #959 - Add keys to template v-for

pull/973/head
Cagatay Civici 2021-02-10 13:29:37 +03:00
parent 3bd70d34c1
commit 8041a244ab
10 changed files with 58 additions and 173 deletions

View File

@ -1,8 +1,8 @@
<template>
<transition name="p-contextmenusub" @enter="onEnter">
<ul ref="container" :class="containerClass" role="menu" v-if="root ? true : parentActive">
<template v-for="(item, i) of model">
<li role="none" :class="getItemClass(item)" :style="item.style" v-if="visible(item) && !item.separator" :key="item.label + i"
<template v-for="(item, i) of model" :key="item.label + i">
<li role="none" :class="getItemClass(item)" :style="item.style" v-if="visible(item) && !item.separator"
@mouseenter="onItemMouseEnter($event, item)">
<router-link v-if="item.to && !item.disabled" :to="item.to" custom v-slot="{navigate, href}">
<a :href="href" @click="onItemClick($event, item, navigate)" :class="getLinkClass(item)" v-ripple role="menuitem">

View File

@ -1,72 +1,51 @@
<template>
<thead class="p-datatable-thead">
<thead class="p-datatable-thead" role="rowgroup">
<template v-if="!columnGroup">
<tr>
<template v-for="(col,i) of columns">
<th v-if="rowGroupMode !== 'subheader' || (groupRowsBy !== columnProp(col, 'field'))" :tabindex="columnProp(col, 'sortable') ? '0' : null" @keydown="onColumnKeyDown($event, col)"
:key="columnProp(col, 'columnKey')||columnProp(col, 'field')||i" :style="columnProp(col, 'headerStyle')" :class="getColumnHeaderClass(col)"
@click="onColumnHeaderClick($event, col)" @mousedown="onColumnHeaderMouseDown($event, col)"
@dragstart="onColumnHeaderDragStart($event)" @dragover="onColumnHeaderDragOver($event)" @dragleave="onColumnHeaderDragLeave($event)" @drop="onColumnHeaderDrop($event)"
:colspan="columnProp(col, 'colspan')" :rowspan="columnProp(col, 'rowspan')" :aria-sort="getAriaSort(col)">
<span class="p-column-resizer" @mousedown="onColumnResizeStart($event)" v-if="resizableColumns"></span>
<div class="p-column-header-content">
<component :is="col.children.header" :column="col" v-if="col.children && col.children.header"/>
<span class="p-column-title" v-if="columnProp(col, 'header')">{{columnProp(col, 'header')}}</span>
<span v-if="columnProp(col, 'sortable')" :class="getSortableColumnIcon(col)"></span>
<span v-if="isMultiSorted(col)" class="p-sortable-column-badge">{{getMultiSortMetaIndex(col) + 1}}</span>
<DTHeaderCheckbox :checked="allRowsSelected" @change="onHeaderCheckboxChange($event)" :disabled="empty" v-if="columnProp(col, 'selectionMode') ==='multiple' && filterDisplay !== 'row'" />
<DTColumnFilter v-if="filterDisplay === 'menu' && col.children && col.children.filter" :field="columnProp(col, 'filterField')||columnProp(col, 'field')" :type="columnProp(col, 'dataType')" display="menu"
:showMenu="columnProp(col, 'showFilterMenu')" :filterElement="col.children && col.children.filter"
:filterHeaderTemplate="col.children && col.children.filterheader" :filterFooterTemplate="col.children && col.children.filterfooter"
:filterClearTemplate="col.children && col.children.filterclear" :filterApplyTemplate="col.children && col.children.filterapply"
:filters="filters" :filtersStore="filtersStore" @filter-change="$emit('filter-change', $event)" @filter-apply="$emit('filter-apply')" :filterMenuStyle="columnProp(col, 'filterMenuStyle')" :filterMenuClass="columnProp(col, 'filterMenuClass')"
:showOperator="columnProp(col, 'showFilterOperator')" :showClearButton="columnProp(col, 'showClearButton')" :showApplyButton="columnProp(col, 'showApplyButton')"
:showMatchModes="columnProp(col, 'showFilterMatchModes')" :showAddButton="columnProp(col, 'showAddButton')" :matchModeOptions="columnProp(col, 'filterMatchModeOptions')" :maxConstraints="columnProp(col, 'maxConstraints')"
@operator-change="$emit('operator-change',$event)" @matchmode-change="$emit('matchmode-change', $event)" @constraint-add="$emit('constraint-add', $event)" @constraint-remove="$emit('constraint-remove', $event)" @apply-click="$emit('apply-click',$event)"/>
</div>
</th>
<tr role="row">
<template v-for="(col,i) of columns" :key="columnProp(col, 'columnKey')||columnProp(col, 'field')||i">
<DTHeaderCell v-if="rowGroupMode !== 'subheader' || (groupRowsBy !== columnProp(col, 'field'))" :column="col"
@column-click="$emit('column-click', $event)" @column-mousedown="$emit('column-mousedown', $event)"
@column-dragstart="$emit('column-dragstart', $event)" @column-dragover="$emit('column-dragover', $event)" @column-dragleave="$emit('column-dragleave', $event)" @column-drop="$emit('column-drop', $event)"
:resizableColumns="resizableColumns" @column-resizestart="$emit('column-resizestart', $event)"
:sortMode="sortMode" :sortField="sortField" :sortOrder="sortOrder" :multiSortMeta="multiSortMeta"
:allRowsSelected="allRowsSelected" :empty="empty" @checkbox-change="$emit('column-change', $event)"
:filters="filters" :filtersStore="filtersStore" @filter-change="$emit('filter-change', $event)" @filter-apply="$emit('filter-apply')"
@operator-change="$emit('operator-change',$event)" @matchmode-change="$emit('matchmode-change', $event)" @constraint-add="$emit('constraint-add', $event)"
@constraint-remove="$emit('constraint-remove', $event)" @apply-click="$emit('apply-click',$event)"/>
</template>
</tr>
<tr v-if="filterDisplay === 'row'">
<template v-for="(col,i) of columns">
<th v-if="rowGroupMode !== 'subheader' || (groupRowsBy !== columnProp(col, 'field'))" :key="columnProp(col, 'columnKey')||columnProp(col, 'field')||i"
:class="getFilterColumnHeaderClass(col)" :style="columnProp(col, 'filterHeaderStyle')">
<DTColumnFilter v-if="col.children && col.children.filter" :field="columnProp(col, 'filterField')||columnProp(col, 'field')" :type="columnProp(col, 'dataType')" display="row"
:showMenu="columnProp(col, 'showFilterMenu')" :filterElement="col.children && col.children.filter" :filterHeader="col.children && col.children.filterHeader" :filterFooter="col.children && col.children.filterFooter"
:filters="filters" :filtersStore="filtersStore" @filter-change="$emit('filter-change', $event)" @filter-apply="$emit('filter-apply')" :filterMenuStyle="columnProp(col, 'filterMenuStyle')" :filterMenuClass="columnProp(col, 'filterMenuClass')"
:showOperator="columnProp(col, 'showFilterOperator')" :showClearButton="columnProp(col, 'showClearButton')" :showApplyButton="columnProp(col, 'showApplyButton')"
:showMatchModes="columnProp(col, 'showFilterMatchModes')" :showAddButton="columnProp(col, 'showAddButton')" :matchModeOptions="columnProp(col, 'filterMatchModeOptions')" :maxConstraints="columnProp(col, 'maxConstraints')"
@operator-change="$emit('operator-change',$event)" @matchmode-change="$emit('matchmode-change', $event)" @constraint-add="$emit('constraint-add', $event)" @constraint-remove="$emit('constraint-remove', $event)" @apply-click="$emit('apply-click',$event)"/>
<DTHeaderCheckbox :checked="allRowsSelected" @change="onHeaderCheckboxChange($event)" :disabled="empty" v-if="columnProp(col, 'selectionMode')==='multiple'" />
</th>
<tr v-if="filterDisplay === 'row'" role="row">
<template v-for="(col,i) of columns" :key="columnProp(col, 'columnKey')||columnProp(col, 'field')||i">
<DTHeaderCell v-if="rowGroupMode !== 'subheader' || (groupRowsBy !== columnProp(col, 'field'))" :column="col" :filterColumn="true"
:allRowsSelected="allRowsSelected" :empty="empty" @checkbox-change="$emit('checkbox-change', $event)"
:filters="filters" :filtersStore="filtersStore" @filter-change="$emit('filter-change', $event)" @filter-apply="$emit('filter-apply')"
@operator-change="$emit('operator-change',$event)" @matchmode-change="$emit('matchmode-change', $event)" @constraint-add="$emit('constraint-add', $event)"
@constraint-remove="$emit('constraint-remove', $event)" @apply-click="$emit('apply-click',$event)"/>
</template>
</tr>
</template>
<template v-else>
<tr v-for="(row,i) of columnGroup.children.default()" :key="i">
<th v-for="(col,i) of row.children.default()" :key="columnProp(col, 'columnKey')||columnProp(col, 'field')||i" :style="columnProp(col, 'headerStyle')" :class="getColumnHeaderClass(col)" :tabindex="columnProp(col, 'sortable') ? '0' : null"
@click="onColumnHeaderClick($event, col)" @keydown="onColumnKeyDown($event, col)" @dragstart="onColumnHeaderDragStart($event)" @dragover="onColumnHeaderDragOver($event)" @dragleave="onColumnHeaderDragLeave($event)" @drop="onColumnHeaderDrop($event)"
:colspan="columnProp(col, 'colspan')" :rowspan="columnProp(col, 'rowspan')" :aria-sort="getAriaSort(col)">
<component :is="col.children.header" :column="col" v-if="col.children && col.children.header"/>
<span class="p-column-title" v-if="columnProp(col, 'header')">{{columnProp(col, 'header')}}</span>
<span v-if="columnProp(col, 'sortable')" :class="getSortableColumnIcon(col)"></span>
<span v-if="isMultiSorted(col)" class="p-sortable-column-badge">{{getMultiSortMetaIndex(col) + 1}}</span>
<component :is="col.children.filter" :column="col" v-if="col.children && col.children.filter"/>
<DTHeaderCheckbox :checked="allRowsSelected" @change="onHeaderCheckboxChange($event)" :disabled="empty" v-if="columnProp(col, 'selectionMode') ==='multiple'" />
</th>
<tr v-for="(row,i) of columnGroup.children.default()" :key="i" role="row">
<template v-for="(col,j) of row.children.default()" :key="columnProp(col, 'columnKey')||columnProp(col, 'field')||j">
<DTHeaderCell v-if="rowGroupMode !== 'subheader' || (groupRowsBy !== columnProp(col, 'field'))" :column="col"
@column-click="$emit('column-click', $event)" @column-mousedown="$emit('column-mousedown', $event)"
:sortMode="sortMode" :sortField="sortField" :sortOrder="sortOrder" :multiSortMeta="multiSortMeta"
:allRowsSelected="allRowsSelected" :empty="empty" @checkbox-change="$emit('column-change', $event)"
:filters="filters" :filtersStore="filtersStore" @filter-change="$emit('filter-change', $event)" @filter-apply="$emit('filter-apply')"
@operator-change="$emit('operator-change',$event)" @matchmode-change="$emit('matchmode-change', $event)" @constraint-add="$emit('constraint-add', $event)"
@constraint-remove="$emit('constraint-remove', $event)" @apply-click="$emit('apply-click',$event)"/>
</template>
</tr>
</template>
</thead>
</template>
<script>
import {DomHandler} from 'primevue/utils';
import HeaderCheckbox from './HeaderCheckbox.vue';
import ColumnFilter from './ColumnFilter';
import HeaderCell from './HeaderCell';
export default {
emits: ['column-click', 'column-mousedown', 'column-dragstart', 'column-dragover', 'column-dragleave', 'column-drop',
'column-resizestart', 'checkbox-change', 'column-click','filter-change', 'filter-apply',
'column-resizestart', 'checkbox-change', 'filter-change', 'filter-apply',
'operator-change', 'matchmode-change', 'constraint-add', 'constraint-remove', 'filter-clear', 'apply-click'],
props: {
columnGroup: {
@ -113,6 +92,10 @@ export default {
type: Array,
default: null
},
filterDisplay: {
type: String,
default: null
},
filters: {
type: Object,
default: null
@ -120,116 +103,18 @@ export default {
filtersStore: {
type: Object,
default: null
},
filterDisplay: {
type: String,
default: null
}
},
methods: {
columnProp(col, prop) {
return col.props ? ((col.type.props[prop].type === Boolean && col.props[prop] === '') ? true : col.props[prop]) : null;
},
isMultiSorted(column) {
return this.columnProp(column, 'sortable') && this.getMultiSortMetaIndex(column) > -1
},
isColumnSorted(column) {
return this.sortMode === 'single' ? (this.sortField && (this.sortField === this.columnProp(column, 'field') || this.sortField === this.columnProp(column, 'sortField'))) : this.isMultiSorted(column);
},
getColumnHeaderClass(column) {
return [this.columnProp(column, 'headerClass'),
{'p-sortable-column': this.columnProp(column, 'sortable')},
{'p-resizable-column': this.resizableColumns},
{'p-highlight': this.isColumnSorted(column)}
];
},
getFilterColumnHeaderClass(column) {
return ['p-filter-column', this.columnProp(column, 'filterHeaderClass')];
},
getSortableColumnIcon(column) {
let sorted = false;
let sortOrder = null;
if (this.sortMode === 'single') {
sorted = this.sortField && (this.sortField === this.columnProp(column, 'field') || this.sortField === this.columnProp(column, 'sortField'));
sortOrder = sorted ? this.sortOrder: 0;
}
else if (this.sortMode === 'multiple') {
let metaIndex = this.getMultiSortMetaIndex(column);
if (metaIndex > -1) {
sorted = true;
sortOrder = this.multiSortMeta[metaIndex].order;
}
}
return [
'p-sortable-column-icon pi pi-fw', {
'pi-sort-alt': !sorted,
'pi-sort-amount-up-alt': sorted && sortOrder > 0,
'pi-sort-amount-down': sorted && sortOrder < 0
}
];
},
getMultiSortMetaIndex(column) {
let index = -1;
for (let i = 0; i < this.multiSortMeta.length; i++) {
let meta = this.multiSortMeta[i];
if (meta.field === this.columnProp(column, 'field') || meta.field === this.columnProp(column, 'sortField')) {
index = i;
break;
}
}
return index;
},
onColumnHeaderClick(event, col) {
this.$emit('column-click', {originalEvent: event, column: col});
},
onColumnHeaderMouseDown(event, col) {
this.$emit('column-mousedown', {originalEvent: event, column: col});
},
onColumnHeaderDragStart(event) {
this.$emit('column-dragstart', event);
},
onColumnHeaderDragOver(event) {
this.$emit('column-dragover', event);
},
onColumnHeaderDragLeave(event) {
this.$emit('column-dragleave', event);
},
onColumnHeaderDrop(event) {
this.$emit('column-drop', event);
},
onColumnResizeStart(event) {
this.$emit('column-resizestart', event);
},
onHeaderCheckboxChange(event) {
this.$emit('checkbox-change', event);
},
onColumnKeyDown(event, col) {
if (event.which === 13 && event.currentTarget.nodeName === 'TH' && DomHandler.hasClass(event.currentTarget, 'p-sortable-column')) {
this.$emit('column-click', {originalEvent: event, column: col});
}
},
getAriaSort(column) {
if (this.columnProp(column, 'sortable')) {
const sortIcon = this.getSortableColumnIcon(column);
if (sortIcon[1]['pi-sort-amount-down'])
return 'descending';
else if (sortIcon[1]['pi-sort-amount-up-alt'])
return 'ascending';
else
return 'none';
}
else {
return null;
}
}
},
components: {
'DTHeaderCheckbox': HeaderCheckbox,
'DTColumnFilter': ColumnFilter
'DTHeaderCell': HeaderCell
}
}
</script>

View File

@ -1,8 +1,8 @@
<template>
<div :class="containerClass">
<ul class="p-megamenu-root-list" role="menubar">
<template v-for="(category,index) of model">
<li v-if="visible(category)" :key="category.label + '_' + index" :class="getCategoryClass(category)" :style="category.style"
<template v-for="(category,index) of model" :key="category.label + '_' + index">
<li v-if="visible(category)" :class="getCategoryClass(category)" :style="category.style"
@mouseenter="onCategoryMouseEnter($event, category)" role="none">
<router-link v-if="category.to && !category.disabled" :to="category.to" custom v-slot="{navigate, href}">
<a :href="href" :class="getLinkClass(category)" @click="onCategoryClick($event, category, navigate)" @keydown="onCategoryKeydown($event, category)" role="menuitem" v-ripple>
@ -21,8 +21,8 @@
<div v-for="(column,columnIndex) of category.items" :key="category.label + '_column_' + columnIndex" :class="getColumnClassName(category)">
<ul v-for="(submenu,submenuIndex) of column" class="p-megamenu-submenu" :key="submenu.label + '_submenu_' + submenuIndex" role="menu">
<li :class="getSubmenuHeaderClass(submenu)" :style="submenu.style" role="presentation">{{submenu.label}}</li>
<template v-for="(item, i) of submenu.items">
<li role="none" :class="getSubmenuItemClass(item)" :style="item.style" v-if="visible(item) && !item.separator" :key="item.label + i">
<template v-for="(item, i) of submenu.items" :key="item.label + i">
<li role="none" :class="getSubmenuItemClass(item)" :style="item.style" v-if="visible(item) && !item.separator">
<router-link v-if="item.to && !item.disabled" :to="item.to" custom v-slot="{navigate, href}">
<a :href="href" :class="getLinkClass(item)" @click="onLeafClick($event, item, navigate)" role="menuitem" v-ripple>
<span v-if="item.icon" :class="['p-menuitem-icon', item.icon]"></span>

View File

@ -2,11 +2,11 @@
<transition name="p-connected-overlay" @enter="onEnter" @leave="onLeave">
<div :ref="containerRef" :class="containerClass" v-if="popup ? overlayVisible : true">
<ul class="p-menu-list p-reset" role="menu">
<template v-for="(item, i) of model">
<template v-for="(item, i) of model" :key="item.label+i">
<template v-if="item.items && visible(item) && !item.separator">
<li class="p-submenu-header" :key="item.label+i" v-if="item.items">{{item.label}}</li>
<template v-for="(child, j) of item.items">
<Menuitem v-if="visible(child) && !child.separator" :key="child.label + i + j" :item="child" @click="itemClick" />
<li class="p-submenu-header" v-if="item.items">{{item.label}}</li>
<template v-for="(child, j) of item.items" :key="child.label + i + j">
<Menuitem v-if="visible(child) && !child.separator" :item="child" @click="itemClick" />
<li v-else-if="visible(child) && child.separator" :class="['p-menu-separator', child.class]" :style="child.style" :key="'separator' + i + j" role="separator"></li>
</template>
</template>

View File

@ -1,7 +1,7 @@
<template>
<ul :class="containerClass" :role="root ? 'menubar' : 'menu'">
<template v-for="(item, i) of model">
<li role="none" :class="getItemClass(item)" :style="item.style" v-if="visible(item) && !item.separator" :key="item.label + i"
<template v-for="(item, i) of model" :key="item.label + i">
<li role="none" :class="getItemClass(item)" :style="item.style" v-if="visible(item) && !item.separator"
@mouseenter="onItemMouseEnter($event, item)">
<router-link v-if="item.to && !item.disabled" :to="item.to" custom v-slot="{navigate, href}">
<a :href="href" @click="onItemClick($event, item, navigate)" :class="getLinkClass(item)" v-ripple @keydown="onItemKeyDown($event, item)" role="menuitem">

View File

@ -1,7 +1,7 @@
<template>
<div class="p-panelmenu p-component">
<template v-for="(item, index) of model">
<div v-if="visible(item)" :key="item.label + '_' + index" :class="getPanelClass(item)" :style="item.style">
<template v-for="(item, index) of model" :key="item.label + '_' + index">
<div v-if="visible(item)" :class="getPanelClass(item)" :style="item.style">
<div :class="getHeaderClass(item)" :style="item.style">
<a :href="item.url" class="p-panelmenu-header-link" @click="onItemClick($event, item)" :tabindex="item.disabled ? null : '0'"
:aria-expanded="isActive(item)" :id="ariaId +'_header'" :aria-controls="ariaId +'_content'">

View File

@ -1,7 +1,7 @@
<template>
<ul class="p-submenu-list" role="tree">
<template v-for="(item, i) of model">
<li role="none" :class="getItemClass(item)" :style="item.style" v-if="visible(item) && !item.separator" :key="item.label + i">
<template v-for="(item, i) of model" :key="item.label + i">
<li role="none" :class="getItemClass(item)" :style="item.style" v-if="visible(item) && !item.separator">
<router-link v-if="item.to && !item.disabled" :to="item.to" custom v-slot="{navigate, href}">
<a :href="href" :class="getLinkClass(item)" @click="onItemClick($event, item, navigate)" role="treeitem" :aria-expanded="isActive(item)">
<span :class="['p-menuitem-icon', item.icon]"></span>

View File

@ -1,8 +1,8 @@
<template>
<div :id="id" :class="containerClass">
<ul role="tablist">
<template v-for="(item,index) of model">
<li v-if="visible(item)" :key="item.to" :class="getItemClass(item)" :style="item.style" role="tab" :aria-selected="isActive(item)" :aria-expanded="isActive(item)">
<template v-for="(item,index) of model" :key="item.to">
<li v-if="visible(item)" :class="getItemClass(item)" :style="item.style" role="tab" :aria-selected="isActive(item)" :aria-expanded="isActive(item)">
<router-link :to="item.to" v-if="!isItemDisabled(item)" custom v-slot="{navigate, href}">
<a :href="href" class="p-menuitem-link" @click="onItemClick($event, item, navigate)" role="presentation">
<span class="p-steps-number">{{index + 1}}</span>

View File

@ -1,8 +1,8 @@
<template>
<div class="p-tabmenu p-component">
<ul ref="nav" class="p-tabmenu-nav p-reset" role="tablist">
<template v-for="(item,i) of model">
<li :key="item.label + '_' + i" :class="getItemClass(item)" :style="item.style" v-if="visible(item)" role="tab" :aria-selected="isActive(item)" :aria-expanded="isActive(item)">
<template v-for="(item,i) of model" :key="item.label + '_' + i">
<li :class="getItemClass(item)" :style="item.style" v-if="visible(item)" role="tab" :aria-selected="isActive(item)" :aria-expanded="isActive(item)">
<router-link v-if="item.to && !item.disabled" :to="item.to" custom v-slot="{navigate, href}">
<a :href="href" class="p-menuitem-link" @click="onItemClick($event, item, navigate)" role="presentation" v-ripple>
<span :class="getItemIcon(item)" v-if="item.icon"></span>

View File

@ -1,7 +1,7 @@
<template>
<ul ref="element" :class="containerClass" role="'menubar' : 'menu'" aria-orientation="horizontal">
<template v-for="(item, i) of model">
<li :class="getItemClass(item)" :style="item.style" v-if="visible(item) && !item.separator" :key="item.label + i"
<template v-for="(item, i) of model" :key="item.label + i">
<li :class="getItemClass(item)" :style="item.style" v-if="visible(item) && !item.separator"
@mouseenter="onItemMouseEnter($event, item)" role="none">
<router-link v-if="item.to && !item.disabled" :to="item.to" custom v-slot="{navigate, href}">
<a :href="href" @click="onItemClick($event, item, navigate)" :class="getLinkClass(item)" v-ripple @keydown="onItemKeyDown($event, item)" role="menuitem">