import { Component } from '@angular/core';

import { User } from './user/model';

@Component({
  selector: 'app-root',
  template: `
  <main class="container">
    <h1>
      {{title}}
    </h1>
    
    <app-user-form [user]="user"></app-user-form>
    
    <!-- the evidence that the user has changed -->
    <hr> Basic {{user.basicAuth}}
  </main>
  `
})
export class AppComponent {
  title = 'My DORF App';

  // object to be passed to the form
  user: User = new User();
}
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

import { 
  DorfFieldsModule,
  DorfField,
  DorfFieldWrapperComponent,
  DorfGroupWrapperComponent
} from 'dorf';

import { CustomButtonsComponent } from './ext/custom-buttons-component';

import { UserFormComponent } from './user/user-form.component';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    UserFormComponent,
    AppComponent,
    CustomButtonsComponent,
    DorfFieldWrapperComponent,
    DorfGroupWrapperComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    DorfFieldsModule.forRoot({
      css: {
        section: 'row',
        wrapper: 'form-group col-12 row',
        label: 'col-2 col-form-label',
        fieldGeneralization: 'col-10',
        htmlField: 'form-control',
        buttons: {
          save: 'btn btn-primary'
        }
      },
      dorfFields: [{
        tag: DorfField.CHECKBOX,
        css: {
          wrapper: 'checkbox col-12 row',
          htmlField: 'checkbox'
        }
      }],
      requiredWithStar: true
    })
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';

platformBrowserDynamic().bootstrapModule(AppModule);
<!DOCTYPE html>
<html>
  <head>
    <!-- online version, forked from Angular tutorial -->
    <meta charset="utf-8">
    <title>DORF App</title>
  
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://unpkg.com/bootstrap@4.0.0-alpha.6/dist/css/bootstrap.min.css">
    
    <!-- Polyfills -->
    <script src="https://unpkg.com/core-js/client/shim.min.js"></script>

    <script src="https://unpkg.com/zone.js@0.7.4?main=browser"></script>
    <script src="https://unpkg.com/systemjs@0.19.39/dist/system.src.js"></script>

    <script src="systemjs.config.js"></script>
    <script>
      System.import('main.js').catch(function(err){ console.error(err); });
    </script>
  </head>

  <body>
    <app-root>Loading...</app-root>
  </body>
</html>
/**
 * WEB ANGULAR VERSION
 * (based on systemjs.config.js in angular.io)
 * System configuration for Angular samples
 * Adjust as necessary for your application needs.
 */
(function (global) {
  System.config({
    // DEMO ONLY! REAL CODE SHOULD NOT TRANSPILE IN THE BROWSER
    transpiler: 'ts',
    typescriptOptions: {
      // Copy of compiler options in standard tsconfig.json
      "target": "es5",
      "module": "commonjs",
      "moduleResolution": "node",
      "sourceMap": true,
      "emitDecoratorMetadata": true,
      "experimentalDecorators": true,
      "lib": ["es2015", "dom"],
      "noImplicitAny": true,
      "suppressImplicitAnyIndexErrors": true
    },
    meta: {
      'typescript': {
        "exports": "ts"
      }
    },
    paths: {
      // paths serve as alias
      'npm:': 'https://unpkg.com/'
    },
    // map tells the System loader where to look for things
    map: {
      // our app is within the app folder
      'app': 'app',

      // angular bundles
      '@angular/animations': 'npm:@angular/animations/bundles/animations.umd.js',
      '@angular/animations/browser': 'npm:@angular/animations/bundles/animations-browser.umd.js',
      '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
      '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
      '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
      '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
      '@angular/platform-browser/animations': 'npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js',
      '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
      '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
      '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
      '@angular/router/upgrade': 'npm:@angular/router/bundles/router-upgrade.umd.js',
      '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
      '@angular/upgrade': 'npm:@angular/upgrade/bundles/upgrade.umd.js',
      '@angular/upgrade/static': 'npm:@angular/upgrade/bundles/upgrade-static.umd.js',

      // other libraries
      'dorf':                      'npm:dorf@3.2.4',
      
      'rxjs':                      'npm:rxjs@5.0.1',
      'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js',
      'ts':                        'npm:plugin-typescript@5.2.7/lib/plugin.js',
      'typescript':                'npm:typescript@2.3.2/lib/typescript.js',

    },
    // packages tells the System loader how to load when no filename and/or no extension
    packages: {
      app: {
        main: './main.ts',
        defaultExtension: 'ts',
        meta: {
          './*.ts': {
            loader: 'systemjs-angular-loader.js'
          }
        }
      },
      dorf: {
        main: "./index.js",
        defaultExtension: "js"
      },
      rxjs: {
        defaultExtension: 'js'
      }
    }
  });

})(this);
var templateUrlRegex = /templateUrl\s*:(\s*['"`](.*?)['"`]\s*)/gm;
var stylesRegex = /styleUrls *:(\s*\[[^\]]*?\])/g;
var stringRegex = /(['`"])((?:[^\\]\\\1|.)*?)\1/g;

module.exports.translate = function(load){
  if (load.source.indexOf('moduleId') != -1) return load;

  var url = document.createElement('a');
  url.href = load.address;

  var basePathParts = url.pathname.split('/');

  basePathParts.pop();
  var basePath = basePathParts.join('/');

  var baseHref = document.createElement('a');
  baseHref.href = this.baseURL;
  baseHref = baseHref.pathname;

  if (!baseHref.startsWith('/base/')) { // it is not karma
    basePath = basePath.replace(baseHref, '');
  }

  load.source = load.source
    .replace(templateUrlRegex, function(match, quote, url){
      var resolvedUrl = url;

      if (url.startsWith('.')) {
        resolvedUrl = basePath + url.substr(1);
      }

      return 'templateUrl: "' + resolvedUrl + '"';
    })
    .replace(stylesRegex, function(match, relativeUrls) {
      var urls = [];

      while ((match = stringRegex.exec(relativeUrls)) !== null) {
        if (match[2].startsWith('.')) {
          urls.push('"' + basePath + match[2].substr(1) + '"');
        } else {
          urls.push('"' + match[2] + '"');
        }
      }

      return "styleUrls: [" + urls.join(', ') + "]";
    });

  return load;
};
import { Validators } from '@angular/forms';
import { DorfObject, InputType, DorfInput, DorfCheckbox } from 'dorf';

export interface IUser {
  _login: string;
  _password: string;
  _acceptance: boolean;
}

@DorfObject()
export class User {
  @DorfInput({
    label: 'Username',
    type: 'input' as InputType,
    validator: Validators.required
  })
  private _login: string;

  @DorfInput({
    label: 'Password',
    type: 'password' as InputType,
    validator: Validators.required
  })
  private _password: string;

  @DorfCheckbox({
    innerLabel: 'I accept the terms and conditions',
    validator: Validators.requiredTrue
  })
  private _acceptance: boolean;

  constructor(options?: IUser) {
    if (options) {
      this._login = options._login;
      this._password = options._password;
      this._acceptance = options._acceptance;
    }
  }

  update(options?: IUser) {
    if (options) {
      this._login = options._login;
      this._password = options._password;
      this._acceptance = options._acceptance;
    }
  }

  get login() { return this._login; }
  get password() { return btoa(this._password); }
  get acceptance() { return this._acceptance; }

  get basicAuth() {
    if (this._login && this._password) {
      return btoa(`${this._login}:${this._password}`);
    }
  }
}
import { Component, Output, EventEmitter } from '@angular/core';
import { IDorfForm, DorfForm, DorfObjectInput, DorfConfigService } from 'dorf';

import { IUser, User } from './model';

@DorfForm()
@Component({
  selector: 'app-user-form'
})
export class UserFormComponent implements IDorfForm {
  @DorfObjectInput() user: User;

  constructor(public config: DorfConfigService) { }

  onDorfSubmit() {
    this.user.update(this['form'].value as IUser);
  }
}
# DORF QuickStart - part II, overridden component

Dynamic Forms in Angular. Tutorial:
* [_Medium_ version](https://medium.com/@mat3e/creating-angular-dynamic-form-with-dorf-part-ii-af1693d481bf)
* [_mat3e.github.io/dorf_ version](https://mat3e.github.io/dorf/tutorial/quickstart-2.html)
* [_wiki_ version](https://github.com/mat3e/dorf/wiki/QuickStart)
import { Component } from '@angular/core';
import { DorfButtonsComponent } from 'dorf';

@Component({
    selector: 'dorf-buttons',
    template: `
    <section [ngClass]="config.css.buttons?.group">
        <button (click)="submit()" [ngClass]="config.css.buttons?.save" [disabled]="!form || !form.valid">Submit</button>
    </section>
    `
})
export class CustomButtonsComponent extends DorfButtonsComponent { }