Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(DataTable): prevent rowClick trigger when button clicked #6544

Merged
merged 3 commits into from
Oct 16, 2024

Conversation

KumJungMin
Copy link
Contributor

@KumJungMin KumJungMin commented Oct 9, 2024

Defect Fixes


How To Resolve

Cause

  • I changed event.target to event.currentTarget to fix the issue from DataTable: Cannot set properties of null (setting 'tabIndex') #6323.
  • However, after this change, clicking a button inside a row in the DataTable triggers the row click event.
  • The issue is that the isClickable argument is using event.currentTarget, so even if a button is clicked, the argument is still set to the row.
스크린샷 2024-10-09 오후 1 40 00



Solution

const targetRow = event.currentTarget?.closest('tr[data-p-selectable-row="true"]');

Result

  • Case 1: Clicking a button inside the row does not trigger the row-clicked event.
sample code
  <template>
      <div class="card">
          <DataTable v-model:selection="selectedProduct" :value="products" selectionMode="single" dataKey="id" :metaKeySelection="false" @rowSelect="onRowSelect" @rowUnselect="onRowUnselect" tableStyle="min-width: 50rem">
              <template #header>
                  <div class="flex flex-wrap items-center justify-between gap-2">
                      <span class="text-xl font-bold">Products</span>
                  </div>
              </template>
              <Column field="name" header="Name"></Column>
              <Column header="Image">
                  <template #body="slotProps">
                      <button icon="pi pi-refresh" rounded raised @click="buttonClicked">button</button>
                  </template>
              </Column>
              <Column field="price" header="Price">
                  <template #body="slotProps">
                      {{ formatCurrency(slotProps.data.price) }}
                  </template>
              </Column>
              <Column field="category" header="Category"></Column>
              <Column field="rating" header="Reviews">
                  <template #body="slotProps">
                      <Rating :modelValue="slotProps.data.rating" readonly />
                  </template>
              </Column>
              <Column header="Status">
                  <template #body="slotProps">
                      <Tag :value="slotProps.data.inventoryStatus" :severity="getSeverity(slotProps.data)" />
                  </template>
              </Column>
              <template #footer> In total there are {{ products ? products.length : 0 }} products. </template>
          </DataTable>
          <Toast />
      </div>
  </template>
  
  <script setup>
  import { ProductService } from '@/service/ProductService';
  import { useToast } from 'primevue/usetoast';
  import { onMounted, ref } from 'vue';
  
  onMounted(() => {
      ProductService.getProductsMini().then((data) => (products.value = data));
  });
  
  const products = ref();
  
  const formatCurrency = (value) => {
      return value.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
  };
  
  const getSeverity = (product) => {
      switch (product.inventoryStatus) {
          case 'INSTOCK':
              return 'success';
  
          case 'LOWSTOCK':
              return 'warn';
  
          case 'OUTOFSTOCK':
              return 'danger';
  
          default:
              return null;
      }
  };
  
  const selectedProduct = ref();
  const toast = useToast();
  
  const onRowSelect = (event) => {
      toast.add({
          severity: 'info',
          summary: 'Product Selected',
          detail: 'Name: ' + event.data.name,
          life: 3000
      });
  };
  
  const onRowUnselect = (event) => {
      toast.add({
          severity: 'warn',
          summary: 'Product Unselected',
          detail: 'Name: ' + event.data.name,
          life: 3000
      });
  };
  
  const buttonClicked = () => {
      alert('button click!');
  };
  </script>
resul1.mov

  • Case 2: Clicking text inside the row does not cause a null error.
sample code
  <template>
      <div class="card">
          <DataTable
              :value="products"
              editMode="cell"
              selectionMode="single"
              @cell-edit-complete="onCellEditComplete"
              :pt="{
                  table: { style: 'min-width: 50rem' },
                  column: {
                      bodycell: ({ state }) => ({
                          class: [{ 'pt-0 pb-0': state['d_editing'] }]
                      })
                  }
              }"
          >
              <Column v-for="col of columns" :key="col.field" :field="col.field" :header="col.header" style="width: 25%">
                  <template #body="{ data, field }">
                      <div>
                          {{ field === 'price' ? formatCurrency(data[field]) : data[field] }}
                      </div>
                  </template>
                  <template #editor="{ data, field }">
                      <div>{{ data[field] }} 1</div>
                  </template>
              </Column>
          </DataTable>
      </div>
  </template>
  
  <script setup>
  import { ProductService } from '@/service/ProductService';
  import { onMounted, ref } from 'vue';
  
  const products = ref();
  const columns = ref([
      { field: 'code', header: 'Code' },
      { field: 'name', header: 'Name' },
      { field: 'quantity', header: 'Quantity' },
      { field: 'price', header: 'Price' }
  ]);
  
  onMounted(() => {
      ProductService.getProductsMini().then((data) => (products.value = data));
  });
  
  const onCellEditComplete = (event) => {
      let { data, newValue, field } = event;
  
      switch (field) {
          case 'quantity':
          case 'price':
              if (isPositiveInteger(newValue)) data[field] = newValue;
              else event.preventDefault();
              break;
  
          default:
              if (newValue.trim().length > 0) data[field] = newValue;
              else event.preventDefault();
              break;
      }
  };
  
  const isPositiveInteger = (val) => {
      let str = String(val);
  
      str = str.trim();
  
      if (!str) {
          return false;
      }
  
      str = str.replace(/^0+/, '') || '0';
      var n = Math.floor(Number(str));
  
      return n !== Infinity && String(n) === str && n >= 0;
  };
  
  const formatCurrency = (value) => {
      return new Intl.NumberFormat('en-US', {
          style: 'currency',
          currency: 'USD'
      }).format(value);
  };
  </script>
2024-10-09.1.56.07.mov

Copy link

vercel bot commented Oct 9, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

2 Skipped Deployments
Name Status Preview Comments Updated (UTC)
primevue ⬜️ Ignored (Inspect) Visit Preview Oct 9, 2024 5:34am
primevue-v3 ⬜️ Ignored (Inspect) Visit Preview Oct 9, 2024 5:34am

@KumJungMin KumJungMin marked this pull request as ready for review October 9, 2024 04:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

DataTable - button click in a cell propagates to row-click
2 participants