Skip to content
Augustin Šulc edited this page Mar 25, 2022 · 12 revisions
logo

Frui.ts is a frontend framework using MVVM design pattern for clean separation of concerns and long-term maintainability.

Frui.ts is family of indenpendent packages witch toghether creates full MVVM framework. Packages could be also used alone, or some parts could be replaced by another library of your choise.

Friu-ts-packages-structure

It allows ViewModel-first approach, which enables automated testing of complex workflows on the ViewModel level with no need to simulate actual user interaction with the UI.

This framework is designed to support both small and large applications with SOLID codebase. It is built on top of the React library, using MobX and written in the modern TypeScript language.

Why should you use Frui.ts?

Because you can write this:

model.ts - define your model or entities, define validation rules

interface ICustomer {
  id: number;
  firstName: string;
  lastName: string;
  categoryId: number;
}

const CustomerValidationRules = {
  firstName: { maxLength: 35 },
  lastName: { required: true, maxLength: 35 },
  categoryId: { required: true },
};

viewModel.ts - write only the code that actually makes sense

class CustomerViewModel {
  categories = [
    { id: 1, name: "Premium" },
    { id: 2, name: "Standard" },
  ];

  @observable customer: ICustomer;

  constructor(private customerId, private repository: ICustomersRepository) {}

  async onInitialize() {
    this.customer = await this.repository.loadCustomer(this.customerId);
    attachAutomaticValidator(this.customer, CustomerValidationRules);
  }

  get canSave() {
    return isValid(this.customer);
  }

  @action.bound
  async save() {
    await this.repository.updateCustomer(this.customerId, this.customer);
  }
}

view.tsx - declare how the VM should be presented

const customerView: ViewComponent<CustomerViewModel> = observer(({ vm }) => (
  <form onSubmit={preventDefault(vm.submit)}>
    {/* Note the two-way binding here, autocomplete works for 'target' and 'property' properties */}
    <Input target={vm.customer} property="firstName" label="First name" />
    <Input target={vm.customer} property="lastName" label="Last name" />
    <Select
      target={vm.customer}
      property="categoryId"
      label="Category"
      items={vm.categories}
      keyProperty="id"
      textProperty="name"
      mode="key"
    />

    {/* The button will be enabled/disabled as the input values change */}
    <Button type="submit" disabled={!vm.canSave}>
      Save
    </Button>
  </form>
));

registerView(customerView, CustomerViewModel);

Examples