Using slots to render slot content for child component

For sure - its still very early in development, so its not super usable (yet) but here’s the full details:

import { BaseModel } from 'types/BaseModel';
import { MdcSnackbarService } from '@aurelia-mdc-web/snackbar';
import { BaseService } from 'services/api/BaseService';
import { DialogService } from 'aurelia-dialog';
import { bindable, inject } from 'aurelia-framework';
import { ValidationController } from 'aurelia-validation';
import { Field } from 'types/Field';

import debug from 'debug';
const logger = debug('app/components/admin/admin-table');

function titleize(key: string) {
    const split = key
        .replace(/_/g, ' ')
        .replace(/([A-Z])/g, " $1");
    return split.charAt(0).toUpperCase() + split.slice(1);
}

@inject(
    DialogService,
    MdcSnackbarService,
)
export class AdminTable {
    @bindable _fields: Field[]
    @bindable RowType: any;
    @bindable service: BaseService;
    @bindable DialogViewModel: any;
    @bindable controller: ValidationController;

    @bindable rows?: BaseModel[];    
    @bindable params = {};
    @bindable perPage = 10;
    @bindable page = 1;
    @bindable total = 0;

    @bindable title: string;

    constructor(
        protected dialogService: DialogService,
        protected snackbarService: MdcSnackbarService,
    ) {
        
    }

    attached() {
        this.fetchRows();
    }

    perPageChanged(val) {
        this.params = {
            ...this.params,
            $limit: val,
        }
    }

    pageChanged(val) {
        this.params = {
            ...this.params,
            $skip: (val-1) * this.perPage,
        }
    }

    paramsChanged() {
        this.fetchRows();
    }

    fetchRows() {
        if (!this.service) {
            logger('Missing required property: service')
            return;
        }
        return this.service.service.find({ query: this.params })
            .then(result => {
                Object.assign(this, {
                    total: result.total,
                    rows: result.data.map(row => new this.RowType(row)),
                })

                if (!this.fields && result.data.length) {
                    this.fields = this.getFieldsFromRow(result.data[0])
                }
            });
    }

    set fields(fields: Field[]) {
        this._fields = fields;
    }
    get fields() : Field[] {
        if (!this._fields && this.rows?.length) {
            return this.getFieldsFromRow(this.rows[0])
        }
        return this._fields || [];
    }

    getFieldsFromRow(row: BaseModel) {
        return Object.keys(row).map(key => ({
            label: titleize(key),
            name: key,
        }))
    }

    create() {
        return this.edit({} as BaseModel)
    }

    edit(item: BaseModel) {
        return this.dialogService.open({
            viewModel: this.DialogViewModel,
            model: {...item},
        }).whenClosed(result => {
            if (result.wasCancelled) {
                return;
            }
            if (item.id) {
                for (let i = 0; i < this.rows.length; i++){
                    if (this.rows[i].id === item.id) {
                        this.rows = this.rows.map(row => {
                            if (row.id === item.id) {
                                return result.output;
                            }
                            return row;
                        })
                        break;
                    }
                }
            } else {
                this.rows = [
                    ...this.rows,
                    new this.RowType(result.output),
                ]
            }
        })
    }

    getSelectedRows() : BaseModel[] {
        return this.rows.filter(row => !!row.__checked);
    }

    deleteSelected() {
        const selected = this.getSelectedRows()
        if (!selected.length) {
            this.snackbarService.open(`No ${this.title}(s) selected`)
            return;
        }
        return this.delete(selected)
    }

    delete(users: BaseModel[]) {
        return Promise.all(users.map(user => {
            return this.service.service.remove(user.id)
                .then(() => this.params = {...this.params})
        })).then(() => this.snackbarService.open(`${this.title}(s) deleted`))
            .catch(e => {
                logger(e)
                this.snackbarService.open(e);
            })
    }

}
<template>
    <template replaceable part="table">
        <mdc-data-table class="admin">
            <mdc-data-table-header>
                    <mdc-data-table-header-cell>
                        <mdc-checkbox></mdc-checkbox>
                    </mdc-data-table-header-cell>
                    <mdc-data-table-header-cell>Edit</mdc-data-table-header-cell>
                    <mdc-data-table-header-cell repeat.for="field of fields">${field.label}</mdc-data-table-header-cell>
            </mdc-data-table-header>
            <mdc-data-table-content>
                <mdc-data-table-row repeat.for="row of rows">
                        <mdc-data-table-cell>
                            <mdc-checkbox checked.bind="row.__checked"></mdc-checkbox>
                        </mdc-data-table-cell>
                        <mdc-data-table-cell>
                            <button click.delegate="edit(row)" mdc-icon-button icon="edit"></button>
                        </mdc-data-table-cell>
                        <mdc-data-table-cell repeat.for="field of fields">${row[field.name]}</mdc-data-table-cell>
                </mdc-data-table-row>
            </mdc-data-table-content>
        </mdc-data-table>
    </template>
    <div class="admin-actions">
        <button mdc-button type="button" click.delegate="create()">
            <mdc-icon>add</mdc-icon> Create ${title}
        </button>
        <button mdc-button type="button" click.delegate="deleteSelected()">
            <mdc-icon>delete</mdc-icon> Delete Selected
        </button>
    </div>
    <mdc-pagination per-page.two-way="perPage" page.two-way="page" total.bind="total">
    </mdc-pagination>
</template>

Example usage:

<mdc-expandable accordion="admin">
        <div slot="caption">Manage Users</div>

        <admin-table row-type.bind="User" service.bind="usersService" dialog-view-model.bind="UserEditViewModel"
            title="User">
            <template replace-part="table">
                <mdc-data-table class="admin">
                    <mdc-data-table-header>
                        <mdc-data-table-header-cell>
                            <mdc-checkbox></mdc-checkbox>
                        </mdc-data-table-header-cell>

                        <mdc-data-table-header-cell>Edit</mdc-data-table-header-cell>
                        <mdc-data-table-header-cell>Name</mdc-data-table-header-cell>
                        <mdc-data-table-header-cell>Username</mdc-data-table-header-cell>
                        <mdc-data-table-header-cell>Email</mdc-data-table-header-cell>
                        <mdc-data-table-header-cell>Roles</mdc-data-table-header-cell>
                    </mdc-data-table-header>
                    <mdc-data-table-content>
                        <mdc-data-table-row repeat.for="row of rows">

                            <mdc-data-table-cell>
                                <mdc-checkbox checked.bind="row.__checked"></mdc-checkbox>
                            </mdc-data-table-cell>
                            <mdc-data-table-cell>
                                <button click.delegate="edit(row)" mdc-icon-button icon="edit"></button>
                            </mdc-data-table-cell>
                            <mdc-data-table-cell>${row.fullname}</mdc-data-table-cell>
                            <mdc-data-table-cell>${row.username}</mdc-data-table-cell>
                            <mdc-data-table-cell>${row.email}</mdc-data-table-cell>
                            <mdc-data-table-cell>
                                <mdc-form-field repeat.for="role of roles">
                                    <mdc-checkbox checked.to-view="row.hasRole(role)"
                                        change.delegate="updateUserRole(row, role, $event.target.checked)">
                                    </mdc-checkbox>
                                    <label>${role.role_name}</label>
                                </mdc-form-field>
                            </mdc-data-table-cell>
                        </mdc-data-table-row>
                    </mdc-data-table-content>
                </mdc-data-table>
            </template>
        </admin-table>
    </mdc-expandable>
1 Like