Skip to content

SolidLabResearch/solid-sfef-cli

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Solid FE Form CLI


Introduction

The Solid Front End Form CLI is meant to help create forms on Vue/Angular frameworks from SHACL (Shapes Constraint Language) files.

The create command recreates a <form> using the rules from a SHACL file on the framework selected.

The available frameworks are Vue3 and Angular2.

All outputted form components are created without styles.

For both frameworks, all HTML form elements have been implemented. For each form element, all possible attributes and values have been taken into consideration.

But, only the <input> element is considered in the mapping as of now.

Please, check BASIC_TYPE_MAP in constants.ts for a mapping between sh:datatype and basic components and its types.

See Changelog for the current status of the CLI.

---
title: The 'create' diagram flow from .ttl shape to the FE 'form' component
---
flowchart LR
    A(SHACL\n.ttl):::bold
    B(JSONLD)
    C(custom\nJSON)
    D(form\ncomponent):::bold
    E(BasicInput\nBasicFieldset\nBasicButton\nBasicTextarea\nBasicSelect)
    A --> B
    B --> C
    C --> D
    subgraph DE[FE framework]
        D <-. dependecies .-> E
    end
    classDef bold stroke-width:3px
Loading

CLI commands

  • sfef set-custom-css : Set up custom CSS classes to be used in the FE form component.

  • sfef create -f <framework> -s <shape> [-c] : Creates the form (following SHACL shape) and required dependencies according to the selected framework.

  • sfef list-shapes : List of available shapes (SHACL .ttl files).


Install

  • Solid-sfef-cli is a development CLI tool, it is recommended to install it using --global parameter. The following commands can be used:

    npm install @solidlab/solid-sfef-cli --global

    or

    npm i @solidlab/solid-sfef-cli -g
  • It could also be installed locally as a dev dependency using --save-dev parameter. The following commands can be used:

    npm install @solidlab/solid-sfef-cli --save-dev

    or

    npm i @solidlab/solid-sfef-cli -D

Usage and examples

The current section bash commands are under the assumption that the installation was done globally.

If the installation was done locally bash commands should be adapted accordingly.

e.g.

instead of

sfef list-shapes

use

node . list-shapes

Help

Use the help command to print out the commands and parameters available in the CLI tool.

sfef help

Set custom CSS styles

Define some custom CSS classes to be used when creating the form.

sfef set-custom-css

The command asks for CSS classes on:

  • <form> element
  • <div> wrapper around input and label
  • <label> element
  • <em> element which contains additional information bound to the label (sh:description)
  • <input> element

The custom classes are stored in the project root under filename form-custom-classes.json.

Example of custom CSS classes JSON structure:

[
    {
        "element":"form",
        "classes":["css-custom__form"]
    }, {
        "element":"input-wrapper",
        "classes":["css-custom__wrapper"]
    }, {
        "element":"input-label",
        "classes":["css-custom__label", "css-custom__label--bold"]
    }, {
        "element":"input-additional-info",
        "classes":["css-custom__info"]
    }, {
        "element":"input-element",
        "classes":["css-custom__input", "css-custom__input--required"]
    }
]

Create form file and dependencies

Creates the form component according to the frame selected (vue or angular) and the shape (.ttl file).

The create command has 2 required parameters (--framework and --shape) and 1 optional parameter (--css).

sfef create --framework <vue/angular> --shape <fileName/Path>

or

sfef create -f <vue/angular> -s <fileName/filePath>

With the optional parameter --css, the component file will include the css classes available in form-custom-classes.json. This JSON file needs to be created using the set-custom-css command.

sfef create --framework <vue/angular> --shape <fileName/filePath> --css

or

sfef create -f <vue/angular> -s <fileName/filePath> --c

The shape could be an absolute path or a file name (relative path). The relative path refers to the solid-sfef-cli package. Check available .ttl file names using sfef list-shapes command.

examples of valid fileName values:

  • 'adresregister-SHACL'

    a .ttl file name from '.assets/shacl' folder in the cli package.

  • '/Users/myname/Documents/GIT/myProject/.shapes/my-project-shape.ttl'

    an absolute path.


Vue example

sfef create -f vue -s example

Executing above command will create the FormExample.vue file in src/vue folder along with all depency components (BasicInput.vue and BasicOption.vue).

The example.ttl includes various input types: text, date, email, checkbox and number.

Being 'Given name', 'Family name', 'Street address', 'house number' and 'Postal code' required fields.

The 'Genre' field uses a <datalist> to restrict the options allowed.

Finally 'Postal code' uses a regex pattern.

// FormExample.vue
<template>
<form id="example" class="">
    <h2>PersonShape</h2>
    <BasicInput
        inputType="text"
        inputId="given-name-0-0"
        inputName="given-name-0-0"
        inputForm="example"
        inputWrapperClass=""
        inputClass=""
        inputLabel="Given name or first name"
        inputLabelClass="input-label "
        :inputRequired="true"
    />
    <BasicInput
        inputType="text"
        inputId="family-name-0-1"
        inputName="family-name-0-1"
        inputForm="example"
        inputWrapperClass=""
        inputClass=""
        inputLabel="Family name or surname"
        inputLabelClass="input-label "
        :inputRequired="true"
    />
    <BasicInput
        inputType="date"
        inputId="birth-date-0-2"
        inputName="birth-date-0-2"
        inputForm="example"
        inputWrapperClass=""
        inputClass=""
        inputLabel="Birth Date"
        inputLabelClass="input-label "
    />
    <BasicInput
        inputType="text"
        inputId="gender-0-3"
        inputName="gender-0-3"
        inputForm="example"
        inputWrapperClass=""
        inputClass=""
        inputLabel="Gender"
        inputLabelClass="input-label "
        inputListId="list-gender-0-3"
        :inputListOptions='[{"label":"Female","value":"female"},{"label":"Male","value":"male"}]'
    />
    <BasicInput
        inputType="email"
        inputId="email-0-4"
        inputName="email-0-4"
        inputForm="example"
        inputWrapperClass=""
        inputClass=""
        inputLabel="E-mail address"
        inputLabelClass="input-label "
    />
    <BasicInput
        inputType="checkbox"
        inputId="receive-newsletter-0-5"
        inputName="receive-newsletter-0-5"
        inputForm="example"
        inputWrapperClass=""
        inputClass=""
        inputLabel="Receive newsletter"
        inputLabelClass="input-label "
    />
    <BasicInput
        inputType="text"
        inputId="address-0-6"
        inputName="address-0-6"
        inputForm="example"
        inputWrapperClass=""
        inputClass=""
        inputLabel="Address"
        inputLabelClass="input-label "
    />
    <h2>AddressShape</h2>
    <BasicInput
        inputType="text"
        inputId="street-address-1-0"
        inputName="street-address-1-0"
        inputForm="example"
        inputWrapperClass=""
        inputClass=""
        inputLabel="Street Address"
        inputLabelClass="input-label "
        :inputRequired="true"
    />
    <BasicInput
        inputType="number"
        inputId="street-number-1-1"
        inputName="street-number-1-1"
        inputForm="example"
        inputWrapperClass=""
        inputClass=""
        inputLabel="Street Number"
        inputLabelClass="input-label "
        :inputRequired="true"
    />
    <BasicInput
        inputType="text"
        inputId="postal-code-1-2"
        inputName="postal-code-1-2"
        inputForm="example"
        inputWrapperClass=""
        inputClass=""
        inputLabel="Postal Code"
        inputLabelClass="input-label "
        inputAdditionalInfo="Type postal code: 2 letters + 4 digits"
        inputLabelInfoClass=""
        :inputRequired="true"
        inputPattern="[a-z A-Z]{2}[0-9]{4}"
    />
</form>
</template>
<script lang="ts">
import { defineComponent } from "vue"

import BasicInput from "./components/BasicInput.vue"
export default defineComponent({
    name: "FormExample",
    components: {
        BasicInput,
    },
})
</script>

Which will look like:

form without styling

and with a some minimal styling:

form with styling

when all fields are filled properly:

form with styling and filled correctly


Angular example

sfef create -f angular -s another-example -c

Executing above command will create the FormAnotherExample.component.html and FormAnotherExample.component.ts file in src/app/FormAnotherExample folder along with BasicInput depency component.

The another-example.ttl shape includes text and email input types.

Being 'Given name', 'Family name', 'Country', 'City', 'Street Line', 'Postal code' and 'Organization name' required fields.

As the optional parameter -c (--css) is also present, thus the css classes from form-custom-classes.json are applied to corresponding elements.

[
    {"element":"form","classes":["css__form"]},
    {"element":"input-wrapper","classes":["css__wrapper"]},
    {"element":"input-label","classes":["css__label"]},
    {"element":"input-additional-info","classes":["css__xtra-info"]},
    {"element":"input-element","classes":["css__input"]}
]

Resulting in following html and ts files:

// FormAnotherExample.component.html
<form id="another-example" class="css__form">
    <h2>Contact</h2>
    <app-basic-input
        inputType="text"
        inputId="given-name-0-0"
        inputName="given-name-0-0"
        inputForm="another-example"
        inputWrapperClass="css__wrapper"
        inputClass="css__input"
        inputLabel="Given Name"
        inputLabelClass="input-label css__label"
        inputRequired="true"
    ></app-basic-input>
    <app-basic-input
        inputType="text"
        inputId="family-name-0-1"
        inputName="family-name-0-1"
        inputForm="another-example"
        inputWrapperClass="css__wrapper"
        inputClass="css__input"
        inputLabel="Family Name"
        inputLabelClass="input-label css__label"
        inputRequired="true"
    ></app-basic-input>
    <app-basic-input
        inputType="email"
        inputId="email-0-2"
        inputName="email-0-2"
        inputForm="another-example"
        inputWrapperClass="css__wrapper"
        inputClass="css__input"
        inputLabel="Email"
        inputLabelClass="input-label css__label"
    ></app-basic-input>
    <app-basic-input
        inputType="text"
        inputId="address-0-3"
        inputName="address-0-3"
        inputForm="another-example"
        inputWrapperClass="css__wrapper"
        inputClass="css__input"
        inputLabel="Address"
        inputLabelClass="input-label css__label"
    ></app-basic-input>
    <app-basic-input
        inputType="text"
        inputId="works-for-0-4"
        inputName="works-for-0-4"
        inputForm="another-example"
        inputWrapperClass="css__wrapper"
        inputClass="css__input"
        inputLabel="Works For"
        inputLabelClass="input-label css__label"
    ></app-basic-input>
    <h2>Address</h2>
    <app-basic-input
        inputType="text"
        inputId="address-country-1-0"
        inputName="address-country-1-0"
        inputForm="another-example"
        inputWrapperClass="css__wrapper"
        inputClass="css__input"
        inputLabel="Country"
        inputLabelClass="input-label css__label"
        inputRequired="true"
    ></app-basic-input>
    <app-basic-input
        inputType="text"
        inputId="address-locality-1-1"
        inputName="address-locality-1-1"
        inputForm="another-example"
        inputWrapperClass="css__wrapper"
        inputClass="css__input"
        inputLabel="City"
        inputLabelClass="input-label css__label"
        inputRequired="true"
    ></app-basic-input>
    <app-basic-input
        inputType="text"
        inputId="street-address-1-2"
        inputName="street-address-1-2"
        inputForm="another-example"
        inputWrapperClass="css__wrapper"
        inputClass="css__input"
        inputLabel="Street Line"
        inputLabelClass="input-label css__label"
        inputRequired="true"
    ></app-basic-input>
    <app-basic-input
        inputType="text"
        inputId="postal-code-1-3"
        inputName="postal-code-1-3"
        inputForm="another-example"
        inputWrapperClass="css__wrapper"
        inputClass="css__input"
        inputLabel="Postal Code"
        inputLabelClass="input-label css__label"
        inputRequired="true"
    ></app-basic-input>
    <h2>Organization</h2>
    <app-basic-input
        inputType="text"
        inputId="name-2-0"
        inputName="name-2-0"
        inputForm="another-example"
        inputWrapperClass="css__wrapper"
        inputClass="css__input"
        inputLabel="Organization Name"
        inputLabelClass="input-label css__label"
        inputRequired="true"
    ></app-basic-input>
    <app-basic-input
        inputType="text"
        inputId="address-2-1"
        inputName="address-2-1"
        inputForm="another-example"
        inputWrapperClass="css__wrapper"
        inputClass="css__input"
        inputLabel="Organization Address"
        inputLabelClass="input-label css__label"
    ></app-basic-input>
</form>

and

// FormAnotherExample.component.ts
import { Component, ViewEncapsulation } from '@angular/core';

import { BasicInputComponent } from "../components/BasicInput/BasicInput.component"

@Component({
    selector: "app-form-another-example",
    standalone: true,
    imports: [
        BasicInputComponent,
    ],
    templateUrl: './FormAnotherExample.component.html',
    encapsulation: ViewEncapsulation.None
})
export class FormAnotherExample {
}

Which will look like:

form without styling

and with a some minimal styling:

form with styling

when all fields are filled properly:

form with styling and filled correctly


List shapes available

List the actual available shapes inside the solid-sfef-cli package.

sfef list-shapes

A part from the listed shapes, the create command also allows to use global path for any shape file.

Example of package's shapes folder path on OSX: '/Users/myname/node_modules/@solidlab/solid-sfef-cli/.assets/shacl/'


Uninstall

To uninstall, simply run:

  • for global installation:

    npm uninstall @solidlab/solid-sfef-cli --global

    or

    npm rm @solidlab/solid-sfef-cli -g
  • for development installation:

    npm uninstall @solidlab/solid-sfef-cli --save-dev

    or

    npm rm @solidlab/solid-sfef-cli -D

Changelog

version 0.1.0 - January 2024

Limitation

The create functionality is adapted to a small amount of shape scenarios. See the .ttl files in the ./assets/shacl/ folder for some examples.

BasicInput component

As a result of this scope scenarios, only the HTML <input> element is considered.

See BASIC_TYPE_MAP in constants.ts for mapping between shape (sh:property) and the corresponding Basic element (as well as type value).

e.g.

    ...
    ['email', ['BasicInput', 'email']],
    ['string', ['BasicInput', 'text']],
    ['lang String', ['BasicInput', 'text']],
    ['boolean', ['BasicInput', 'checkbox']],
    ['integer', ['BasicInput', 'number']],
    ...
<input>

Only a small amount of available attributes are taken in account: id, name, form, type, pattern, required and list.

And for the type attribute only some values are considered: text, number, checkbox, email, url, date, time, datetime-local and month.

<label>

Related to the <label> element, the value is coming from sh:name (or sh:path).

On top, an option to add additional information to the label is available. The value is coming from sh:description and set in a <span> element. Then, it is up to developer to style it properly (e.g. second line, :hover, floating,...).

Ready to grow

Anyway, in both frameworks, the basic HTML forms elements has been created, and are ready to be implemented.

The <input>, <button>, <fieldset>, <select> and <textarea> components are created following expected HTML markup, with all the possible attributes and values.

So, the package is ready to be extended for any particular shapes scenarios, or simply be custom adapted.