Skip to content

BsModel

A built-in data model class for working with entity object and remote API.

Overview

Vue MDBootstrap provides built-in data model class for working with entity object and remote API. BsModel represents an entity object that your application manages. For example, one might define a model for Users, Products, Cars, or other real-world object that we want to model in the system. Models also can be used for binding data by most of the input control components.

Usage Example

ts
import { BsModel } from "vue-mdbootstrap";

const user = new BsModel({
  uid: null,
  username: null,
  displayName: null,
  email: null,
  phoneNumber: null,
  enabled: true
});

// assign a field with spesific value
user.username = 'John Doe';

// assign all fields value
user.assignValues({
  uid: 123,
  username: 'john.doe',
  displayName: 'John Doe',
  email: 'john@example.com',
  phoneNumber: '061-430940',
  enabled: true
});

Working with REST API

Sometimes we want to work with RESTful backend to load and save data. We can achieve this by configuring the RestProxy when creating the Model instance.

ts
import { BsModel } from "vue-mdbootstrap";

const user = new BsModel({
  schema: {
    uid: null,
    username: null,
    displayName: null,
    email: null,
    phoneNumber: null,
    enabled: true,
  },
  proxy: {
    // do not change text: `{id}`
    save: { url: './api/users', method: 'post' },
    update: { url: './api/users/{id}', method: 'patch' },
    delete: { url: './api/users/{id}', method: 'delete' },
    fetch: './api/users/{id}',
  }
}, null, 'uid');

Loading Data

Data can be load in the following ways:

ts
user.fetch(123)
    .catch(function (error) {
      console.log(error);
    });

Saving Data

After assigned all fields value, data can be save as new record to the RESTful backend in the following ways:

ts
user.save()
    .catch(function (error) {
      console.log(error);
    });

And to perform update via RESTful backend can be done in the following ways:

ts
user.update()
    .catch(function (error) {
      console.log(error);
    });

CSRF Token

If the RESTful backend needs CSRF token within the POST, PATCH, PUT or DELETE request, we can accomplish it with :

ts
import { BsModel } from "vue-mdbootstrap";

const user = new BsModel({
  schema: {
    uid: null,
    username: null,
    displayName: null,
    email: null,
    phoneNumber: null,
    enabled: true,
  },
  proxy: {
    // do not change text: '{id}'
    save: { url: './api/users', method: 'post' },
    update: { url: './api/users/{id}', method: 'patch' },
    delete: { url: './api/users/{id}', method: 'delete' },
    fetch: './api/users/{id}',
  },
  csrfConfig: {
    // do not change text: '{name}'
    url: './api/token/{name}',
    // 'token_name' is a keyword to identify the token. 
    // This keyword is used by the RESTful backend to create the CSRF token.
    tokenName: 'token_name', 
    dataField: 'value',
    // If 'suffix' is 'true' then the keyword will become: 'token_name-create', 'token_name-update', 'token_name-delete'
    // when perfoming HTTP request to save, update or delete record.
    suffix: false,
  }
}, null, 'uid');

Working with Virtual Fields

Sometimes RESTful backend delivers a nested object, but we want our Model represents a simple object Model. We can solve this situation using virtual fields and combine them with onAfterFetch event which is triggered after successfully loading or saving the object Model.

ts
import { BsModel } from "vue-mdbootstrap";
import type { TModelOptions, TRecord } from 'vue-mdbootstrap';

export class ActivityModel extends BsModel{
  constructor() {
    const model: TModelOptions = {
      schema: {
        uid: null,
        code: null,
        status: null,
        title: null,
        periode: null,
        year: null,
      },
      proxy: {
        save: { url: './api/activity', method: 'post' },
        update: { url: './api/activity/{id}', method: 'patch' },
        delete: { url: './api/activity/{id}', method: 'delete' },
        fetch: './api/activity/{id}',
      }
    };

    super(model, null, 'uid');
    // below is the virtual fields
    this.set(this, 'budget', null);
    this.set(this, 'realisasi', null);
    this.set(this, 'programId', null);
    this.set(this, 'programName', null);
    this.set(this, 'batchId', null);
    this.set(this, 'batchName', null);
  }

  // automatically called when fetching from remote API
  onAfterFetch(data: TRecord): void {
    // assigns values to the virtual fields
    this.set(this, 'budget', data.budget);
    this.set(this, 'realisasi', data.realisasi);

    if (data.program) {
      this.set(this, 'programId', data.program.id);
      this.set(this, 'programName', data.program.title);
    }
    if (data.batch) {
      this.set(this, 'batchId', data.batch.id);
      this.set(this, 'batchName', data.batch.title);
    }
  }
};
ts
import { ActivityModel } from "./ActivityModel";

const myModel = new ActivityModel();
myModel.fetch(123)
       .catch(function (error) {
          console.log(error);
       });

Working with Nested Fields

There are use case when RESTful backend delivers a nested object and we want our Models to keep track those nested objects. But when performing save or update, we want to ignore those nested objects and some unneeded fields.

ts
import { BsModel } from "vue-mdbootstrap";
import type { TModelOptions, TRecord } from 'vue-mdbootstrap';
import type { ISupplier } from './Supplier';

export class InventoryObat extends BsModel {
  constructor() {
    const model: TModelOptions = {
      schema: {
        id: null,
        supplier: null,   // nested object, ignore when performing save/update
        supplierId: null, // ignore when performing save/update
        tanggal: null,
        invNum: null,     // ignore when performing save/update
        kodeObat: null,
        namaObat: null,
        satuan: null,
        jenisObat: null,
        kadaluarsa: null,
        jumlahMasuk: null,
        hargaDasar: null,
        hargaObat: null,
        jatuhTempo: null,
        jumlahTerhutang: null, // ignore when performing save/update
        jumlahBayar: null,
        lunas: false,
        catatan: null,
        sisaStok: null,  // ignore when performing save/update
        createdAt: null, // ignore when performing save/update
      },
      proxy: {
        fetch: './inventory/obat/{id}',
        delete: './inventory/obat/{id}',
        save: { url: './inventory/obat/masuk', method: 'post' },
        update: { url: './inventory/obat/update/{id}', method: 'patch' },
      },
    };

    super(model, null, 'id', 'content');
  }

  onAfterFetch(data: TRecord): void {
    this.set('supplierId', (data.supplier as ISupplier)?.supplierId);
  }

  // this method is needed to convert our data to simple object
  // and automatically called when performing 'save' or 'update'
  toObject(): TRecord {
    return {
      id: this.get('id'),
      supplier: this.get('supplierId'),
      tanggal: this.get('tanggal'),
      kodeObat: this.get('kodeObat'),
      namaObat: this.get('namaObat'),
      satuan: this.get('satuan'),
      jenisObat: this.get('jenisObat'),
      kadaluarsa: this.get('kadaluarsa'),
      jumlahMasuk: this.get('jumlahMasuk'),
      hargaDasar: this.get('hargaDasar'),
      hargaObat: this.get('hargaObat'),
      jatuhTempo: this.get('jatuhTempo'),
      jumlahBayar: this.get('jumlahBayar'),
      lunas: this.get('lunas'),
      catatan: this.get('catatan'),
    };
  }
}
ts
import { BsModel } from "vue-mdbootstrap";
import type { TModelOptions } from 'vue-mdbootstrap';

export class Supplier extends BsModel {
  constructor() {
    const schema: TModelOptions = {
      schema: {
        supplierId: null,
        companyName: null,
        officePhone: null,
        contactPerson: null,
        contactPhone: null,
        department: null,
      },
      proxy: {
        fetch: './inventory/suppliers/{id}',
        delete: './inventory/suppliers/{id}',
        save: { url: './inventory/suppliers', method: 'post' },
        update: { url: './inventory/suppliers/{id}', method: 'patch' },
      },
    };

    super(schema, adapter, 'supplierId', 'content');
  }
}

export declare interface ISupplier extends Supplier {
  supplierId?: string;
  companyName?: string;
  officePhone?: string;
  contactPerson?: string;
  contactPhone?: string;
  department?: string;
}

API Reference

Released under the BSD-3-Clause License.