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

subform-control-value-accessor

v1.0.0

Published

Implemnted ControlValueAccessor interface abstract classes

Downloads

38

Readme

SubformControlValueAccessor

SubformControlValueAccessor is ControlValueAccessor interface implementation project for Angular Framework. Current project contains realized 2 abstract classes: ControlValueAccessorComponent for simple custom controls and FormControlValueAccessorComponent for subforms

Install

You can run this command in your terminal

$ npm install subform-control-value-accessor

Usage

ControlValueAccessorComponent

ControlValueAccessorComponent is abstract class for simple (not subform) custom form controls. This implementation support not only ControlValueAccessor interface functions but error managment too

Properties

Methods

Creating simple custom control

Module

Add FormModule to your module for ngModel

import { FormsModule } from '@angular/forms'; // add FormsModule for ngModel

@NgModule({
  declarations: [
    CustomComponent
  ],
  imports: [
    CommonModule,
    FormsModule // add FormsModule for ngModel
  ],
  exports: [
    CustomComponent
  ]
})
export class CustomModule { }

Component class

Extend your class component from ControlValueAccessorComponent and set your control data type


export class CustomComponent extends ControlValueAccessorComponent<string> 

Provide ControlContainer object to constructor


constructor(@Optional() @Host() @SkipSelf() controlContainer: ControlContainer){
    super(controlContainer);
}

Implement abstract property data. ControlValueAccessorComponent has 2 getters and setters for linked data: "value" and "data" "data" is internal property without "onChange" event. This property is used by "writeValue" method ("ControlValueAccessor" interface) "value" is public property with "onChange" event. "value" === "data" + "onChange" event


private _data: string | null = null;

  protected get data(): string | null { // data: T | null
    return this._data;
  }

  protected set data(value : string | null){ // data: T | null
    this._data = value;
  }

Set provider


const CUSTOM_VALUE_ACCESSOR = {       
  provide: NG_VALUE_ACCESSOR, 
  useExisting: forwardRef(() => CustomComponent),
  multi: true     
};

@Component({
  selector: 'custom-component',
  templateUrl: './custom.component.html',
  providers: [CUSTOM_VALUE_ACCESSOR] //set provider
})

Full component code. You can find this code in examples.


import { Component, Host, Optional, SkipSelf, forwardRef } from '@angular/core';
import { ControlContainer, NG_VALUE_ACCESSOR } from '@angular/forms';
import { ControlValueAccessorComponent } from 'subform-control-value-accessor'; //import abstract class

//create provider for ControlValueAccessor
const CUSTOM_VALUE_ACCESSOR = {       
  provide: NG_VALUE_ACCESSOR, 
  useExisting: forwardRef(() => CustomComponent),
  multi: true     
};

@Component({
  selector: 'custom-component',
  templateUrl: './custom.component.html',
  providers: [CUSTOM_VALUE_ACCESSOR] //set provider
})
export class CustomComponent extends ControlValueAccessorComponent<string>  { // extend class with ControlValueAccessorComponent<T>. 
  //ControlValueAccessorComponent<T> is generic class. Set your parameter type.

  /**
   * ControlValueAccessorComponent<T> uses ControlContainer parameter in constructor
   */
  constructor(@Optional() @Host() @SkipSelf() controlContainer: ControlContainer){
    super(controlContainer);
  }

  /**
   * implement abstraction properties
   * 
   * ControlValueAccessorComponent<T> has 2 getters and setters for linked data: "value" and "data"
   * "data" is internal property without "onChange" event. This property is used by "writeValue" method ("ControlValueAccessor" interface)
   * "value" is public property with "onChange" event. 
   * "value" === "data" + "onChange" event
   */
  private _data: string | null = null;

  protected get data(): string | null { //data: T | null
    return this._data;
  }

  protected set data(value : string | null){ //data: T | null
    this._data = value;
  }
}

Component html

Create component template and link ngModel to PUBLIC "value" property


<input [(ngModel)]="value">
<p *ngFor="let error of activeErrorMessages">{{ error }}</p> 

Using custom control in parent component with ngModel without form

Component class

Set data property for ngModel linking


export class ParentComponent {
  public data: string = 'non-form data';
}

Component html

Create component template and link data property to ngModel


<custom-component [(ngModel)]="data"></custom-component>

<p>{{ data }}</p>

Using custom control in parent component with form

Component class

Set and initalize form property and object with custom error messages


export class ParentComponent {
  constructor(private formBuilder: FormBuilder){}

  public form!: FormGroup;

  public errorMessages = {
    'required': 'Field is required',
    'minlength': (control: AbstractControl) => 'Min length is 4 characters'
  };

  public ngOnInit(): void {
    this.form = this.formBuilder.group({
      custom: ['custom control value', [Validators.required, Validators.minLength(4)]]
    });
  }
}

Component html

Create component template and link control to form


<form [formGroup]="form">

    <label for="custom">Custom Control</label>
    <custom-component
        formControlName="custom" 
        name="custom"
        style="display: block;"
        [errorMessages]="errorMessages">
    </custom-component>
</form>

<p>Form valid: {{ form.valid }}</p>
<p>Form value: {{ form.value | json }}</p>

FormControlValueAccessorComponent

FormControlValueAccessorComponent is abstract class for custom subform controls. This class extends ControlValueAccessorComponent and adds new functionality for subform controls.

Properties

Only new or overided properties are described here

Methods

Only new or overided methods are described here

Creating simple custom form control

Module

Add ReactiveFormsModule to your module


import { FormsModule, ReactiveFormsModule } from '@angular/forms';

@NgModule({
  declarations: [
    CustomFormComponent
  ],
  imports: [
    CommonModule,
    FormsModule,
    ReactiveFormsModule
  ]
})
export class CustomFormModule { }

Component class

Extend your class component from FormControlValueAccessorComponent and set your control data type


export class CustomFormComponent extends FormControlValueAccessorComponent<FormModel>

Provide ControlContainer and FormBuilder objects to constructor


constructor(
    @Optional() @Host() @SkipSelf() controlContainer: ControlContainer,
    private formBuilder: FormBuilder) {
    super(controlContainer);
  }

Implement form abstract getter


private _form!: FormGroup;

public get form(): FormGroup {
  return this._form;  
}

Implement initForm and patchForm methods


protected initForm(): void {
    this._form = this.formBuilder.group({
      firstName: ['John', [Validators.required]], //default form data
      lastName: ['Doe', [Validators.required]] //default form data
    });
}

protected patchForm(value: FormModel | null): void {
    value && this.form.patchValue(value);
}

Create providers for NG_VALUE_ACCESSOR and NG_VALIDATORS and your component


const CUSTOM_FORM_VALUE_ACCESSOR = {       
  provide: NG_VALUE_ACCESSOR, 
  useExisting: forwardRef(() => CustomFormComponent),
  multi: true     
};

const CUSTOM_FORM_VALIDATORS = {
  provide: NG_VALIDATORS, 
  useExisting: forwardRef(() => CustomFormComponent), 
  multi: true
};

@Component({
  selector: 'custom-form',
  templateUrl: './custom-form.component.html',
  providers: [ CUSTOM_FORM_VALUE_ACCESSOR, CUSTOM_FORM_VALIDATORS ] // Add providers to component decorator
})

Full component code. You can find this code in examples.


import { Component, Host, Optional, SkipSelf, forwardRef } from '@angular/core';
import { ControlContainer, FormBuilder, FormGroup, NG_VALIDATORS, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { FormControlValueAccessorComponent } from 'subform-control-value-accessor';

/**
 * Component data structure
 */
interface FormModel {
  firstName: string;
  lastName: string
}

/**
 * NG_VALUE_ACCESSOR provider for custom control
 */
const CUSTOM_FORM_VALUE_ACCESSOR = {       
  provide: NG_VALUE_ACCESSOR, 
  useExisting: forwardRef(() => CustomFormComponent),
  multi: true     
};

/**
 * NG_VALIDATORS provider for custom control
 */
const CUSTOM_FORM_VALIDATORS = {
  provide: NG_VALIDATORS, 
  useExisting: forwardRef(() => CustomFormComponent), 
  multi: true
};

@Component({
  selector: 'custom-form',
  templateUrl: './custom-form.component.html',
  providers: [ CUSTOM_FORM_VALUE_ACCESSOR, CUSTOM_FORM_VALIDATORS ] // Add providers to component decorator
})
export class CustomFormComponent extends FormControlValueAccessorComponent<FormModel> { // extend FormControlValueAccessorComponent and set your data type
  private _form!: FormGroup;
  
  constructor(
    @Optional() @Host() @SkipSelf() controlContainer: ControlContainer,
    private formBuilder: FormBuilder) {
    super(controlContainer);
  }

  /**
   * Implememt form getter
   */
  public get form(): FormGroup {
    return this._form;  
  }

  /**
   * Implement form initialization method
   */
  protected initForm(): void {
    this._form = this.formBuilder.group({
      firstName: ['John', [Validators.required]], //default form data
      lastName: ['Doe', [Validators.required]] //default form data
    });
  }

  /**
   * Implement for patching method
   */
  protected patchForm(value: FormModel | null): void {
    value && this.form.patchValue(value);
  }
}

Component html

Create component template and link formGroup to "form" property


<form [formGroup]="form">

    <label for="firstName">First Name</label>
    <input formControlName="firstName" name="default" style="display: block;"/>
    <ng-container *ngIf="this.form.controls['firstName'].invalid">
        <p style="color:red" *ngIf="this.form.controls['firstName'].errors?.['required']">Field is required</p>
    </ng-container>
    
    <label for="lastName">Last Name</label>
    <input formControlName="lastName" name="default" style="display: block;"/>
    <ng-container *ngIf="this.form.controls['lastName'].invalid">
        <p style="color:red" *ngIf="this.form.controls['lastName'].errors?.['required']">Field is required</p>
    </ng-container>
</form>

Using custom control in parent component

Parent component class

Create and initialize FormGroup object


import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';

@Component({
  selector: 'parent',
  templateUrl: './parent.component.html'
})
export class ParentComponent implements OnInit {
  constructor(private formBuilder: FormBuilder){}

  public form!: FormGroup;

  /**
   * Initialize form. You can set default value to custom component. If you set null then component uses component default value
   */
  public ngOnInit(): void {
    this.form = this.formBuilder.group({
      custom: [{firstName: 'Jane', lastName: 'Doe'}],
    });
  }
}

Parent component html

Create form template and provide custom component as form control


<form [formGroup]="form">
    <custom-form formControlName="custom"></custom-form>
</form>

<p>Form Valid: {{ form.valid }}</p>
<p>Form Value: {{ form.value | json }}</p>

Summary

All examples you can find in git project page Good luck!