npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@doughtnerd/qwizard-core

v0.0.21

Published

- [Jump to usage](#usage) - [Building Forms](#building-forms) - [Validating Forms](#validating-forms) - [Custom Validators](#custom-validators)

Downloads

29

Readme

Qwizard Core

What is it?

Right now, it's a framework-agnostic, stateless, form-building library with an API modeled after Angular's form API.

Why is it?

This mostly began as an experiment to see what building a JS focused, stateless, semi-functional-programming style forms library might look like.

In it's future though, I hope to turn it into a forms-as-data focused library where an entire wizard-style form workflow could be modeled as json with this library generating the runtime necessary to facilitate the experience. If that can be pulled off, a dev team could develop the simple building blocks (a component library) tailored to company UX and infrastructure and anyone could then build/manage complex form-based workflows and make live changes without requiring deployments or dev intervention, just by using a GUI and saving their 'Wizard' experience as JSON in a DB.

Usage

Building Forms

The most fundamental part of the API is the Form object. You use it to build your forms.

You use it to construct a single-input control of your form, called a FormControl.

import { Form, FormControl, Validators } from '@doughtnerd/qwizard-core'

// A FormControl represents an individual 'input' of the form
const formControl: FormControl = Form.Control()

// You can provide a default value
const formControl: FormControl = Form.Control("I'm a default!")

// You can set the validators to use
const formControl: FormControl = Form.Control('', [Validators.notEmpty])

// You can use async validation
const formControl: FormControl = Form.Control('[email protected]', [], [checkWithServerToEnsureUsernameNotTakenValidator])

Or to construct a grouping of controls, called a FormGroup.

import { Form, FormGroup, Validators } from '@doughtnerd/qwizard-core'

// Here we group two different FormControls like you might find on a Login page
const formGroup: FormGroup = Form.Group({
  username: Form.Control(),
  password: Form.Control()
})

// You can validate a whole FormGroup, like you might find on a sign-up page
const formGroup: FormGroup = Form.Group({
  password: Form.Control(),
  confirmPassword: Form.Control()
}, [passwordsMatchValidator])

// You can also use async validation on a FormGroup, like when you have a list of names on a server of people you don't like.
const formGroup: FormGroup = Form.Group({
  firstName: Form.Control(),
  lastName: Form.Control()
}, [], [checkThatWeDontHateYouValidator])

Or to construct an array of controls, called a FormArray

import { Form, FormArray, Validators } from '@doughtnerd/qwizard-core'

// Here we create an array where a user can enter an ice-cream flavor and notes about it with one default entry.
const formArray: FormArray = Form.Array([
  Form.Group({
    flavorName: Form.Control(),
    notes: Form.Control()
  })
])

// You can use validators and async validators on arrays, just like Groups or Controls
const formArray: FormArray = Form.Array([
  Form.Group({
    flavorName: Form.Control(),
    notes: Form.Control()
  })
], [Validators.maxLength(5)])

// You can treat the controls array on the FormArray just like you would a normal array. Want to add a control? Do it.
const formArray: FormArray = Form.Array([], [Validators.maxLength(5)])

formArray.controls.push(
  Form.Group({
    flavorName: Form.Control(),
    notes: Form.Control()
  })
)

// Same goes for removing or completely reassigning the array
const formArray: FormArray = Form.Array([])

// This works
formArray.controls.pop() 

// This works too
formArray.controls = [
  Form.Group({
    flavorName: Form.Control(),
    notes: Form.Control()
  })
] 

Validating Forms

The easiest way to validate your form is to use the validateAbstractControl function.

import { 
  Form, 
  Validators,
  ValidationResultsTree, 
  validateAbstractControl 
} from '@doughtnerd/qwizard-core'

const formGroup = Form.Group({
  password: Form.Control('', [Validators.notEmpty]),
  confirmPassword: Form.Control()
});

async function runValidation() {
  const validationResult: ValidationResultsTree = await validateAbstractControl(formGroup)

  // Do something with the result
}

Custom Validators

The library has a handful of built-in validators provided on the Validators object but you can easily make your own.

import { 
  AbstractControl,
  Form, 
  FormControlValidatorFn,
  FormControl,
  ValidatorFn, 
  ValidationError,
  ValidationErrors,
  ValidatorArgumentError,
  isFormControl,
  isFormGroup
} from '@doughtnerd/qwizard-core'

// Here we make a validator that only works with FormControls
const myCustomValidator: FormControlValidatorFn = (input: FormControl) => {
  // If you're not working with TS, it may be helpful to use the `isFormControl` function here, just in case...
  if(!isFormControl(input)) {
    throw new ValidatorArgumentError('Hey, quit trying to use me on anything but a FormControl')
  }
  
  const controlValue = input.control.value
  if(controlValue === 'Gilgamesh') {
    return {
      notGilgamesh: new ValidationError("Sorry, Emiya doesn't like Gilgamesh")
    }
  }

  return
}

// Want your validator to work with more than one type of form control? Use the `ValidatorFn` type instead.
const myCustomValidator: ValidatorFn = (input: AbstractControl) => {
  // You should still check to make sure the validator is getting used on the right types of controls though.

  if(isFormGroup(input)) {
    // Do your validator logic
  }

  if(isFormControl(input)) {
    // Do your validator logic
  }

  return
}

// Pro tip, need to provide an argument to your validator? Use higher-order-functions, just like the built-in regex validator does.
function regexValidator(regex: RegExp): ValidatorFn {
  const validator: ValidatorFn = (input: FormControl) => {
    if (!isFormControl(input)) {
      throw new ValidatorArgumentError('regex: input is not a FormControl')
    }
    
    const isValid = regex.test(input.control.value)

    if(!isValid) {
      return { regex: new ValidationError(`Input value must match the pattern ${regex}. Value was ${input.control.value}`) }
    }

    return
  }

  return validator
}

Building

Run nx build core to build the library.

Running unit tests

Run nx test core to execute the unit tests via Jest.