Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using it's URL and the proper URL extention.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <!--
  Forked from:
  https://quasar.dev/vue-components/table#Example--Custom-top-with-add%2Fremove-row
-->
<div id="q-app">
  <div class="q-pa-md">
    <!-- selectedRows: {{ selectedRows }} -->
      <q-table
        ref="myTable"
        title="'Treats' QTable with multiple selection, multiple header, and extra 'virtual' column for sorting on selected/deselected rows "
        :data="data"
        :columns="columns"
        row-key="id"
        :filter="filter"
        :loading="loading"
        separator="cell"
        selection="multiple"
        :selected.sync="selectedRows"
      >
        <!-- Added selection="multiple" above -->
        <!-- Added :selected.sync above -->
        <template v-slot:header="props">
          <q-tr>
            <q-th>Cols: {{props.cols.length}} </q-th> 
            <!-- Added for proper column alignment -->
            <q-th colspan="1">"Virtual" column for sorting on multiple (de-)selection</q-th>
            <q-th colspan="2">Group 1</q-th>
            <q-th colspan="3">Group 2</q-th>
            <q-th colspan="2">Group 3</q-th>
          </q-tr>
          <q-tr :props="props">
            <q-th> 
              <!-- 
              Added the following <q-th> with a checkbox 
              for "select/deselect all":
              -->
              <q-checkbox v-model="props.selected" dense />
              <!-- 
              Sorting on selected/deselected possible thru 
              new virtual column right to this column
              -->
            </q-th> 
            <q-th v-for="col in props.cols" :key="col.name" :props="props">
              {{ col.label }}
            </q-th>
          </q-tr>
        </template>
      </q-table>
    <br />
    <q-btn @click="triggerSortOnVirtualColumn()"label="Sort table on (de-)selection" no-caps color="primary"> </q-btn>
    </div>
</div>
              
            
!

CSS

              
                
              
            
!

JS

              
                new Vue({
  el: '#q-app',
  data () {
    return {
      loading: false,
      filter: '',
      rowCount: 10,
      selectedRows: [],
      columns: [
        { // "Virtual" column to sort on selected/deselected rows
          name: 'sortSelected',
          required: true,
          label: 'Sort on (de-)selection thru button below or use sort icon here >>>',
          align: 'left',
          sortable: true,
          // Function to set boolean value indicating whether row is selected or not:
          field: row => this.setRowSelected(row), 
          // Sort function for Boolean values:
          sort: (a, b, rowa, rowb) => (a===b) ? 0 : a ? -1 : 1,
        },
        {
          name: 'desc',
          required: true,
          label: 'Dessert (100g serving)',
          align: 'left',
          // Caution: field: row => row.name in next line maps this col to the prop
          // named "name" in actual table data, not "desc", which is the col name!!!
          field: row => row.name,
          format: val => `${val}`,
          sortable: true,
        },
        { name: 'calories', align: 'center', label: 'Calories', field: 'calories', sortable: true },
        { name: 'fat', label: 'Fat (g)', field: 'fat', sortable: true },
        { name: 'carbs', label: 'Carbs (g)', field: 'carbs' },
        { name: 'protein', label: 'Protein (g)', field: 'protein' },
        { name: 'sodium', label: 'Sodium (mg)', field: 'sodium' },
        { name: 'calcium', label: 'Calcium (%)', field: 'calcium', sortable: true, sort: (a, b) => parseInt(a, 10) - parseInt(b, 10) },
        { name: 'iron', label: 'Iron (%)', field: 'iron', sortable: true, sort: (a, b) => parseInt(a, 10) - parseInt(b, 10) }
      ],
      data: [
        {
          id: 1,
          name: 'Frozen Yogurt', // this prop gets mapped to 
          // "desc" column!
          sortSelected: false, // Needs initialization, otherwise
          // errors!
          calories: 159,
          fat: 6.0,
          carbs: 24,
          protein: 4.0,
          sodium: 87,
          calcium: '14%',
          iron: '1%'
        },
        {
          id: 2,
          name: 'Ice cream sandwich',
          sortSelected: false, // Needs initialization!
          calories: 237,
          fat: 9.0,
          carbs: 37,
          protein: 4.3,
          sodium: 129,
          calcium: '8%',
          iron: '1%'
        },
        {
          id: 3,
          name: 'Eclair',
          sortSelected: false, // Needs initialization!
          calories: 262,
          fat: 16.0,
          carbs: 23,
          protein: 6.0,
          sodium: 337,
          calcium: '6%',
          iron: '7%'
        },
        {
          id: 4,
          name: 'Cupcake',
          sortSelected: false, // Needs initialization!
          calories: 305,
          fat: 3.7,
          carbs: 67,
          protein: 4.3,
          sodium: 413,
          calcium: '3%',
          iron: '8%'
        },
        {
          id: 5,
          name: 'Gingerbread',
          sortSelected: false, // Needs initialization!
          calories: 356,
          fat: 16.0,
          carbs: 49,
          protein: 3.9,
          sodium: 327,
          calcium: '7%',
          iron: '16%'
        },
        {
          id: 6,
          name: 'Jelly bean',
          sortSelected: false, // Needs initialization1
          calories: 375,
          fat: 0.0,
          carbs: 94,
          protein: 0.0,
          sodium: 50,
          calcium: '0%',
          iron: '0%'
        },
        {
          id: 7,
          name: 'Lollipop',
          sortSelected: false, // Needs initialization!
          calories: 392,
          fat: 0.2,
          carbs: 98,
          protein: 0,
          sodium: 38,
          calcium: '0%',
          iron: '2%'
        },
        {
          id: 8,
          name: 'Honeycomb',
          sortSelected: false, // Needs initialization!
          calories: 408,
          fat: 3.2,
          carbs: 87,
          protein: 6.5,
          sodium: 562,
          calcium: '0%',
          iron: '45%'
        },
        {
          id: 9,
          name: 'Donut',
          sortSelected: false, // Needs initialization!
          calories: 452,
          fat: 25.0,
          carbs: 51,
          protein: 4.9,
          sodium: 326,
          calcium: '2%',
          iron: '22%'
        },
        {
          id: 10,
          name: 'KitKat',
          sortSelected: false, // Needs initialization!
          calories: 518,
          fat: 26.0,
          carbs: 65,
          protein: 7,
          sodium: 54,
          calcium: '12%',
          iron: '6%'
        }
      ],
    }
  },
  
  methods: {
    // emulate fetching data from server
    addRow () {
      this.loading = true
      setTimeout(() => {
        const
          index = Math.floor(Math.random() * (this.data.length + 1)),
          row = this.original[Math.floor(Math.random() * this.original.length)]
        if (this.data.length === 0) {
          this.rowCount = 0
        }
        row.id = ++this.rowCount
        const addRow = { ...row } // extend({}, row, { name: `${row.name} (${row.__count})` })
        this.data = [...this.data.slice(0, index), addRow, ...this.data.slice(index)]
        this.loading = false
      }, 500)
    },
    removeRow () {
      this.loading = true
      setTimeout(() => {
        const index = Math.floor(Math.random() * this.data.length)
        this.data = [...this.data.slice(0, index), ...this.data.slice(index + 1)]
        this.loading = false
      }, 500)
    },
    setRowSelected: function(row) {
       // Function to set boolean value of the virtual column based
       // on whether its row is selected or not.
       // Can't be computed:, since it needs argument row?!?
       
       // console.log("In setRowSelected");
       // console.log("selectedRows:", this.selectedRows);
       if (this.selectedRows.length > 0) {
         // Use QTable API to determine if row is selected or not:
         let result = this.$refs.myTable.isRowSelected(row.id);
         // Alternative code, works as well, but less performant:
         // let result = this.selectedRows.some ( selRow => selRow['id'] === row.id );
         // Caution: let result = this.selectedRows.includes(row.id); does NOT do the job to find the id, 
         // since selectedRows is an array of objects: [ {id: 1, ...}, ...]
         // Using "some" is the right solution for it, see
         // https://stackoverflow.com/questions/8217419/how-to-determine-if-javascript-array-contains-an-object-with-an-attribute-that-e 
         console.log("row.name/id/result:" , row.name+"/"+row.id+"/"+result);
         return result; 
       } else return false;
     },
     triggerSortOnVirtualColumn: function(){
       // Method to trigger sorting on (de-)selection using QTable API
       this.$refs.myTable.sort("sortSelected"); // column name as parameter
     }
  },
})
              
            
!
999px

Console