diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..63213bf5
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,15 @@
+name: CI
+
+on: [push]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Test and build
+ uses: actions/setup-node@v1
+ - run: npm install
+ - run: npm run test
+ env:
+ CI: true
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 00000000..d4a66781
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,30 @@
+name: Deploy
+env:
+ CI: true
+on:
+ push:
+ branches:
+ - master
+jobs:
+ release:
+ runs-on: ubuntu-latest
+ steps:
+ - name: checkout
+ uses: actions/checkout@v2
+ - name: setup Node
+ uses: actions/setup-node@v1
+ - run: npm install
+ - name: Github pages
+ uses: sterlingwes/gh-pages-deploy-action@v1.1
+ with:
+ access-token: ${{ secrets.ACCESS_TOKEN }}
+ source-directory: dist/ngx-schema-form
+ build-command: npm run build-demo
+ - name: Tag
+ run: ./tag-version.sh
+ - run: npm run build:lib
+ - name: Publish
+ uses: JS-DevTools/npm-publish@v1
+ with:
+ token: ${{ secrets.NPM_TOKEN }}
+ package: ./dist/schema-form/package.json
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 6e0721d2..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,50 +0,0 @@
-language: node_js
-node_js:
- - 12.4.0
-cache:
- directories:
- - node_modules
-dist: trusty
-env:
- global:
- - DBUS_SESSION_BUS_ADDRESS=/dev/null
- - DISPLAY=:99.0
- - CHROME_BIN=chromium-browser
-before_script:
- - sh -e /etc/init.d/xvfb start
-install:
- - npm install && npm install -g @angular/cli
-script:
- - npm run test
- - npm run build:lib
-notifications:
- email:
- - ebrehault@gmail.com
-jobs:
- include:
- - stage: Tagging
- if: (branch = master) AND (tag IS blank)
- after_success: source ./deploy.sh
- deploy:
- provider: releases
- api_key:
- secure: MYvXFlAVeaWcTuD+MjyFmwJJYXTKFZj8IjY3IhcqvW3EkSJK9BwdRXCGwcmAf5SI3uLXk9xf+t1uScV788frWcqL7jdRZQ04CpjbXuPkyXr+gqScLd9JHHlVTH+v8oAscXlOnyAnzfd+7uOsWLNaTdAzrSxhpnndR9Jw8l33VWl6DKQS3sfocx7YH/nqilOAj0KcF0yXEd1wjIRP7SNojIBFK6A1d/u0mZVRnVX58jeBgDKyl2TinuAJWA4iDBLziPgCvnPVOhTGSfc9m/YC/pAorq/yt74kLFBgKXoK979h927YUHxLTjZNJJhEM3XDRtf4ZJohfZcUs1BwdjYB8gKqV6yLGayQwyzfAkhvX7x93SwcZg/46zwPygzhHWkqX+//874N8RhK1duqYf0TU64K4vc/3fvYdX6cPjvr50u2lQyz3FaQ+QyKNStrk/7WkGkfHaTfFAO/arNEYnxc4/9PPXFy3I8CbFj3SIlc9CZHNZYzWb8NS8QHynAebqla4hcQjdOyi7TFWMqbmIRuynf+B8tCC093MJPjNadbZBdtTpRyO+y5dvD+qA2UBH5NLKHv9PBbi4Po8j9GAohxJPtBLdtGQPvtl64Z0cbAcuHO2o2/PXdtEBgflI/hSwFeK9Zb9GwgMFiBUySvLedoUJDjvKEDWSjvJVA8f92E8ew=
- name: Build $PACKAGE_VERSION
- on:
- repo: guillotinaweb/ngx-schema-form
- branch: master
- tags: true
- - stage: NPM release
- if: tag IS present
- before_deploy: cd dist/schema-form
- deploy:
- provider: npm
- edge: true
- email: ebrehault@gmail.com
- skip_cleanup: true
- api_key:
- secure: Qp/YeV/m9dpJ0vhrEi67qnzyik1CJbJMZr9lqQm9bHAsKTmMj+lQLDRvZ9bwoZy3F3+JHy7m8sdZyfVySr4F8kiI/EVEG2Qw0A89Xsi16L8XxPB8ycBiDnUYPVkwFHnfMG3e+UVW8mLrJkyyWnaSsy4fSjGnArt5bZcGmq4lxRHfWeoO5XU6731FEfMBdiDPQxCUHTFlSe0gRFVyx0wXSQ58RVTDk0OCnRCX0En8L4+Q2JqB6GOAF8N5KtsjN+h15oDoWqLayM2gidkBzET2eLvunPGpT7/6NV4q6Ds0HAglR7ZQLFwp5K4mdTYSBF2QY+FGofQ2hf6VTpWbBL0XCWRVU9W3Ma6dem1UYu3N2XX1K0ogrSRaAD1xy5HLcE5a5yubRRfCdh4fTHlM3OV0/VlfjYuE+QX8f7vT9axKrWdV8vuB68P0RrZb2BoJF124LWrg9h75bWCOxN3aJwxttEobvTqE2s+rCi4cR4Kb30uTPGXpZiY8JTT48YCFsFfulr1FGrzOKbGnCK6wmqrwHRAWHx8DfVbSHkEkzVxPf4mYbCCi+CS3FcAtZqSDYRhMzZvKV7YUf/e78jphBF5rswWR6GBzxTQ13hfg+Ai20tU6er+AJdI1NGvcrv/TZKLw5wWGvF6gKWoZedj7nL1EOoFJx7tVzVTlboXGOWkuQAw=
- on:
- tags: true
- repo: guillotinaweb/ngx-schema-form
- branch: master
diff --git a/README.md b/README.md
index 3b333b7a..533e306d 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Ngx Schema Form [](https://travis-ci.org/guillotinaweb/ngx-schema-form)
+# Ngx Schema Form [](https://github.com/guillotinaweb/ngx-schema-form/actions?query=workflow%3ACI)
Ngx Schema Form is an Angular 2+ module allowing you to instanciate an HTML form from a [JSON schema](http://json-schema.org/).
@@ -12,19 +12,20 @@ We think `angular-schema-form` is a great Angular 1 library, and when it will mo
## Demo
-[Demo](https://guillotinaweb.github.io/ngx-schema-form/dist/ngx-schema-form)
+[Demo](https://guillotinaweb.github.io/ngx-schema-form/)
## Features
-* Generate a form from a single json schema object
-* Generate a form from a default set of html constructs
-* Allow initialization from previous values
-* Validation handled by z-schema
-* Allow injection of custom validators
-* Allow declaration of custom widgets
-* Allow injection of custom bindings (new!)
+- Generate a form from a single json schema object
+- Generate a form from a default set of html constructs
+- Allow initialization from previous values
+- Validation handled by z-schema
+- Allow injection of custom validators
+- Allow declaration of custom widgets
+- Allow injection of custom bindings (new!)
## Installation
+
To use Ngx Schema Form in your project simply execute the following command:
```bash
@@ -34,8 +35,9 @@ npm install ngx-schema-form --save
You just have to check that all the peer-dependencies of this module are satisfied in your package.json.
##### JSON Schema
+
With the installation there comes a JSON-Schema file that declares all specific or additional
-properties supported by *ngx-schema-form*.
+properties supported by _ngx-schema-form_.
When using `*.json` files you may declare it with the `$schema` property to let your IDE's autocompletion help you create a schema-form.
@@ -48,8 +50,8 @@ When using `*.json` files you may declare it with the `$schema` property to let
```
-
## Getting started
+
Here our goal will be to create a simple login form.
Let's start by creating a simple AppComponent taking a simple JSON schema as input.
@@ -59,51 +61,52 @@ Let's start by creating a simple AppComponent taking a simple JSON schema as inp
import { Component } from "@angular/core";
@Component({
- selector:"minimal-app",
+ selector: "minimal-app",
// Bind the "mySchema" member to the schema input of the Form component.
- template: ''
+ template: '',
})
-
export class AppComponent {
// The schema that will be used to generate a form
mySchema = {
- "properties": {
- "email": {
- "type": "string",
- "description": "email",
- "format": "email"
+ properties: {
+ email: {
+ type: "string",
+ description: "email",
+ format: "email",
},
- "password": {
- "type": "string",
- "description": "Password"
+ password: {
+ type: "string",
+ description: "Password",
+ },
+ rememberMe: {
+ type: "boolean",
+ default: false,
+ description: "Remember me",
},
- "rememberMe": {
- "type": "boolean",
- "default": false,
- "description": "Remember me"
- }
},
- "required": ["email","password","rememberMe"]
- }
+ required: ["email", "password", "rememberMe"],
+ };
}
```
Create a module which import the AppComponent and configure Ngx schema form.
+
```js
//app.module.ts
import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
-import { SchemaFormModule, WidgetRegistry, DefaultWidgetRegistry } from "ngx-schema-form";
+import {
+ SchemaFormModule,
+ WidgetRegistry,
+ DefaultWidgetRegistry,
+} from "ngx-schema-form";
import { AppComponent } from "./app.component";
@NgModule({
- imports: [
- SchemaFormModule.forRoot(),
- BrowserModule
- ],
+ imports: [SchemaFormModule.forRoot(), BrowserModule],
declarations: [AppComponent],
- providers: [{provide: WidgetRegistry, useClass: DefaultWidgetRegistry}]
+ providers: [{ provide: WidgetRegistry, useClass: DefaultWidgetRegistry }],
})
export class AppModule {}
```
@@ -117,10 +120,10 @@ The input schema support almost all the features listed on the [JSON schema spec
### Accessing the form's value
#### Input binding
+
It is possible to provide initial values to the form.
You can set the initial form's value through the `model` input:
-
```js
@Component({
template: ''
@@ -132,11 +135,12 @@ export class AppComponent {
```
#### Output binding
+
The Form component provides the `onChange` output binding of which value represents the value of the form.
For instance, you can display the current forms's value with the following template:
```js
-template: '{{value | json}}'
+template: '{{value | json}}';
```
The `model` property allow two-way data binding:
@@ -146,29 +150,30 @@ The `model` property allow two-way data binding:
```
### Widgets
+
Each field can be displayed using a specific widget.
To declare the widget you want to use, add its `id` to the field's definition:
```js
mySchema = {
- "properties": {
- "email": {
- "type": "string",
- "description": "email",
- "format": "email"
+ properties: {
+ email: {
+ type: "string",
+ description: "email",
+ format: "email",
},
- "password": {
- "type": "string",
- "description": "Password",
- "widget": "password"// == "widget": {"id": "password"}
+ password: {
+ type: "string",
+ description: "Password",
+ widget: "password", // == "widget": {"id": "password"}
},
- "rememberMe": {
- "type": "boolean",
- "default": false,
- "description": "Remember me"
- }
- }
-}
+ rememberMe: {
+ type: "boolean",
+ default: false,
+ description: "Remember me",
+ },
+ },
+};
```
If there is no widget declared in a given property's schema, its type is used as widget id and the [default registry](#default-widgets-registry) gives a default widget (see details below).
@@ -177,47 +182,48 @@ The following JSON schema is equivalent with the above example:
```js
mySchema = {
- "properties": {
- "email": {
- "type": "string",
- "description": "email",
- "format": "email",
- "widget": "string"
+ properties: {
+ email: {
+ type: "string",
+ description: "email",
+ format: "email",
+ widget: "string",
},
- "password": {
- "type": "string",
- "description": "Password",
- "widget": "password"// == "widget": {"id": "password"}
+ password: {
+ type: "string",
+ description: "Password",
+ widget: "password", // == "widget": {"id": "password"}
},
- "rememberMe": {
- "type": "boolean",
- "default": false,
- "description": "Remember me",
- "widget": "boolean"
- }
- }
-}
+ rememberMe: {
+ type: "boolean",
+ default: false,
+ description: "Remember me",
+ widget: "boolean",
+ },
+ },
+};
```
Some widgets accept parameters as input, in such cases, it is possible to provide them in the schema directly within the `widget` property (here the [TinyMCE widget](https://github.com/fbessou/ng2sf-tinymce) ):
```js
mySchema = {
- "properties": {
- "pageContent": {
- "type": "string",
- "description": "Page content",
- "widget": {
- "id": "richtext",
- "plugins": "textcolor colorpicker",
- "toolbar": "forecolor backcolor"
- }
- }
- }
-}
+ properties: {
+ pageContent: {
+ type: "string",
+ description: "Page content",
+ widget: {
+ id: "richtext",
+ plugins: "textcolor colorpicker",
+ toolbar: "forecolor backcolor",
+ },
+ },
+ },
+};
```
### Default widget's registry
+
Available widgets are managed through a `WidgetRegistry`.
The default registry ([`DefaultWidgetRegistry`](./projects/schema-form/src/lib/defaultwidgets/defaultwidgetregistry.ts)) contains many widgets listed below, ordered by type:
@@ -262,6 +268,7 @@ Note that the select and radio widgets rely on the `oneOf` property:
```
### Actions and buttons
+
Each schema can be extended by adding buttons after its widget.
```js
@@ -269,65 +276,73 @@ Each schema can be extended by adding buttons after its widget.
@Component({
selector: "minimal-app",
// Bind the actions map to the the "actions" input
- template: ''
+ template: '',
})
export class AppComponent {
// The schema that will be used to generate a form
mySchema = {
- "properties": {
- "email": {
- "type": "string",
- "description": "email",
- "format": "email"
+ properties: {
+ email: {
+ type: "string",
+ description: "email",
+ format: "email",
},
- "password": {
- "type": "string",
- "description": "Password",
- "buttons": [{
- "id": "reset",
- "label": "Reset"
- }]
+ password: {
+ type: "string",
+ description: "Password",
+ buttons: [
+ {
+ id: "reset",
+ label: "Reset",
+ },
+ ],
+ },
+ rememberMe: {
+ type: "boolean",
+ default: false,
+ description: "Remember me",
},
- "rememberMe": {
- "type": "boolean",
- "default": false,
- "description": "Remember me"
- }
},
- "required": ["email", "password", "rememberMe"],
- "buttons": [{
- "id": "alert", // the id of the action callback
- "label": "Alert !" // the text inside the button
- }]
- }
+ required: ["email", "password", "rememberMe"],
+ buttons: [
+ {
+ id: "alert", // the id of the action callback
+ label: "Alert !", // the text inside the button
+ },
+ ],
+ };
// Declare a mapping between action ids and their event listener
myActions = {
- "alert": (property) => { alert(JSON.stringify(property.value)) },
- "reset": (property) => { property.reset() }
- }
+ alert: (property) => {
+ alert(JSON.stringify(property.value));
+ },
+ reset: (property) => {
+ property.reset();
+ },
+ };
}
```
#### Render buttons
-You may define you own widget to create buttons by
+You may define you own widget to create buttons by
overriding the default widget for action buttons
or create completely customized button widgets.
##### Override
-Override the default action button widget
+Override the default action button widget
in your `WidgetRegistry` implementation
and register your own button widget.
```js
- this.register('button', MyButtonWidgetComponent);
+this.register("button", MyButtonWidgetComponent);
```
##### Custom
-Define a custom button widget by
+Define a custom button widget by
setting the property `button.widget` in the schema
```json
@@ -346,35 +361,33 @@ setting the property `button.widget` in the schema
}
]
}
-```
+```
and then register it in your `WidgetRegistry` implementation
```js
- this.register('my_custom_button', MyCustomButtonWidgetComponent);
+this.register("my_custom_button", MyCustomButtonWidgetComponent);
```
-
+
##### Binding
The button widget will get provided the `button` object form the schema
-including the `button.action` from the action registry
+including the `button.action` from the action registry
and the `formProperty` object.
-To be fully AOT compatible
-the custom button widget may then extend `ButtonWidget` or
+To be fully AOT compatible
+the custom button widget may then extend `ButtonWidget` or
provide the properties `button` and `formProperty` by it self.
```js
- import {Component} from "@angular/core";
- import {ButtonWidget} from 'ngx-schema-form/dist/defaultwidgets'
-
- @Component({
- selector: 'sf-button-widget',
- templateUrl: 'custom-button.widget.html'
- })
- export class CustomWidgetComponent extends ButtonWidget {
-
- }
+import { Component } from "@angular/core";
+import { ButtonWidget } from "ngx-schema-form/dist/defaultwidgets";
+
+@Component({
+ selector: "sf-button-widget",
+ templateUrl: "custom-button.widget.html",
+})
+export class CustomWidgetComponent extends ButtonWidget {}
```
```js
@@ -389,6 +402,7 @@ provide the properties `button` and `formProperty` by it self.
```
### Advanced validation
+
JSON schema provides validation against a static schema but its often necessary to provide other validation rules.
The Form component accepts a `validators` input bound to a map between a field id and a validation function.
The validation function takes three arguments: the value of the field, the property corresponding to it and the form object.
@@ -401,38 +415,43 @@ To perform this check we create a custom validator:
@Component({
selector: "minimal-app",
// Bind the validator map to the the "validators" input
- template: ''
+ template:
+ '',
})
export class AppComponent {
mySchema = {
- "properties": {
- "email": {
- "type": "string",
- "description": "email",
- "format": "email"
+ properties: {
+ email: {
+ type: "string",
+ description: "email",
+ format: "email",
},
- "password": {
- "type": "string",
- "description": "Password"
+ password: {
+ type: "string",
+ description: "Password",
+ },
+ passwordCheck: {
+ type: "string",
+ description: "Password (verification)",
},
- "passwordCheck": {
- "type": "string",
- "description": "Password (verification)"
- }
},
- "required": ["email", "password", "passwordCheck"]
- }
+ required: ["email", "password", "passwordCheck"],
+ };
// Declare a mapping between action ids and their implementations
myValidators = {
"/passwordCheck": (value, property, form) => {
- const passwordProperty = formProperty.findRoot().getProperty('password')
- if (passwordProperty.value !== undefined && property.valid && value !== passwordProperty.value) {
- return { "passwordCheck": { "expectedValue": "same as 'password'" } }
+ const passwordProperty = formProperty.findRoot().getProperty("password");
+ if (
+ passwordProperty.value !== undefined &&
+ property.valid &&
+ value !== passwordProperty.value
+ ) {
+ return { passwordCheck: { expectedValue: "same as 'password'" } };
}
return null;
- }
- }
+ },
+ };
}
```
@@ -449,144 +468,153 @@ When you type in the name of the parent (first person) the name of the children
@Component({
selector: "minimal-app",
// Bind the bindings map to the the "bindings" input
- template: ''
+ template:
+ '',
})
export class AppComponent {
- mySchema =
- {
- "type": "object",
- "title": "Example with custom bindings.",
- "description": "Type a family name to see how the name gets synchronized with the children.",
- "properties": {
- "name": {
- "type": "string",
- "title": "Surname"
- },
- "forename": {
- "type": "string",
- "title": "Forename"
- },
- "children": {
- "type": "array",
- "title": "Family",
- "items": {
- "type": "object",
- "title": "Children",
- "properties": {
- "name": {
- "type": "string",
- "title": "Surname"
- },
- "forename": {
- "type": "string",
- "title": "forename"
- },
- "age": {
- "type": "number",
- "title": "age"
- }
- }
- }
- }
- }
- }
+ mySchema = {
+ type: "object",
+ title: "Example with custom bindings.",
+ description:
+ "Type a family name to see how the name gets synchronized with the children.",
+ properties: {
+ name: {
+ type: "string",
+ title: "Surname",
+ },
+ forename: {
+ type: "string",
+ title: "Forename",
+ },
+ children: {
+ type: "array",
+ title: "Family",
+ items: {
+ type: "object",
+ title: "Children",
+ properties: {
+ name: {
+ type: "string",
+ title: "Surname",
+ },
+ forename: {
+ type: "string",
+ title: "forename",
+ },
+ age: {
+ type: "number",
+ title: "age",
+ },
+ },
+ },
+ },
+ },
+ };
// Declare a mapping between field and event-id
myFieldBindings = {
- '/name': [
- {
- 'input': (event, formProperty: FormProperty) => {
- const parent: PropertyGroup = formProperty.findRoot();
-
- /**
- * Set the input value for the children
- */
- const child1: FormProperty = parent.getProperty('children/0/name');
-
- child1.setValue(formProperty.value, false);
-
- const child2: FormProperty = parent.getProperty('children/1/name');
- child2.setValue(event.target.value, false);
-
- /**
- * Get the input value for all the children
- */
- for (const objectProperty of parent.getProperty('children').properties) {
- console.log('Value for child ', objectProperty, objectProperty.properties['name'].value);
- }
+ "/name": [
+ {
+ input: (event, formProperty: FormProperty) => {
+ const parent: PropertyGroup = formProperty.findRoot();
+
+ /**
+ * Set the input value for the children
+ */
+ const child1: FormProperty = parent.getProperty("children/0/name");
+
+ child1.setValue(formProperty.value, false);
+
+ const child2: FormProperty = parent.getProperty("children/1/name");
+ child2.setValue(event.target.value, false);
+
+ /**
+ * Get the input value for all the children
+ */
+ for (const objectProperty of parent.getProperty("children")
+ .properties) {
+ console.log(
+ "Value for child ",
+ objectProperty,
+ objectProperty.properties["name"].value
+ );
}
- }
- ]
- };
+ },
+ },
+ ],
+ };
}
```
### Conditional fields
+
It is possible to make the presence of a field depends on another field's value.
-To achieve this you just have to add a `visibleIf` property to a field's definition.
+To achieve this you just have to add a `visibleIf` property to a field's definition.
**Value**
The value to match is set as array item.
Setting multiple items will make the visiblity condition `true` if one of the values matches.
-If it is required to match all values head over to the section `visibleIf` with `allOf` condition.
+If it is required to match all values head over to the section `visibleIf` with `allOf` condition.
**$ANY$**
-Adding the value `$ANY$` to the array of conditional values, will make the field visible for any value inserted.
+Adding the value `$ANY$` to the array of conditional values, will make the field visible for any value inserted.
```js
@Component({
selector: "minimal-app",
- template: ''
+ template: '',
})
export class AppComponent {
mySchema = {
- "properties": {
- "name": {
- "type": "string",
- "description": "Username"
+ properties: {
+ name: {
+ type: "string",
+ description: "Username",
},
- "comment": {
- "type": "string",
- "description": "Comment"
+ comment: {
+ type: "string",
+ description: "Comment",
},
- "registerNewsletter": {
- "type": "boolean",
- "description": "I want to receive the newsletter",
- "default": false,
- "visibleIf": {
- "comment": ['$ANY$']
- }
+ registerNewsletter: {
+ type: "boolean",
+ description: "I want to receive the newsletter",
+ default: false,
+ visibleIf: {
+ comment: ["$ANY$"],
+ },
},
- "registerEmail": {
- "type": "string",
- "description": "Email",
- "format": "email",
+ registerEmail: {
+ type: "string",
+ description: "Email",
+ format: "email",
// Declare that this field must be displayed only if registerNewsletter is true
- "visibleIf": {
- "registerNewsletter": [true]
- }
- }
+ visibleIf: {
+ registerNewsletter: [true],
+ },
+ },
},
- "required": ["name", "comment", "registerToNewsletter"]
- }
+ required: ["name", "comment", "registerToNewsletter"],
+ };
}
```
+
**$EMPTY$**
Assigning an empty Object to 'visibleIf' is interpreted as _visibleIf_ nothing, thereby the widget is hidden and not present in model.
```js
mySchema = {
- "properties": {
- "hidden": {
- "type": "boolean",
- "visibleIf": { }
- }
- }
- }
+ properties: {
+ hidden: {
+ type: "boolean",
+ visibleIf: {},
+ },
+ },
+};
```
`visibleIf` may also declare conditional binding by using `oneOf` or `allOf` properties.
Where `oneOf` is handled as `OR` and `allOf` is handled as `AND`.
+
```
"visibleIf": {
"allOf": [
@@ -603,17 +631,19 @@ Where `oneOf` is handled as `OR` and `allOf` is handled as `AND`.
]
}
```
-The `oneOf` a is prioritized before the `allOf` and both are prioritized before the
+
+The `oneOf` a is prioritized before the `allOf` and both are prioritized before the
property binding.
-
+
_`oneOf` and `allOf` oneOf and allOf are reserved keywords and not suitable as property names_
**Arrays**
-To address array items or not yet existing properties the `visibleIf`
+To address array items or not yet existing properties the `visibleIf`
condition path may contain wildcard `*`.
-e.g
+e.g
+
```
"visibleIf": {
"oneOf": [
@@ -626,10 +656,11 @@ e.g
}
```
-To address a specific item the `visibleIf`
+To address a specific item the `visibleIf`
condition path should contain the index position.
-e.g
+e.g
+
```
"visibleIf": {
"oneOf": [
@@ -667,13 +698,14 @@ and `target` is the `FormProperty` that has been defined by the `path`.
}
```
-
#### Hidden fields
+
When a field has been made invisible by the condition `visibleIf`
then the property of the invisible field will be missing in the result model.
If there is need to submit default values that are not visible for the form
the `widget.id` `hidden` might be the better choice
+
```js
mySchema = {
"properties": {
@@ -689,6 +721,7 @@ the `widget.id` `hidden` might be the better choice
}
}
```
+
so the value of the hidden field will be bound to the output model
```js
@@ -700,10 +733,12 @@ so the value of the hidden field will be bound to the output model
```
### Fields presentation and ordering
+
As a JSON object is an unordered collection you can't be sure your fields will be correctly ordered when the form is built.
The `order` and `fieldsets` entries of the schema are here to organize your fields.
#### Ordering
+
The `order` entry is an array listing all the fields ids in the order they must appear in the form:
```js
@@ -718,6 +753,7 @@ The `order` entry is an array listing all the fields ids in the order they must
```
#### Fieldsets
+
With the `fieldsets` property, you can describe the different parts of the form and the fields they contain:
```js
@@ -820,37 +856,46 @@ export class MyComponent {
```
## Creating a custom widget
+
Ngx schema form allows you to create your own widget.
Note: Currently this feature is not completely defined and the API might change.
You need to derivate the widget you want to customize:
+
```javascript
@Component({
- selector: 'mdl-sf-string-widget',
- templateUrl: './string.widget.html'
+ selector: "mdl-sf-string-widget",
+ templateUrl: "./string.widget.html",
})
export class MyStringWidget extends StringWidget {}
```
You need to provide its html template (let's imagine we want to use the Material Design text field):
+
```html
-
+
```
And you need to declare it in a custom registry:
+
```javascript
-import { MyStringWidget } from './mystring';
+import { MyStringWidget } from "./mystring";
export class MyWidgetRegistry extends DefaultWidgetRegistry {
constructor() {
super();
- this.register('string', MyStringWidget);
+ this.register("string", MyStringWidget);
}
}
```
@@ -868,6 +913,7 @@ providers: [{provide: WidgetRegistry, useClass: MyWidgetRegistry}],
```
Note: you will also need to import `ReactiveFormsModule` if you want to be able to use form control:
+
```javascript
import { ReactiveFormsModule } from '@angular/forms';
...
@@ -881,20 +927,23 @@ import { ReactiveFormsModule } from '@angular/forms';
```
## Create form from html instead of json schema
+
Ngx schema form allows you to create forms from angular html templates too.
-For this you only need to import `TemplateSchemaModule` to your app, and use the
+For this you only need to import `TemplateSchemaModule` to your app, and use the
directive `templateSchema` on sf-form.
The followin html will generate the same form as the json schema in getting started section.
```html
-
+
Email
- Password
+
+ Password
+
Remember Me
-
```
+
For more details see example app.
## Development and build
@@ -921,6 +970,7 @@ npm start
```
## Building the API documentation
+
You can build an HTML version of the API documentation by running the following command:
```bash
diff --git a/deploy.sh b/deploy.sh
deleted file mode 100644
index b56b302d..00000000
--- a/deploy.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/usr/bin/env bash
-
-git config --global user.email "build@travis-ci.com";
-git config --global user.name "Travis CI";
-export PACKAGE_VERSION=$(npm run get_version | tail -n 1);
-if ! [ $(git tag -l "$PACKAGE_VERSION") ]; then
- echo "TAGGING $PACKAGE_VERSION...";
- export TRAVIS_TAG=$PACKAGE_VERSION;
- git tag $PACKAGE_VERSION;
-fi
diff --git a/package.json b/package.json
index 9ca566c2..b56d8cda 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,7 @@
"build": "ng build",
"copy:schema": "node -e \"var src='./schema/ngx-schema-form-schema.json'; var dest='./dist/schema-form/ngx-schema-form-schema.json'; var fs = require('fs'); if (fs.existsSync(src)) { var data = fs.readFileSync(src, 'utf-8'); fs.writeFileSync(dest, data);}\"",
"build:lib": "ng build --prod schema-form && npm run copy:schema && cp ./README.md ./dist/schema-form/",
- "build-demo": "ng build --prod --base-href /ngx-schema-form/dist/ngx-schema-form/",
+ "build-demo": "ng build --prod --base-href /ngx-schema-form/",
"test:lib": "ng test schema-form --watch=false",
"test": "ng test --watch=false",
"lint": "ng lint",
diff --git a/projects/schema-form/karma.conf.js b/projects/schema-form/karma.conf.js
index 4c5f8d03..8318fa0f 100644
--- a/projects/schema-form/karma.conf.js
+++ b/projects/schema-form/karma.conf.js
@@ -3,29 +3,29 @@
module.exports = function (config) {
config.set({
- basePath: '',
- frameworks: ['jasmine', '@angular-devkit/build-angular'],
+ basePath: "",
+ frameworks: ["jasmine", "@angular-devkit/build-angular"],
plugins: [
- require('karma-jasmine'),
- require('karma-chrome-launcher'),
- require('karma-jasmine-html-reporter'),
- require('karma-coverage-istanbul-reporter'),
- require('@angular-devkit/build-angular/plugins/karma')
+ require("karma-jasmine"),
+ require("karma-chrome-launcher"),
+ require("karma-jasmine-html-reporter"),
+ require("karma-coverage-istanbul-reporter"),
+ require("@angular-devkit/build-angular/plugins/karma"),
],
client: {
- clearContext: false // leave Jasmine Spec Runner output visible in browser
+ clearContext: false, // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
- dir: require('path').join(__dirname, '../../coverage'),
- reports: ['html', 'lcovonly'],
- fixWebpackSourcePaths: true
+ dir: require("path").join(__dirname, "../../coverage"),
+ reports: ["html", "lcovonly"],
+ fixWebpackSourcePaths: true,
},
- reporters: ['progress', 'kjhtml'],
+ reporters: ["progress", "kjhtml"],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
- browsers: ['Chrome'],
- singleRun: false
+ browsers: ["ChromeHeadless"],
+ singleRun: false,
});
};
diff --git a/src/karma.conf.js b/src/karma.conf.js
index b6e00421..40cd5cb1 100644
--- a/src/karma.conf.js
+++ b/src/karma.conf.js
@@ -3,29 +3,29 @@
module.exports = function (config) {
config.set({
- basePath: '',
- frameworks: ['jasmine', '@angular-devkit/build-angular'],
+ basePath: "",
+ frameworks: ["jasmine", "@angular-devkit/build-angular"],
plugins: [
- require('karma-jasmine'),
- require('karma-chrome-launcher'),
- require('karma-jasmine-html-reporter'),
- require('karma-coverage-istanbul-reporter'),
- require('@angular-devkit/build-angular/plugins/karma')
+ require("karma-jasmine"),
+ require("karma-chrome-launcher"),
+ require("karma-jasmine-html-reporter"),
+ require("karma-coverage-istanbul-reporter"),
+ require("@angular-devkit/build-angular/plugins/karma"),
],
client: {
- clearContext: false // leave Jasmine Spec Runner output visible in browser
+ clearContext: false, // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
- dir: require('path').join(__dirname, '../coverage'),
- reports: ['html', 'lcovonly'],
- fixWebpackSourcePaths: true
+ dir: require("path").join(__dirname, "../coverage"),
+ reports: ["html", "lcovonly"],
+ fixWebpackSourcePaths: true,
},
- reporters: ['progress', 'kjhtml'],
+ reporters: ["progress", "kjhtml"],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
- browsers: ['Chrome'],
- singleRun: false
+ browsers: ["ChromeHeadless"],
+ singleRun: false,
});
-};
\ No newline at end of file
+};
diff --git a/tag-version.sh b/tag-version.sh
new file mode 100755
index 00000000..2949a05e
--- /dev/null
+++ b/tag-version.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+git config --global user.email "github@github.com";
+git config --global user.name "GitHub Actions";
+export PACKAGE_VERSION=$(npm run get_version | tail -n 1);
+echo "PACKAGE VERSION: $PACKAGE_VERSION"
+echo "Search in existing… $(git tag -l "$PACKAGE_VERSION")"
+if ! [ $(git tag -l "$PACKAGE_VERSION") ]; then
+ echo "TAGGING $PACKAGE_VERSION...";
+ git tag $PACKAGE_VERSION;
+ git push --tags;
+fi
\ No newline at end of file