text:
---
meta:
 - name: description
   content: Documentation over Logger by Ts.ED framework.
 - name: keywords
   content: logger decorators ts.ed express.js koa.js typescript node.js javascript
---
# Logger

Ts.ED has its own logger available through [`@tsed/logger`](https://logger.tsed.io) package.

## Features

Ts.ED logger supports many features, and is optimized to be used in production.

- Many [layout](https://logger.tsed.io/layouts) formats are supported:
 - [Colored console](https://logger.tsed.io/layouts/colored.html) logging to stdout or stderr.
 - [Json layout](https://logger.tsed.io/layouts/json.html).
- Many appender (stream) formats are supported:
  - [Console](https://logger.tsed.io/appenders/console.html).
  - [File](https://logger.tsed.io/appenders/file.html).
  - [File date](https://logger.tsed.io/appenders/file-date.html), with configurable log rolling based on file size or date.
  - [Stdout](https://logger.tsed.io/appenders/stdout.html).
  - [Stderr](https://logger.tsed.io/appenders/stderr.html).
  - [Insight](https://tsed.io/appenders/insight.md).
  - [LogEntries](https://tsed.io/appenders/logentries.md).
  - [LogStash HTTP](https://tsed.io/appenders/logstash-http.md).
  - [LogStash UDP](https://tsed.io/appenders/logstash-udp.md).
  - [Loggly](https://tsed.io/appenders/loggly.md).
  - [RabbitMQ](https://tsed.io/appenders/rabbitmq.md).
  - [Seq](https://tsed.io/tutorials/seq.md).
  - [Slack](https://tsed.io/tutorials/slack.md).
  - [Smtp](https://logger.tsed.io/appenders/smtp.html).
- @@RequestLogger@@, in **production** mode, caches all request logs until the response is sent to your consumer. See [request logger](/docs/logger.html#request-logger) section bellow.

You can create your own layout/appender:
 - [Customize appender (chanel)](https://logger.tsed.io/appenders/custom.html),
 - [Customize layout](https://logger.tsed.io/layouts/custom.html)

## Configuration

Logger can be configured through the @@Configuration@@ decorator:

<div class="table-features">

Props | Description
--- | ---
`logger.level` | Change the default log level displayed in the terminal. Values: `debug`, `info`, `warn` or `error`. By default: `info`.
`logger.logRequest` | Log all incoming requests. By default, it's true and prints the configured `logger.requestFields`.
`logger.requestFields` | Fields displayed when a request is logged. Possible values: `reqId`, `method`, `url`, `headers`, `body`, `query`,`params`, `duration`.
`logger.reqIdBuilder` | A function called for each incoming request to create a request id.
`logger.jsonIndentation` | The number of space characters to use as white space in JSON output. Default is 2 (0 in production).
`logger.disableRoutesSummary` | Disable routes table displayed in the logger.
`logger.format` | Specify log format. Example: `%[%d{[yyyy-MM-dd hh:mm:ss,SSS}] %p%] %m`. See [@tsed/logger configuration](https://logger.tsed.io).
`logger.ignoreUrlPatterns` | (`String` or `RegExp`) List of patterns to ignore logged request according to the `request.url`.

</div>

::: warning
It is recommended to disable logRequest in production. Logger has a cost on the performance.
:::

## Use Json Layout in production

You add this code to switch the logger to Json layout in production mode:

```typescript
import {env} from "@tsed/core";
import {$log, Configuration} from "@tsed/common";
import "@tsed/platform-express";

export const isProduction = process.env.NODE_ENV === Env.PROD;

if (isProduction) {
  $log.appenders.set("stdout", {
    type: "stdout",
    levels: ["info", "debug"],
    layout: {
      type: "json"
    }
  });
  $log.appenders.set("stderr", {
    levels: ["trace", "fatal", "error", "warn"],
    type: "stderr",
    layout: {
      type: "json"
    }
  });
}

@Configuration({
  logger: {
    disableRoutesSummary: isProduction // remove table with routes summary
  }
})
export class Server {}
```

This configuration will display the log as following:

```bash
{"startTime":"2017-06-05T22:23:08.479Z","categoryName":"json-test","data":["this is just a test"],"level":"INFO","context":{}}
```

It's more useful if you planed to parse the log with LogStash or any log tool parser.

## Inject logger

Logger can be injected in any injectable provider as follows:

```typescript
import {Logger} from "@tsed/common";
import {Injectable, Inject} from "@tsed/di";

@Injectable()
export class MyService {
  @Inject()
  logger: Logger;

  $onInit() {
    this.logger.info('Hello world');
  }
}
```

::: tip
Prefer the @@RequestLogger@@ usage if you want to attach your log the current request. See the next section.
:::

## Request logger

For each Request, a logger will be attached to the @@PlatformContext@@ and can be used like here:

```typescript
import {Controller, Context, Get} from "@tsed/common";
import {Logger} from "@tsed/logger";
import {MyService} from "../services/MyService";

@Controller("/")
class MyController {
  @Inject()
  myService: MyService;

  @Get("/")
  get(@Context() ctx: Context) {
    ctx.logger.info({customData: "test"}); // parameter is optional
    ctx.logger.debug({customData: "test"});
    ctx.logger.warn({customData: "test"});
    ctx.logger.error({customData: "test"});
    ctx.logger.trace({customData: "test"});

    // forward ctx object to the service and use logger inside.
    // All request
    myService.doSomething("test", ctx);
  }
}
```

```typescript
import {PlatformContext} from "@tsed/common";
import {Injectable, Inject} from "@tsed/di";

@Injectable()
export class MyService {
  doSomething(input: string, ctx: PlatformContext) {
     ctx.logger.info({event: "test", input});
  }
}
```

::: tip
All log use through ctx.logger will be associated with the uniq request id generated by Ts.ED.
:::

::: tip
@@RequestLogger@@, in **production** mode, caches all request logs until the response is sent to your consumer.
:::

A call with one of these methods will generate a log according to the `logger.requestFields` configuration:

```bash
[2017-09-01 11:12:46.994] [INFO ] [TSED] - {
  "status": 200,
  "reqId": 1,
  "method": "GET",
  "url": "/api-doc/swagger.json",
  "duration": 92,
  "headers": {
    "host": "<IP_ADDRESS>:8001",
    "connection": "keep-alive",
    "upgrade-insecure-requests": "1",
    "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36",
    "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
    "accept-encoding": "gzip, deflate",
    "accept-language": "fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4"
  },
  "body": {},
  "query": {},
  "customData": "test"
}
```

You can configure the displayed fields from the server configuration:

```typescript
import {Configuration} from "@tsed/common";

@Configuration({
  logger: {
    requestFields: ["reqId", "method", "url", "headers", "body", "query","params", "duration"]
  }
})
export class Server {}
```

or you can override the middleware with @@OverrideProvider@@.

Example:

<<< @/docs/snippets/configuration/override-platform-log-middleware.ts

## Shutdown logger

Shutdown returns a Promise that will be resolved when @tsed/logger has closed all appenders and finished writing log events.
Use this when your program exits to make sure all your logs are written to files, sockets are closed, etc.

```typescript
import {$log} from "@tsed/logger";

$log
  .shutdown()
  .then(() => {
     console.log("Complete")
  });
```

repository: jkrnak/tsed

branch_name: refs/heads/production

repo_name: jkrnak/tsed


text:
# My Tool Shed Database

Create a database that keeps tracks of everything in your tool shed. Name it whatever you would like. You will need one table that stores the information for all of your tools as well as the columns which will help you categorize them. Add the columns below.

* name
* date_purchased
* rating (for much you like it on a scale of 1 to 5)
* is_good_condition

Pay particular attention to the data types you will need to assign to each one of these.

license: MIT

repository: samuraijane/dc-builders

branch_name: refs/heads/master

extension: md

gha_language: JavaScript

gha_license_id: MIT

license_type: permissive

repo_name: samuraijane/dc-builders


text:
---
layout: post
title:  SMA Capstone Project Highlight by APG News
date:   2008-07-03 16:40:16
description: SMA inaugural class showcases Senior Capstone Projects
---

Article published by APG News on the Science and Mathematics Academy Senior Capstone Research Symposium.

[SMA inaugural class showcases Senior Capstone Projects.](https://www.army.mil/article/10610/)

extension: markdown


text:
---
layout: default
---

# 1996-09-10 - Ban CU Secrecy, Keep TLA's!

## Header Data

From<EMAIL_ADDRESS>(John Young)

To<EMAIL_ADDRESS>Message Hash: acb86f48264408ba56773f6f1d07e9034ac133b39fef5cdbe9a1ad98203977a5

Message ID<EMAIL_ADDRESS>Reply To: _N/A_

UTC Datetime: 1996-09-10 23:11:03 UTC

Raw Date: Wed, 11 Sep 1996 07:11:03 +0800

## Raw message

```
{% raw  %}From<EMAIL_ADDRESS>(John Young)
Date: Wed, 11 Sep 1996 07:11:03 +0800
To<EMAIL_ADDRESS>Subject: Ban CU Secrecy, Keep TLA's!
Message-ID<EMAIL_ADDRESS>MIME-Version: 1.0
Content-Type: text/plain

   FiTi, 10 September 1996.

   Call to abolish banking secrecy

   By William Lewis in Cambridge

   The UK government should take a lead and abolish banking
   secrecy in its dependent territory offshore centres, a
   former legal adviser to MI5 and MI6, the British
   intelligence agencies, said yesterday.

   Mr David Bickford, the first British intelligence lawyer
   to speak publicly in the UK, said at a conference in
   Cambridge, 50 miles north-east of London, that there
   "appears to be no justification at all for offshore bank
   secrecy other than to protect the criminal".

   He said "offshore bank secrecy can and must be abolished"
   and "the UK should be the first to abolish this secrecy
   given their control of their dependent territory offshore
   centres".

   Mr Bickford, who now runs an international legal
   consultancy, said "endemic corruption" is caused by
   offshore secrecy, and it is "difficult to see why it is
   tolerated by any other than those with an unlawful
   disposition".

   He said allowing countries to maintain offshore banking
   secrecy is "a classic example of the corruptive influence
   of organised crime".

   Mr Bickford added that the "justification is put forward
   at all is an example of the overwhelming subversive
   corruptive influence of organised crime which has managed
   to magic a seemingly acceptable position out of the sheer
   weight of its financial proceeds from narcotics, fraud,
   extortion and other criminal enterprise".

   Mr Raymond Kendall, secretary general of Interpol told
   the 14th International Symposium on Economic Crime that
   governments should commit more resources and step up
   co-operation to tackle the growing problem of
   international corruption.

   [End]

{% endraw %}
```

## Thread

+ Return to [September 1996](/archive/1996/09)

+ Return to<EMAIL_ADDRESS>(John Young)](/authors/jya_at_pipeline_com_john_young_)"

+ 1996-09-10 (Wed, 11 Sep 1996 07:11:03 +0800) - Ban CU Secrecy, Keep TLA's! -<EMAIL_ADDRESS>(John Young)_


text:
# hugo-brunch
Adds [Hugo](https://gohugo.io) support to [Brunch](http://brunch.io).

## Usage
Install the plugin via npm with `npm install --save-dev hugo-brunch`.

Or, do manual install:

* Add `"hugo-brunch": "x.y.z"` to `package.json` of your brunch app.
* If you want to use git version of plugin, use the GitHub URI
`"hugo-brunch": "git+ssh://git@github.com:serg/hugo-brunch.git"`.

Put the folder with the Hugo assets into the `app/` folder. The path for this folder can be for e.g. `app/your-hugo-folder/`. You can name the Hugo assets folder as you wish.

## Options
By default the Brunch doesn't tracks the changes in folders and files that begins with the `_` (underscore) symbol. The Hugo subfolders and files can have names with underscore at the beginning. These rules are used by the Hugo conventions for templating.
For teach the Brunch to track any Hugo folders and files you need to override the ignore rules for the files and folders in the `brunch-config.js` (or .coffee). Write this rule in the `module.exports` section:

```js
module.exports = {
  ...
  conventions: {
    ignored: [/\/_(?!default|index)/, /vendor\/(node|j?ruby-.+|bundle)\//]
  },
  ...
}
```
By default Brunch has these rules for ignore the tracking of some file types: `[/\/_/, /vendor\/(node|j?ruby-.+|bundle)\//]`. In the configuration above we have updated the `/\/_/` rule and leaves the second rule as is.

To work with the `hugo-brunch` plugin you need to define the required `sourceFolder` field. This field must point to your Hugo folder. You can setup it in this way:

```js
module.exports = {
  ...
  plugins: {
    hugo: { sourceFolder: 'app/your-hugo-folder' }
  },
  ...
}
```
**That's all you need for a basic configuration.**
## Defaults
The `hugo-brunch` relies on the following default options:
```js
module.exports = {
  ...
  plugins: {
    hugo: {
      sourceFolder: '',
      outputFolder: 'public',
      executablePath: 'node_modules/hugo-bin/vendor/hugo',
      args: ['--buildDrafts','--buildFuture','--verbose'],
      fileExtensions: ['html','toml','yaml','json','md',
                      'svg','png','jpg','jpeg','ico','gif','webp',
                      'eot','ttf','woff','woff2',
                      'mp4','webm','ogg',
                      'mp3','wav',
                      'txt']
    }
  },
  ...
}
```
`sourceFolder` (required) Location of the Hugo website assets.

`outputFolder` (optional) Location for a folder with a website generated by Hugo. By default the `paths.public` value are used from brunch config. Please note, this path needs to be constructed by starting inside the Hugo assets folder or you can use an absolute path.

`executablePath` (optional) Alternative location of the Hugo execution file.

`args` (optional) The arguments, that will be used during Hugo execution. The defaults for development mode is `['--buildDrafts','--buildFuture','--verbose']`, for production - `['--verbose']`. ***Please notice:*** when you override this option your arguments will be applied to both modes - for the development and for the production.

`fileExtensions` (optional) The plugin will watch the changes in files with these extensions. This option will be applied only for files in a folder defined in the `sourceFolder` value.

**Warning**
All changed values in the Brunch config file will override the default values of plugin. In some cases this can lead to unexpected results when the Hugo will generate a website. If you need to expand abilities of options you can combine your values with defaults. Please make sure you know what you are doing by follow the [Hugo docs](https://gohugo.io/commands/hugo/).

## Examples
[`hugo-init`](https://github.com/serg/hugo-init) - a Hugo minimal boilerplate.

## Thanks
- To the [Brunch team](https://github.com/brunch/brunch/graphs/contributors) and all related contributors.
- To the [Hugo folks](https://github.com/gohugoio/hugo/graphs/contributors) and all related contributors.
- To [Shun Sato](https://github.com/satoshun00) for the [`hugo-bin`](https://www.npmjs.com/package/hugo-bin) package.

## License
MIT © [Sergey Furtak](https://sergfurtak.com)

By using this plugin you understand the implications and guarantees of the [MIT license](https://github.com/serg/hugo-brunch/blob/master/LICENSE).

repository: serg/hugo-brunch

repo_name: serg/hugo-brunch


text:
---
layout: default
---

# 1994-06-03 - IMP (was Re: ecash-info (

## Header Data

From: Duncan Frissell<EMAIL_ADDRESS>To<EMAIL_ADDRESS>Message Hash: dbf7a68f1c22d450d834587d8cb68a584946471e5a928d6bd58a13ddf7ccf309

Message ID<EMAIL_ADDRESS>Reply To: _N/A_

UTC Datetime: 1994-06-03 14:06:54 UTC

Raw Date: Fri, 3 Jun 94 07:06:54 PDT

## Raw message

```
{% raw  %}From: Duncan Frissell<EMAIL_ADDRESS>Date: Fri, 3 Jun 94 07:06:54 PDT
To<EMAIL_ADDRESS>Subject: IMP (was Re: ecash-info (
Message-ID<EMAIL_ADDRESS>MIME-Version: 1.0
Content-Type: text/plain

H >   2.) It seems to me that that e-cash, contrary to the status quo's
H >thinking,
H >   is *critical* to internet commerce.
H >
H >No, it's not critical.  Some form of transaction mechanism is
H >critical.  Privacy is not critical to the bulk of the economy,
H >though.  Face up to it.  If it were, it would be so obvious that we
H >wouldn't be discussing it on a mailing list.  In fact, _we_ wouldn't be
H >discussing it, but rather a whole bunch of bank vice presidents.

The nicest thing about electronic money is not that anonymous digital cash
is possible. but that the money is electronic.  This means cheap
transaction costs.  This means that even if "banks" deploy ID-based rather
than anonymous transaction systems, civilians can quickly step in and
offer to broker anonymous transactions for those who prefer their privacy.

Just as you can cash checks for other people today, you will be able to
carry out transactions for others on the nets.  Since it is much easier to
operate these kinds of services in an electronic payment system with
automation and on-line verification, suppliers should appear if the demand
warrants.  Since people will be in different physical locations and legal
jurisdictions proof of wrong doing will be hard to come by.

DCF

"It is perfectly possible to overthrow the old order without picking up a
gun or violating any (significant) law.  If the conditions of human life
change as radically and rapidly as they seem destined to, the (always
bizarre) notion that groups of small oligarchies are competent to run the
lives of everyone on earth will be swept aside almost accidentally as
people move into their new lives." -- DCF
--- WinQwk 2.0b#1165

{% endraw %}
```

## Thread

+ Return to [June 1994](/archive/1994/06)

+ Return to "[Duncan Frissell <frissell@panix.com>](/authors/duncan_frissell_frissell_at_panix_com_)"

+ 1994-06-03 (Fri, 3 Jun 94 07:06:54 PDT) - IMP (was Re: ecash-info ( - _Duncan Frissell<EMAIL_ADDRESS>


text:
# 1. Introduction
This blue print describes the requirements, design and implementation of REDIS TLS Support.

# 2. Problem statement
The requests from contrail-analytics clients to Redis server are not encrypted and can pose a
security request.

# 3. Proposed solution
Redis does not support encryption. In order to implement setups where trusted parties can access a
Redis instance over the internet or other untrusted networks, an additional layer of protection should
be implemented. We will use a secure tunneling program called stunnel to encrypt the Redis traffic.
Traffic between Redis clients and servers will be routed through a dedicated SSL encrypted tunnel.

Currently, alarm-gen, analytics-api, collector, query-engine and webui connect to Redis. Out of theses
collector and webui connect to local redis instance listening on localhost <IP_ADDRESS>.
Since, they do not connect to any remote redis instance over internet, there is no need to enable SSL
for these 2 services.

Following change will be needed to enable SSL between Redis clients and servers.

1. All clients, namely, alarm-gen, analytics-api and query-engine will have following conf file changes
   redis_use_ssl=True
   redis_keyfile=/etc/contrail/ssl/private/server-privkey.pem
   redis_certfile=/etc/contrail/ssl/certs/server.pem
   redis_ca_cert=/etc/contrail/ssl/certs/ca-cert.pem

2. Stunnel program needs to run within a new container with following conf parameters.
   cert = /etc/contrail/ssl/certs/ca-cert.pem
   pid = /var/run/stunnel.pid
   foreground = yes
   [redis]
   accept = <IP_ADDRESS>:6379
   connect = <IP_ADDRESS>:6379

   In case of SSL enabled, client of redis-server is stunnel which will always run locally. Stunnel will send the packets to redis
   server on localhost port 6379. So, redis will always listen on localhost:6379. Analytics clients will connect to stunnel on
   public ip-address and port 6379.

   In case of no SSL, behavior will remain unchanged.

3. Alarmgen and Analytics api are using StrictRedis to connect to Redis. It has SSL supports. So, we just need to pass SSL
   parameters when SSL is enabled.

4. Query Engine are using hiredis library to connect to Redis. As of now, hiredis 0.14.0 release is officially available
   but it does not have SSL support. However SSL code is present in latest hiredis github master branch. We have to get an official
   hiredis 0.15.0 release which has SSL support. If latest release is not available, then we can use 0.14.0 release and apply
   SSL patch on top of it.

5. Contrail provisioning will be updated to populate SSL parameters in redis client configuration files. New docker files will
   be added to run stunnel service.

## 3.1 Alternatives considered
None

## 3.2 API schema changes
Not Applicable

## 3.3 User workflow impact
Please see above for the required configuration to be done.

## 3.4 UI changes
None

## 3.5 Notification impact
None

# 4. Implementation
## 4.1 Work items
1. Redis client side changes to use SSL certificates when SSL is enabled. This needs changes in analytics and webui backend.
2. Stunnel docker creation in Redis POD. This needs changes in contrail-container-builder.
3. Deployer changes -
   Priority order for the deployers is the following one:
   RHOSP /TripleO
     -- Stunnel docker will be created if SSL_ENABLE is true in contrail configuration settings.
   OpenShift
   Juju
   Ansible-deployer
      -- Stunnel docker will be created if REDIS_SSL_ENABLE is true in contrail configuration.
   Helm

# 5. Performance and scaling impact
TODO

## 5.1 API and control plane
None

## 5.2 Forwarding performance
None

# 6. Upgrade
None

# 7. Deprecations
None

# 8. Dependencies
None

# 9. Testing
## 9.1 Unit tests
1. Extend existing Redis testcases to use SSL.
2. Stunnel and Redis restart tests.
3. Negative tests.

## 9.2 Dev tests

## 9.3 System tests

# 10. Documentation Impact

# 11. References

repository: tungstenfabric/tf-specs

repo_name: tungstenfabric/tf-specs


text:
# Ionic Custom Form Builder

This is an ionic-angular component library to simplify and eliminate some redundancies of building forms when developing ionic apps.

[![NPM version](https://img.shields.io/npm/v/ion-custom-form-builder.svg?style=flat)](https://www.npmjs.org/package/ion-custom-form-builder)
![npm bundle size](https://img.shields.io/bundlephobia/min/ion-custom-form-builder)
![NPM](https://img.shields.io/npm/l/ion-custom-form-builder)
![GitHub issues](https://img.shields.io/github/issues/ronald-hove/ion-custom-form-builder)
![npm](https://img.shields.io/npm/dw/ion-custom-form-builder)

Features (Current Version)
  - [Email Validation](#Usage-example-output)
  - [Password Validation](#working-with-passwords)
  - [Credit Card Validation (With support for detecting card type)](#credit-card-validation)
  - [Implement Your Own Submit Button](#Implement-your-own-submit-button)
  - Ionic FormFieldTypes included
    - ion-input
    - ion-textarea
  - Use Official Ionic Icons

|    |    |
| -- | -- |
| [Upcoming Features](https://github.com/ronald-hove/ion-custom-form-builder/blob/master/docs/upcoming-features.md) | [Changelog](https://github.com/ronald-hove/ion-custom-form-builder/blob/master/docs/changelog.md) |

Docs
  - [Examples App](https://github.com/ronald-hove/icfb-examples)
  - [FormField Model](https://github.com/ronald-hove/ion-custom-form-builder/blob/master/docs/form-field-model.md)
  - [Component Inputs](https://github.com/ronald-hove/ion-custom-form-builder/blob/master/docs/component-inputs.md)
  - [Component Outputs](https://github.com/ronald-hove/ion-custom-form-builder/blob/master/docs/component-outputs.md)
  - [Examples & Code Samples](https://github.com/ronald-hove/ion-custom-form-builder/blob/master/docs/examples.md)

# Usage Example Output

![Preview Image 1](demo.gif)

# Installation

In your project root, run

```txt
npm i ion-custom-form-builder@latest
```

# Usage Example

Lets walk through a *getting started* example that will show you how to setup the component in your project

import the module in your top level *.module.ts file

```ts
...
import { IonCustomFormBuilderModule } from 'ion-custom-form-builder';
...

@NgModule({
  imports: [
    ...
    IonCustomFormBuilderModule.forRoot()
    ...
  ]
})

...
```

Now in your *.page.html file add

```html
...
  <ion-custom-form-builder
    [formFields]="fields"
    [submitButtonText]="'Login'"
    (formSubmission)="onIonCfbFormSubmission($event)"
  >
  </ion-custom-form-builder>
...
```

Head over to your  *.page.ts file and add

```ts

  ...
  import { FormField } from 'ion-custom-form-builder';
  import { Validators } from '@angular/forms';
  ...

  fields: FormField[] = [];

  constructor() {
    this.fields = [
      {
        icon: 'mail',
        type: 'email',
        title: 'Email',
        formControlName: 'email',
        validators: [Validators.required, Validators.email],
        validationMessages: [
          {
            type: 'required',
            message: 'Email is required'
          },
          {
            type: 'email',
            message: 'Email is incorrect'
          }
        ]
      },
      {
        icon: 'lock-closed',
        type: 'password',
        title: 'Password',
        formControlName: 'password',
        validators: [Validators.required],
        validationMessages: [
          {
            type: 'required',
            message: 'Password is required'
          }
        ]
      }
    ];
  }

  onIonCfbFormSubmission(formData) {
    console.log('FORM_DATA=,', formData);
  }

  ...

```

Serve your app to see it in action

```txt
ionic serve
```

# **Advanced Features**

  - Password Validation
  - Credit Card Validation
  - Implement Your Own Submit Button

# Working with passwords

**Usage Example Output**

![Preview Image 2](demo1.gif)

The ion-form-builder component provides you with an elegant way to validate passwords by doing the following

# Password Validation Usage Example

Head over to your  *.page.ts file and add

In your *.page.ts file , create  **FormField** array objects with **formFieldType** of **'password'** & **'confirm-password'**

```ts

  ...
  import { FormField } from 'ion-custom-form-builder';
  import { Validators } from '@angular/forms';
  ...

  fields: FormField[] = [];

  constructor() {
    this.fields = [
      {
        icon: 'lock-closed',
        type: 'password',
        title: 'Password',
        formControlName: 'password',
        validators: [Validators.required],
        asyncValidators: [this.passwordValidator],
        validationMessages: [
          {
            type: 'required',
            message: 'Password is required'
          },
          {
            type: 'passwordValidator',
            message: 'Passwords do not match'
          }
        ],
      },
      {
        icon: 'lock-closed',
        type: 'password',
        title: 'Confirm Password',
        formControlName: 'confirm-password',
        validators: [Validators.required],
        asyncValidators: [this.confirmPasswordValidator],
        validationMessages: [
          {
            type: 'required',
            message: 'Please confirm your password'
          },
          {
            type: 'confirmPasswordValidator',
            message: 'Passwords do not match'
          }
        ]
      }
    ];
  }
  ...

  /**
   * Validates password against password confirmation
   *
   * @param {AbstractControl} control
   * @return {*}  {Promise<any>}
   */
  passwordValidator(control: AbstractControl): Promise<any> {
    if (!control.parent) {
      return Promise.resolve(null)
    }else if (control?.parent.get('confirm-password')?.value && control?.value !== control?.parent.get('confirm-password')?.value) {
      control.markAsTouched({ onlySelf: true });
      return Promise.resolve({ passwordValidator: { valid: false } });
    }else {
      if (control?.parent.get('confirm-password')?.invalid) {
        control?.parent.get('confirm-password')?.updateValueAndValidity({ onlySelf: true });
      }
      return Promise.resolve(null)
    }
  }

  /**
   * validates password confirmation against password
   *
   * @param {AbstractControl} control
   * @return {*}  {Promise<any>}
   */
  confirmPasswordValidator(control: AbstractControl): Promise<any> {
    if (!control.parent) {
      return Promise.resolve(null)
    }else if (control?.parent.get('password')?.value && control?.value !== control?.parent.get('password')?.value) {
      control?.parent.get('password')?.updateValueAndValidity({ onlySelf: true });
      return Promise.resolve({ confirmPasswordValidator: { valid: false } });
    }else {
      control?.parent.get('password')?.updateValueAndValidity({ onlySelf: true });
      return Promise.resolve(null)
    }
  }
  ...

```

# Credit Card Validation

ion-custom-form-builder comes with the ability to validate credit cards thanks to [Payform Library](https://github.com/jondavidjohn/payform)

**Usage Example Output**

![Preview Image 3](demo2.gif)

# Credit Card Validation Usage Example

**IMPORTANT**

First you need to add the following in the assets array of your angular.json file, this will map library's
assets to your project assets folder

```json
  ...
  "architect": {
    "build": {
      "options": {
        "assets": [
          {
            "glob": "**/*",
            "input": "node_modules/ion-custom-form-builder/src/assets",
            "output": "assets"
          }
        ]
      }
    }
  }
  ...
```

In your *.page.ts file , create a **FormField** array object with a **formFieldType** of **'credit-card'**

```ts
...
  fields: FormField[] = [];

  constructor() {
    this.fields = [
      {
        type: 'number',
        title: 'Card',
        formControlName: 'card',
        formFieldType: 'credit-card',
        validators: [Validators.required],
        validationMessages: [
          {
            type: 'required',
            message: 'Credit card number is required'
          }
        ]
      }
    ];
  }
...
```

Serve your app again to see the changes

```txt
  ionic serve
```

# Implement Your Own Submit Button

You are able to implement your own submit button in case you want content in-between your form and submit button

In your *.page.html file where you put the ion-custom-form-builder component ..

```html
...
<ion-content>
  <ion-custom-form-builder
    [formFields]="fields"
    [showSubmitButton]="false"
    (formSubmission)="onIonCfbFormSubmission($event)">
  </ion-custom-form-builder>
  ...
  <ion-row>
    <ion-col>
      <ion-button expand="block" color="primary" (click)="onClickMyOwnSubmitButton()">
        Submit
      </ion-button>
    </ion-col>
  </ion-row>
</ion-content>
...
```

Then in your *.page.ts file import the IonCustomFormBuilderService..

```ts
  ...
  import { IonCustomFormBuilderService } from 'ion-custom-form-builder';
  ...

  constructor(
    private ionCfbService: IonCustomFormBuilderService
  ) {
  ...
  }

  ...

  /**
   * Triggered by the form submission event
   *
   * @param {*} formData
   */
  onIonCfbFormSubmission(formData) {
    console.log('FORM_DATA=,', formData);
  }

  /**
   * Trigger form submission using IonCustomFormBuilderService
   *
   */
  onClickMyOwnSubmitButton() {
    this.ionCfbService.triggerFormSubmission$.next(true)
  }
  ...
```

repository: ronald-hove/ion-custom-form-builder

gha_language: TypeScript

repo_name: ronald-hove/ion-custom-form-builder


text:
# React v. Lit

Compares React and Lit implementations of the React homepage demos.

[Live](https://pinkhominid.github.io/react-v-lit/)

Local
```sh
npm i
npm start
```

repository: pinkhominid/react-v-lit

gha_language: HTML

repo_name: pinkhominid/react-v-lit