diff --git a/CHANGELOG.md b/CHANGELOG.md index c7a3e0ce..3cecb966 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 2.5.7 (2020-08-17) + +- fix #360: VisibleIf-oneOf with 2+ conditions has same property name is not working (daniele-pecora) +- fix #331: visibleIf stopped working in 2.5.2 (daniele-pecora) +- fix #329: visibleIf "allOf" Edge case BUG (daniele-pecora) + # 2.5.6 (2020-07-16) - Fix #358 Completing Public API diff --git a/README.md b/README.md index bb341a3d..3b333b7a 100644 --- a/README.md +++ b/README.md @@ -522,9 +522,16 @@ export class AppComponent { ``` ### 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. -Adding the value `$ANY$` to the array of conditional values,will make the field visible for any value inserted. +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. + +**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. + +**$ANY$** +Adding the value `$ANY$` to the array of conditional values, will make the field visible for any value inserted. ```js @Component({ @@ -564,7 +571,9 @@ export class AppComponent { } } ``` +**$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": { diff --git a/package.json b/package.json index fdb07ce3..62add4cf 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "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/", - "test": "ng test schema-form --watch=false", + "test:lib": "ng test schema-form --watch=false", + "test": "ng test --watch=false", "lint": "ng lint", "get_version": "cat ./projects/schema-form/package.json | grep version | head -1 | awk -F: '{ print $2 }' | sed 's/[\",]//g' | tr -d '[[:space:]]'" }, diff --git a/projects/schema-form/package.json b/projects/schema-form/package.json index 302be0b4..533c2dd8 100644 --- a/projects/schema-form/package.json +++ b/projects/schema-form/package.json @@ -1,6 +1,6 @@ { "name": "ngx-schema-form", - "version": "2.5.6", + "version": "2.5.7", "repository": { "type": "git", "url": "git+https://github.com/guillotinaweb/ngx-schema-form" diff --git a/projects/schema-form/src/lib/model/formproperty.ts b/projects/schema-form/src/lib/model/formproperty.ts index c08b1249..75475a6f 100644 --- a/projects/schema-form/src/lib/model/formproperty.ts +++ b/projects/schema-form/src/lib/model/formproperty.ts @@ -233,7 +233,7 @@ export abstract class FormProperty { targetProperty: FormProperty, dependencyPath: string, value: any = '', - expression: string|string[]|number = ''): boolean { + expression: string | string[] | number | number[] | boolean | boolean[]): boolean { try { let valid = false if (isNaN(expression as number) && (expression as string).indexOf('$ANY$') !== -1) { @@ -252,8 +252,13 @@ export abstract class FormProperty { } } } else { - valid = !!value ? expression.toString() === value.toString() : false; - + const expArray = Array.isArray(expression) ? expression : (expression ? [expression] : []) + for (const expString of expArray) { + valid = !!value ? `${expString}` === `${value}` : false; + if (valid) { + break + } + } } return valid } catch (error) { @@ -266,7 +271,11 @@ export abstract class FormProperty { } } - private __bindVisibility(): boolean { + /** + * binds visibility conditions of type `oneOf` and `allOf`. + * @returns `true` if any visibility binding of type `oneOf` or `allOf` has been processed. Otherwise `false`. + */ + private __bindVisibility_oneOf_or_allOf(): boolean { /** *
      *     "oneOf":[{
@@ -299,9 +308,19 @@ export abstract class FormProperty {
                   if (property) {
                     let valueCheck;
                     if (this.schema.visibleIf.oneOf) {
-                      valueCheck = property.valueChanges.pipe(map(
-                        value => this.__evaluateVisibilityIf(this, property, dependencyPath, value, visibleIf[dependencyPath])
-                      ));
+                      const _chk = (value) => {
+                        for (const item of this.schema.visibleIf.oneOf) {
+                          for (const depPath of Object.keys(item)) {
+                            const prop = this.searchProperty(depPath);
+                            const propVal = prop.value;
+                            if (this.__evaluateVisibilityIf(this, prop, dependencyPath, propVal, item[depPath])) {
+                              return true
+                            }
+                          }
+                        }
+                        return false;
+                      };
+                      valueCheck = property.valueChanges.pipe(map(_chk));
                     } else if (this.schema.visibleIf.allOf) {
                       const _chk = (value) => {
                         for (const item of this.schema.visibleIf.allOf) {
@@ -332,8 +351,11 @@ export abstract class FormProperty {
           }
 
           combineLatest(propertiesBinding, (...values: boolean[]) => {
+            if (this.schema.visibleIf.allOf) {
+              return values.indexOf(false) === -1;
+            }
             return values.indexOf(true) !== -1;
-          }).pipe(distinctUntilChanged()).subscribe((visible) => {
+          }).subscribe((visible) => {
             this.setVisible(visible);
           });
         }
@@ -344,7 +366,7 @@ export abstract class FormProperty {
 
   // A field is visible if AT LEAST ONE of the properties it depends on is visible AND has a value in the list
   public _bindVisibility() {
-    if (this.__bindVisibility())
+    if (this.__bindVisibility_oneOf_or_allOf())
       return;
     let visibleIf = this.schema.visibleIf;
     if (typeof visibleIf === 'object' && Object.keys(visibleIf).length === 0) {
diff --git a/src/app/json-schema-example/json-schema-example.component.spec.ts b/src/app/json-schema-example/json-schema-example.component.spec.ts
index 7da5cda7..4066a1f1 100644
--- a/src/app/json-schema-example/json-schema-example.component.spec.ts
+++ b/src/app/json-schema-example/json-schema-example.component.spec.ts
@@ -19,7 +19,7 @@ describe('JsonSchemaExampleComponent', () => {
   beforeEach(async(() => {
     TestBed.configureTestingModule({
       imports: [
-        SchemaFormModule,
+        SchemaFormModule.forRoot(),
         HttpClientModule
       ],
       declarations: [ JsonSchemaExampleComponent ],
diff --git a/src/app/json-schema-example/json-schema-example.component.ts b/src/app/json-schema-example/json-schema-example.component.ts
index 8e56ddeb..752ee1f9 100644
--- a/src/app/json-schema-example/json-schema-example.component.ts
+++ b/src/app/json-schema-example/json-schema-example.component.ts
@@ -14,6 +14,7 @@ import binding_sample_schema from './binding_sample_schema.json';
 import binding_sample_model from './binding_sample_model.json';
 import binding_sample_bindings from './binding_sample_bindings';
 import visibility_binding_example from './visibility-binding-example-schema.json';
+import visibility_binding_example2 from './visibility-binding-example-schema2.json';
 
 import {AppService, AppData} from '../app.service';
 import {ISchema} from 'ngx-schema-form';
@@ -39,6 +40,7 @@ export class JsonSchemaExampleComponent implements OnInit, OnDestroy {
     {label: 'Sample 2 - Custom bindings', event: this.changeSchemaWithBindings, selected: false},
     {label: 'Sample 3 - Otherschema', event: this.changeSchemaOtherschema, selected: false},
     {label: 'Sample 4 - Visibility binding', event: this.changeSchemaVisibilityBinding, selected: false},
+    {label: 'Sample 5 - Visibility binding 2', event: this.changeSchemaVisibilityBinding2, selected: false},
   ];
 
   constructor(
@@ -212,6 +214,14 @@ export class JsonSchemaExampleComponent implements OnInit, OnDestroy {
     this.actions = {};
   }
 
+  changeSchemaVisibilityBinding2() {
+    this.schema = visibility_binding_example2 as unknown as ISchema;
+    this.model = {};
+    this.fieldBindings = {};
+    this.fieldValidators = {};
+    this.actions = {};
+  }
+
   disableAll() {
     Object.keys(this.schema.properties).map(prop => {
       this.schema.properties[prop].readOnly = true;
diff --git a/src/app/json-schema-example/json-schema-example.component.visibleIf.spec.ts b/src/app/json-schema-example/json-schema-example.component.visibleIf.spec.ts
new file mode 100644
index 00000000..2513c782
--- /dev/null
+++ b/src/app/json-schema-example/json-schema-example.component.visibleIf.spec.ts
@@ -0,0 +1,679 @@
+// - - - - - - - - - - - - - - - - - - 
+// Running only this test is possible on Angular9 with:
+// ng test --include='**/json-schema-example/*.visibleIf.spec.ts' --watch=true`
+// - - - - - - - - - - - - - - - - - - 
+
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { HttpClientModule } from '@angular/common/http';
+import {
+  SchemaFormModule,
+  SchemaValidatorFactory,
+  ZSchemaValidatorFactory,
+  WidgetRegistry,
+  DefaultWidgetRegistry
+} from '../../../projects/schema-form/src/public_api';
+
+
+import { JsonSchemaExampleComponent } from './json-schema-example.component';
+import { By } from '@angular/platform-browser';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+
+describe('JsonSchemaExampleComponent', () => {
+  let component: JsonSchemaExampleComponent;
+  let fixture: ComponentFixture;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        SchemaFormModule.forRoot(),
+        HttpClientModule,
+        FormsModule,
+        ReactiveFormsModule
+      ],
+      declarations: [ JsonSchemaExampleComponent ],
+      providers: [
+        {provide: WidgetRegistry, useClass: DefaultWidgetRegistry},
+        {
+          provide: SchemaValidatorFactory,
+          useClass: ZSchemaValidatorFactory
+        }
+      ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(JsonSchemaExampleComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
+
+
+describe('JsonSchemaExampleComponent - visibleIf - data-types', () => {
+  let component: JsonSchemaExampleComponent;
+  let fixture: ComponentFixture;
+  
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        SchemaFormModule.forRoot(),
+        HttpClientModule,
+        FormsModule,
+        ReactiveFormsModule
+      ],
+      declarations: [ JsonSchemaExampleComponent ],
+      providers: [
+        {provide: WidgetRegistry, useClass: DefaultWidgetRegistry},
+        {
+          provide: SchemaValidatorFactory,
+          useClass: ZSchemaValidatorFactory
+        }
+      ]
+    })
+    .compileComponents();
+  }));
+  
+  beforeEach(() => {
+    fixture = TestBed.createComponent(JsonSchemaExampleComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+/*
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+*/
+  beforeEach(() => {
+
+    // select demo sample
+    const select: HTMLSelectElement = fixture.debugElement.query(By.css('#samples')).nativeElement;
+    select.value = select.options[4].value;  // <-- select a new value
+    select.dispatchEvent(new Event('change'));
+    fixture.detectChanges();
+  });
+
+  it(`# 1. Test boolean as boolean`, async(() => {
+    // Visible component shows up if a boolean value `true` is provided
+
+    const app = component
+
+    fixture.whenStable().then(() => {
+
+      fixture.detectChanges();
+
+      // expect selected sample schema to be loaded
+      expect(app.schema.properties.demo.properties.typeTest.fieldsets[0].description).toEqual('# 1. Test boolean');
+      
+      // expect page containing a sf-form element
+      let sf_form = fixture.debugElement.query(By.css('sf-form'))
+      expect(sf_form).toBeTruthy()
+
+      
+      // initial state
+      let _test_boolean_check = fixture.debugElement.query(By.css('#demo\\.typeTest\\.checkbool'));
+      expect(_test_boolean_check).toBeTruthy()
+
+      let _test_boolean_visible = fixture.debugElement.query(By.css('#demo\\.typeTest\\.testbool'));
+      expect(_test_boolean_visible).toBeNull()
+
+      // positive state
+      _test_boolean_check.nativeElement.checked = true
+      _test_boolean_check.nativeElement.dispatchEvent(new Event('change'));
+      fixture.detectChanges();
+      
+      _test_boolean_visible = fixture.debugElement.query(By.css('#demo\\.typeTest\\.testbool'));
+      expect(_test_boolean_visible).toBeTruthy()
+
+      // negative state
+      _test_boolean_check.nativeElement.checked = false
+      _test_boolean_check.nativeElement.dispatchEvent(new Event('change'));
+      fixture.detectChanges();
+
+      _test_boolean_visible = fixture.debugElement.query(By.css('#demo\\.typeTest\\.testbool'));
+      expect(_test_boolean_visible).toBeNull()
+    });
+
+  }));
+
+  it(`# 1. Test boolean as string`, async(() => {
+    // Visible component shows up if a boolean value `"true"` as string is provided
+
+    const app = component
+
+    fixture.whenStable().then(() => {
+
+      fixture.detectChanges();
+
+      // expect selected sample schema to be loaded
+      expect(app.schema.properties.demo.properties.typeTest.fieldsets[0].description).toEqual('# 1. Test boolean');
+
+      // expect page containing a sf-form element
+      let sf_form = fixture.debugElement.query(By.css('sf-form'))
+      expect(sf_form).toBeTruthy()
+
+      
+      // initial state
+      let _test_boolean_check_true = fixture.debugElement.query(By.css('#demo\\.typeTest\\.checkboolstring\\.true'));
+      expect(_test_boolean_check_true).toBeTruthy()
+      let _test_boolean_check_false = fixture.debugElement.query(By.css('#demo\\.typeTest\\.checkboolstring\\.false'));
+      expect(_test_boolean_check_false).toBeTruthy()
+
+      let _test_boolean_visible = fixture.debugElement.query(By.css('#demo\\.typeTest\\.testboolstring'));
+      expect(_test_boolean_visible).toBeNull()
+
+      // positive state
+      _test_boolean_check_true.nativeElement.checked = true
+      _test_boolean_check_true.nativeElement.dispatchEvent(new Event('change'));
+      fixture.detectChanges();
+
+      _test_boolean_visible = fixture.debugElement.query(By.css('#demo\\.typeTest\\.testboolstring'));
+      expect(_test_boolean_visible).toBeTruthy()
+
+      // negative state
+      _test_boolean_check_false.nativeElement.checked = false
+      _test_boolean_check_false.nativeElement.dispatchEvent(new Event('change'));
+      fixture.detectChanges();
+
+      _test_boolean_visible = fixture.debugElement.query(By.css('#demo\\.typeTest\\.testboolstring'));
+      expect(_test_boolean_visible).toBeNull()
+    });
+  }));
+
+  it(`# 2. Test number`, async(() => {
+    // Visible component shows up if a number value `1` is provided
+
+    const app = component
+
+    fixture.whenStable().then(() => {
+
+      fixture.detectChanges();
+
+      // expect selected sample schema to be loaded
+      expect(app.schema.properties.demo.properties.typeTest.fieldsets[1].description).toEqual('# 2. Test number');
+
+      // expect page containing a sf-form element
+      let sf_form = fixture.debugElement.query(By.css('sf-form'))
+      expect(sf_form).toBeTruthy()
+
+      
+      // initial state
+      let _test_number_input = fixture.debugElement.query(By.css('#demo\\.typeTest\\.checknum'));
+      expect(_test_number_input).toBeTruthy()
+
+      let _test_number_visible = fixture.debugElement.query(By.css('#demo\\.typeTest\\.testnum'));
+      expect(_test_number_visible).toBeNull()
+
+      // positive state
+      _test_number_input.nativeElement.value = '1'
+      _test_number_input.nativeElement.dispatchEvent(new Event('input'));
+      fixture.detectChanges();
+
+      _test_number_visible = fixture.debugElement.query(By.css('#demo\\.typeTest\\.testnum'));
+      expect(_test_number_visible).toBeTruthy()
+
+      // negative state
+      _test_number_input.nativeElement.value = '2'
+      _test_number_input.nativeElement.dispatchEvent(new Event('input'));
+      fixture.detectChanges();
+
+      _test_number_visible = fixture.debugElement.query(By.css('#demo\\.typeTest\\.testnum'));
+      expect(_test_number_visible).toBeNull()
+    });
+  }));
+
+  it(`# 2. Test number as string`, async(() => {
+    // Visible component shows up if a number value `"1"` as string is provided
+
+    const app = component
+
+    fixture.whenStable().then(() => {
+
+      fixture.detectChanges();
+
+      // expect selected sample schema to be loaded
+      expect(app.schema.properties.demo.properties.typeTest.fieldsets[1].description).toEqual('# 2. Test number');
+
+      // expect page containing a sf-form element
+      let sf_form = fixture.debugElement.query(By.css('sf-form'))
+      expect(sf_form).toBeTruthy()
+
+      
+      // initial state
+      let _test_number_select = fixture.debugElement.query(By.css('#demo\\.typeTest\\.checknumstring'));
+      expect(_test_number_select).toBeTruthy()
+
+      let _test_number_visible = fixture.debugElement.query(By.css('#demo\\.typeTest\\.testnumstring'));
+      expect(_test_number_visible).toBeNull()
+
+      // positive state
+      _test_number_select.nativeElement.value = _test_number_select.nativeElement.options[1].value;  // set to '1'
+      _test_number_select.nativeElement.dispatchEvent(new Event('change'));
+      fixture.detectChanges();
+
+      _test_number_visible = fixture.debugElement.query(By.css('#demo\\.typeTest\\.testnumstring'));
+      expect(_test_number_visible).toBeTruthy()
+
+      // negative state
+      _test_number_select.nativeElement.value = _test_number_select.nativeElement.options[2].value;  // set to '2'
+      _test_number_select.nativeElement.dispatchEvent(new Event('change'));
+      fixture.detectChanges();
+
+      _test_number_visible = fixture.debugElement.query(By.css('#demo\\.typeTest\\.testnumstring'));
+      expect(_test_number_visible).toBeNull()
+    });
+  }));
+
+  it(`# 3. Test string`, async(() => {
+    // Visible component shows up if a string value `"a"` is provided
+
+    const app = component
+
+    fixture.whenStable().then(() => {
+
+      fixture.detectChanges();
+
+      // expect selected sample schema to be loaded
+      expect(app.schema.properties.demo.properties.typeTest.fieldsets[2].description).toEqual('# 3. Test string');
+
+      // expect page containing a sf-form element
+      let sf_form = fixture.debugElement.query(By.css('sf-form'))
+      expect(sf_form).toBeTruthy()
+
+      
+      // initial state
+      let _test_string_input = fixture.debugElement.query(By.css('#demo\\.typeTest\\.checkstring'));
+      expect(_test_string_input).toBeTruthy()
+
+      let _test_number_visible = fixture.debugElement.query(By.css('#demo\\.typeTest\\.teststring'));
+      expect(_test_number_visible).toBeNull()
+
+      // positive state
+      _test_string_input.nativeElement.value = 'a'
+      _test_string_input.nativeElement.dispatchEvent(new Event('input'));
+      fixture.detectChanges();
+
+      _test_number_visible = fixture.debugElement.query(By.css('#demo\\.typeTest\\.teststring'));
+      expect(_test_number_visible).toBeTruthy()
+
+      // negative state
+      _test_string_input.nativeElement.value = 'z'
+      _test_string_input.nativeElement.dispatchEvent(new Event('input'));
+      fixture.detectChanges();
+
+      _test_number_visible = fixture.debugElement.query(By.css('#demo\\.typeTest\\.teststring'));
+      expect(_test_number_visible).toBeNull()
+    });
+  }));
+
+
+});
+
+
+describe('JsonSchemaExampleComponent - visibleIf - condition-types', () => {
+  let component: JsonSchemaExampleComponent;
+  let fixture: ComponentFixture;
+  
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        SchemaFormModule.forRoot(),
+        HttpClientModule,
+        FormsModule,
+        ReactiveFormsModule
+      ],
+      declarations: [ JsonSchemaExampleComponent ],
+      providers: [
+        {provide: WidgetRegistry, useClass: DefaultWidgetRegistry},
+        {
+          provide: SchemaValidatorFactory,
+          useClass: ZSchemaValidatorFactory
+        }
+      ]
+    })
+    .compileComponents();
+  }));
+  
+  beforeEach(() => {
+    fixture = TestBed.createComponent(JsonSchemaExampleComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+/*
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+*/
+  beforeEach(() => {
+
+    // select demo sample
+    const select: HTMLSelectElement = fixture.debugElement.query(By.css('#samples')).nativeElement;
+    select.value = select.options[4].value;  // <-- select a new value
+    select.dispatchEvent(new Event('change'));
+    fixture.detectChanges();
+  });
+
+  it(`# 4. Test 'VisibleIf' with default 'one-of' with multiple values`, async(() => {
+    // Visible component shows up if status value is 'Warn' or 'Fail'
+
+    fixture.whenStable().then(() => {
+
+      fixture.detectChanges();
+
+      // expect page containing a sf-form element
+      let sf_form = fixture.debugElement.query(By.css('sf-form'))
+      expect(sf_form).toBeTruthy()
+
+      
+      // initial state
+      let _test_boolean_check_pass = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding1a\\.status1a\\.Pass'));
+      expect(_test_boolean_check_pass).toBeTruthy()
+      let _test_boolean_check_warn = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding1a\\.status1a\\.Warn'));
+      expect(_test_boolean_check_warn).toBeTruthy()
+      let _test_boolean_check_fail = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding1a\\.status1a\\.Fail'));
+      expect(_test_boolean_check_fail).toBeTruthy()
+
+      let _test_boolean_visible = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding1a\\.visibleComponent1a'));
+      expect(_test_boolean_visible).toBeNull()
+
+      // negative state 'Pass'
+      _test_boolean_check_pass.nativeElement.checked = true
+      _test_boolean_check_pass.nativeElement.dispatchEvent(new Event('change'));
+      fixture.detectChanges();
+
+      _test_boolean_visible = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding1a\\.visibleComponent1a'));
+      expect(_test_boolean_visible).toBeNull()
+
+      // positive state 'Warn'
+      _test_boolean_check_warn.nativeElement.checked = true
+      _test_boolean_check_warn.nativeElement.dispatchEvent(new Event('change'));
+      fixture.detectChanges();
+      
+      _test_boolean_visible = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding1a\\.visibleComponent1a'));
+      expect(_test_boolean_visible).toBeTruthy()
+
+      // negative state 'Pass'
+      _test_boolean_check_pass.nativeElement.checked = true
+      _test_boolean_check_pass.nativeElement.dispatchEvent(new Event('change'));
+      fixture.detectChanges();
+
+      _test_boolean_visible = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding1a\\.visibleComponent1a'));
+      expect(_test_boolean_visible).toBeNull()
+
+      // positive state 'Fail'
+      _test_boolean_check_fail.nativeElement.checked = true
+      _test_boolean_check_fail.nativeElement.dispatchEvent(new Event('change'));
+      fixture.detectChanges();
+      
+      _test_boolean_visible = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding1a\\.visibleComponent1a'));
+      expect(_test_boolean_visible).toBeTruthy()
+
+    });
+
+  }));
+
+
+  it(`# 5. Test 'VisibleIf' with 'oneOf' condition`, async(() => {
+    // Visible component shows up if status value is 'Warn' or 'Fail'
+
+    fixture.whenStable().then(() => {
+
+      fixture.detectChanges();
+
+      // expect page containing a sf-form element
+      let sf_form = fixture.debugElement.query(By.css('sf-form'))
+      expect(sf_form).toBeTruthy()
+
+      
+      // initial state
+      let _test_boolean_check_pass = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding1b\\.status1b\\.Pass'));
+      expect(_test_boolean_check_pass).toBeTruthy()
+      let _test_boolean_check_warn = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding1b\\.status1b\\.Warn'));
+      expect(_test_boolean_check_warn).toBeTruthy()
+      let _test_boolean_check_fail = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding1b\\.status1b\\.Fail'));
+      expect(_test_boolean_check_fail).toBeTruthy()
+
+      let _test_boolean_visible = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding1a\\.visibleComponent1b'));
+      expect(_test_boolean_visible).toBeNull()
+
+      // negative state 'Pass'
+      _test_boolean_check_pass.nativeElement.checked = true
+      _test_boolean_check_pass.nativeElement.dispatchEvent(new Event('change'));
+      fixture.detectChanges();
+
+      _test_boolean_visible = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding1b\\.visibleComponent1b'));
+      expect(_test_boolean_visible).toBeNull()
+
+      // positive state 'Warn'
+      _test_boolean_check_warn.nativeElement.checked = true
+      _test_boolean_check_warn.nativeElement.dispatchEvent(new Event('change'));
+      fixture.detectChanges();
+      
+      _test_boolean_visible = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding1b\\.visibleComponent1b'));
+      expect(_test_boolean_visible).toBeTruthy()
+
+      // negative state 'Pass'
+      _test_boolean_check_pass.nativeElement.checked = true
+      _test_boolean_check_pass.nativeElement.dispatchEvent(new Event('change'));
+      fixture.detectChanges();
+
+      _test_boolean_visible = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding1b\\.visibleComponent1b'));
+      expect(_test_boolean_visible).toBeNull()
+
+      // positive state 'Fail'
+      _test_boolean_check_fail.nativeElement.checked = true
+      _test_boolean_check_fail.nativeElement.dispatchEvent(new Event('change'));
+      fixture.detectChanges();
+      
+      _test_boolean_visible = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding1b\\.visibleComponent1b'));
+      expect(_test_boolean_visible).toBeTruthy()
+
+    });
+
+  }));
+
+  it(`# 6. Test Boolean 'VisibleIf' with 'allOf' condition (check 'Warn' and 'Fail')`, async(() => {
+    // Visible component shows up if status 'Warn' and 'Fail' are checked
+
+    fixture.whenStable().then(() => {
+
+      fixture.detectChanges();
+
+      // expect page containing a sf-form element
+      let sf_form = fixture.debugElement.query(By.css('sf-form'))
+      expect(sf_form).toBeTruthy()
+
+      
+      // initial state
+      let _test_checkbox_pass = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding2a\\.status2a'));
+      expect(_test_checkbox_pass).toBeTruthy()
+      let _test_checkbox_warn = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding2a\\.status2b'));
+      expect(_test_checkbox_warn).toBeTruthy()
+      let _test_checkbox_fail = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding2a\\.status2c'));
+      expect(_test_checkbox_fail).toBeTruthy()
+
+      let _test_boolean_visible = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding2a\\.visibleComponent2a'));
+      expect(_test_boolean_visible).toBeNull()
+      
+      const visibleComponent = '#demo\\.visibleIfBinding2a\\.visibleComponent2a'
+      const checkboxes = [
+        _test_checkbox_pass,
+        _test_checkbox_warn,
+        _test_checkbox_fail
+      ]
+
+      let combinations = [
+        //         'Pass', 'Warn', 'Fail', 'Should component show up?'
+        { values: [false, false, false], visible: false, emit:false }, // the initial state
+        { values: [true, false, false], visible: false, emit:false }, 
+        { values: [false, true, false], visible: false, emit:false }, 
+        { values: [false, false, true], visible: false, emit:false }, 
+        { values: [true, true, false], visible: false, emit:false }, 
+        { values: [true, true, true], visible: true, emit:false }, 
+        { values: [false, true, true], visible: true, emit:false }
+      ]
+      combinations = combinations.concat(
+        // same as above but this forces emitting the change event
+        combinations.map(item => { item.emit = true; return item }))
+
+      for (const combination of combinations) {
+        for (let i = 0; i < combination.values.length; i++) {
+          if (checkboxes[i].nativeElement.checked !== combination.values[i] || combination.emit) {
+            checkboxes[i].nativeElement.checked = combination.values[i]
+            checkboxes[i].nativeElement.dispatchEvent(new Event('change'));
+          }
+          fixture.detectChanges();
+
+          const _test_boolean_visible_el = fixture.debugElement.query(By.css(visibleComponent));
+          const errorOut=`Expected visibility ${combination.visible} | emits: ${combination.emit||false} | checked: pass:${combination.values[0]}/native:${checkboxes[0].nativeElement.checked}, warn:${combination.values[1]}/native:${checkboxes[1].nativeElement.checked}, fail:${combination.values[2]}/native:${checkboxes[2].nativeElement.checked}`
+          if(_test_checkbox_warn.nativeElement.checked && _test_checkbox_fail.nativeElement.checked){
+            expect(_test_boolean_visible_el).toBeTruthy(errorOut)
+          } else {
+            expect(_test_boolean_visible_el).toBeNull(errorOut)
+          }
+        }
+      }
+
+    });
+
+  }));
+
+  it(`# 7. Test String 'VisibleIf' with 'allOf' condition (select 'Warn' and 'Fail')`, async(() => {
+    // Visible component shows up if status 'Warn' and 'Fail' are checked
+
+    fixture.whenStable().then(() => {
+
+      fixture.detectChanges();
+
+      // expect page containing a sf-form element
+      let sf_form = fixture.debugElement.query(By.css('sf-form'))
+      expect(sf_form).toBeTruthy()
+
+      
+      // initial state
+      let _test_select_pass = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding2b\\.status2a'));
+      expect(_test_select_pass).toBeTruthy()
+      let _test_select_warn = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding2b\\.status2b'));
+      expect(_test_select_warn).toBeTruthy()
+      let _test_select_fail = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding2b\\.status2c'));
+      expect(_test_select_fail).toBeTruthy()
+
+      let _test_select_visible = fixture.debugElement.query(By.css('#demo\\.visibleIfBinding2b\\.visibleComponent2b'));
+      expect(_test_select_visible).toBeNull()
+      
+      const visibleComponent = '#demo\\.visibleIfBinding2b\\.visibleComponent2b'
+      const dropdowns = [
+        _test_select_pass,
+        _test_select_warn,
+        _test_select_fail
+      ]
+
+      let combinations = [
+        //         'Pass', 'Warn', 'Fail', 'Should component show up?'
+        { values: ['', '', ''], visible: false, emit:false }, // the initial state
+
+        { values: ['Pass', '', ''], visible: false, emit:false }, 
+        { values: ['', 'Warn', ''], visible: false, emit:false }, 
+        { values: ['', '', 'Fail'], visible: false, emit:false },
+        { values: ['', 'Pass', 'Fail'], visible: true, emit:false }, 
+        { values: ['Pass', 'Warn', 'Pass'], visible: true, emit:false },
+        { values: ['', 'Warn', 'Fail'], visible: true, emit:false }, 
+        { values: ['Pass', 'Warn', 'Fail'], visible: true, emit:false }
+
+      ]
+      combinations = combinations.concat(
+        // same as above but this forces emitting the change event
+        combinations.map(item => { item.emit = true; return item }))
+
+      for (const combination of combinations) {
+        for (let i = 0; i < combination.values.length; i++) {
+          if (dropdowns[i].nativeElement.value !== combination.values[i] || combination.emit) {
+            dropdowns[i].nativeElement.value = combination.values[i]
+            dropdowns[i].nativeElement.dispatchEvent(new Event('change'));
+          }
+          fixture.detectChanges();
+
+          const _test_select_visible_el = fixture.debugElement.query(By.css(visibleComponent));
+          const errorOut=`Expected visibility ${combination.visible} | emits: ${combination.emit||false} | checked: pass:${combination.values[0]}/native:${dropdowns[0].nativeElement.value}, warn:${combination.values[1]}/native:${dropdowns[1].nativeElement.value}, fail:${combination.values[2]}/native:${dropdowns[2].nativeElement.value}`
+          if (_test_select_warn.nativeElement.value === 'Warn' && _test_select_fail.nativeElement.value === 'Fail') {
+            expect(_test_select_visible_el).toBeTruthy(errorOut)
+          } else {
+            expect(_test_select_visible_el).toBeNull(errorOut)
+          }
+        }
+      }
+
+    });
+
+  }));
+
+  it(`# 8. Test oneOf - Set age to 15, set last name to 'aaa'`, async(() => {
+    // Visible component shows up if age is set to 15 and last name to 'aaa'
+
+    fixture.whenStable().then(() => {
+
+      fixture.detectChanges();
+
+      // expect page containing a sf-form element
+      let sf_form = fixture.debugElement.query(By.css('sf-form'))
+      expect(sf_form).toBeTruthy()
+
+      
+      // initial state
+      let _test_input_age = fixture.debugElement.query(By.css('#demo\\.updateVisibiltyTest\\.age'));
+      expect(_test_input_age).toBeTruthy()
+      let _test_select_lastname = fixture.debugElement.query(By.css('#demo\\.updateVisibiltyTest\\.lastName'));
+      expect(_test_select_lastname).toBeTruthy()
+      
+      let _test_visible_component = fixture.debugElement.query(By.css('#demo\\.updateVisibiltyTest\\.firstName'));
+      expect(_test_visible_component).toBeNull()
+
+      let visibleComponent = '#demo\\.updateVisibiltyTest\\.firstName'
+      let elements = [
+        _test_input_age,
+        _test_select_lastname
+      ]
+      
+      let combinations = [
+        //         'Pass', 'Warn', 'Fail', 'Should component show up?'
+        { values: ['', ''], visible: false, emit:false }, // the initial state
+        { values: [0, ''], visible: false, emit:false },
+        { values: [0, 'bbb'], visible: false, emit:false },
+        { values: [0, 'aaa'], visible: false, emit:false },
+        { values: [15, 'aaa'], visible: true, emit:false },
+        { values: [15, 'bbb'], visible: false, emit:false },
+        { values: [155, 'aaa'], visible: false, emit:false },
+
+      ]
+      combinations = combinations.concat(
+        // same as above but this forces emitting the change event
+        combinations.map(item => { item.emit = true; return item }))
+
+      for (const combination of combinations) {
+        for (let i = 0; i < combination.values.length; i++) {
+          if (elements[i].nativeElement.value !== combination.values[i] || combination.emit) {
+            elements[i].nativeElement.value = combination.values[i]
+            elements[i].nativeElement.dispatchEvent(new Event('change'));
+          }
+          fixture.detectChanges();
+
+          const _test_select_visible_el = fixture.debugElement.query(By.css(visibleComponent));
+          const errorOut=`Expected visibility ${combination.visible} | emits: ${combination.emit||false} | checked: age:${combination.values[0]}/native:${elements[0].nativeElement.value}, firstname:${combination.values[1]}/native:${elements[1].nativeElement.value}`
+          if (_test_input_age.nativeElement.value === 15 && _test_select_lastname.nativeElement.value === 'aaa') {
+            expect(_test_select_visible_el).toBeTruthy(errorOut)
+          } else {
+            expect(_test_select_visible_el).toBeNull(errorOut)
+          }
+        }
+      }
+
+    });
+
+  }));
+
+});
+
diff --git a/src/app/json-schema-example/visibility-binding-example-schema2.json b/src/app/json-schema-example/visibility-binding-example-schema2.json
new file mode 100644
index 00000000..386c1b76
--- /dev/null
+++ b/src/app/json-schema-example/visibility-binding-example-schema2.json
@@ -0,0 +1,442 @@
+{
+  "properties": {
+      "demo": {
+          "type": "object",
+          "properties": {
+              "typeTest": {
+                  "fieldsets": [
+                      {
+                          "id": "bool",
+                          "title": "",
+                          "description": "# 1. Test boolean",
+                          "name": "",
+                          "fields": [
+                              "checkbool",
+                              "testbool",
+                              "checkboolstring",
+                              "testboolstring"
+                          ]
+                      },
+                      {
+                          "id": "num",
+                          "title": "",
+                          "description": "# 2. Test number",
+                          "name": "",
+                          "fields": [
+                              "checknum",
+                              "testnum",
+                              "checknumstring",
+                              "testnumstring"
+                          ]
+                      },
+                      {
+                          "id": "num",
+                          "title": "",
+                          "description": "# 3. Test string",
+                          "name": "",
+                          "fields": [
+                              "checkstring",
+                              "teststring"
+                          ]
+                      }
+                  ],
+                  "type": "object",
+                  "properties": {
+                      "checkbool": {
+                          "type": "boolean",
+                          "description": "Boolean test (true) as boolean"
+                      },
+                      "testbool": {
+                          "type": "string",
+                          "description": "Visible if value is 'true' as boolean",
+                          "visibleIf": {
+                              "checkbool": true
+                          }
+                      },
+                      "checkboolstring": {
+                          "type": "string",
+                          "widget": "radio",
+                          "description": "Boolean test (\"true\") as string",
+                          "oneOf": [
+                              {
+                                  "description": "String 'true'",
+                                  "enum": [
+                                      "true"
+                                  ]
+                              },
+                              {
+                                  "description": "String 'false'",
+                                  "enum": [
+                                      "false"
+                                  ]
+                              }
+                          ]
+                      },
+                      "testboolstring": {
+                          "type": "string",
+                          "description": "Visible if value is 'true' as string",
+                          "visibleIf": {
+                              "checkboolstring": "true"
+                          }
+                      },
+                      "checknum": {
+                          "type": "number",
+                          "description": "Number test (1)"
+                      },
+                      "testnum": {
+                          "type": "string",
+                          "description": "Visible if value is '1' as number",
+                          "visibleIf": {
+                              "checknum": 1
+                          }
+                      },
+                      "checknumstring": {
+                          "type": "string",
+                          "description": "Number test (\"1\") as string",
+                          "widget": "select",
+                          "oneOf": [
+                              {
+                                  "description": "Select a number",
+                                  "enum": [
+                                      ""
+                                  ]
+                              },
+                              {
+                                  "description": "Number 1",
+                                  "enum": [
+                                      "1"
+                                  ]
+                              },
+                              {
+                                  "description": "Number 2",
+                                  "enum": [
+                                      "2"
+                                  ]
+                              }
+                          ]
+                      },
+                      "testnumstring": {
+                          "type": "string",
+                          "description": "Visible if value is '1' as string",
+                          "visibleIf": {
+                              "checknumstring": "1"
+                          }
+                      },
+                      "checkstring": {
+                          "type": "string",
+                          "description": "String test (a)"
+                      },
+                      "teststring": {
+                          "type": "string",
+                          "description": "Visible if value is 'a' as string",
+                          "visibleIf": {
+                              "checkstring": "a"
+                          }
+                      }
+                  }
+              },
+              "visibleIfBinding1a": {
+                  "description": "# 4. Test 'VisibleIf' with default 'one-of' with multiple values",
+                  "type": "object",
+                  "properties": {
+                      "status1a": {
+                          "type": "string",
+                          "description": "Visible component shows up if status is 'Warn' or 'Fail'",
+                          "oneOf": [
+                              {
+                                  "description": "Pass",
+                                  "enum": [
+                                      "Pass"
+                                  ]
+                              },
+                              {
+                                  "description": "Warn",
+                                  "enum": [
+                                      "Warn"
+                                  ]
+                              },
+                              {
+                                  "description": "Fail",
+                                  "enum": [
+                                      "Fail"
+                                  ]
+                              }
+                          ],
+                          "widget": "radio"
+                      },
+                      "visibleComponent1a": {
+                          "type": "string",
+                          "description": "Visible component if status is 'Warn' or 'Fail'",
+                          "visibleIf": {
+                              "/demo/visibleIfBinding1a/status1a": [
+                                  "Warn",
+                                  "Fail"
+                              ]
+                          }
+                      }
+                  }
+              },
+              "visibleIfBinding1b": {
+                  "description": "# 5. Test 'VisibleIf' with 'oneOf' condition",
+                  "type": "object",
+                  "properties": {
+                      "status1b": {
+                          "type": "string",
+                          "description": "Visible component shows up if status is 'Warn' or 'Fail'",
+                          "oneOf": [
+                              {
+                                  "description": "Pass",
+                                  "enum": [
+                                      "Pass"
+                                  ]
+                              },
+                              {
+                                  "description": "Warn",
+                                  "enum": [
+                                      "Warn"
+                                  ]
+                              },
+                              {
+                                  "description": "Fail",
+                                  "enum": [
+                                      "Fail"
+                                  ]
+                              }
+                          ],
+                          "widget": "radio"
+                      },
+                      "visibleComponent1b": {
+                          "type": "string",
+                          "description": "Visible component if status is 'Warn' or 'Fail'",
+                          "visibleIf": {
+                              "oneOf": [
+                                  {
+                                      "/demo/visibleIfBinding1b/status1b": [
+                                          "Warn"
+                                      ]
+                                  },
+                                  {
+                                      "/demo/visibleIfBinding1b/status1b": [
+                                          "Fail"
+                                      ]
+                                  }
+                              ]
+                          }
+                      }
+                  }
+              },
+              "visibleIfBinding2a": {
+                  "description": "# 6. Test Boolean 'VisibleIf' with 'allOf' condition (check 'Warn' and 'Fail')",
+                  "type": "object",
+                  "properties": {
+                      "status2a": {
+                          "type": "boolean",
+                          "description": "Pass",
+                          "widget": "checkbox"
+                      },
+                      "status2b": {
+                          "type": "boolean",
+                          "description": "Warn",
+                          "widget": "checkbox"
+                      },
+                      "status2c": {
+                          "type": "boolean",
+                          "description": "Fail",
+                          "widget": "checkbox"
+                      },
+                      "visibleComponent2a": {
+                          "type": "string",
+                          "description": "Visible component if status 'Warn' and 'Fail' are checked",
+                          "visibleIf": {
+                              "allOf": [
+                                  {
+                                      "/demo/visibleIfBinding2a/status2b": [
+                                          true
+                                      ]
+                                  },
+                                  {
+                                      "/demo/visibleIfBinding2a/status2c": [
+                                          true
+                                      ]
+                                  }
+                              ]
+                          }
+                      }
+                  }
+              },
+              "visibleIfBinding2b": {
+                  "description": "# 7. Test String 'VisibleIf' with 'allOf' condition (select 'Warn' and 'Fail')",
+                  "type": "object",
+                  "properties": {
+                      "status2a": {
+                          "type": "string",
+                          "oneOf": [
+                              {
+                                  "description": "...",
+                                  "enum": [
+                                      ""
+                                  ]
+                              },
+                              {
+                                  "description": "Pass",
+                                  "enum": [
+                                      "Pass"
+                                  ]
+                              },
+                              {
+                                  "description": "Warn",
+                                  "enum": [
+                                      "Warn"
+                                  ]
+                              },
+                              {
+                                  "description": "Fail",
+                                  "enum": [
+                                      "Fail"
+                                  ]
+                              }
+                          ],
+                          "widget": "select"
+                      },
+                      "status2b": {
+                          "type": "string",
+                          "oneOf": [
+                              {
+                                  "description": "Select 'Warn' here",
+                                  "enum": [
+                                      ""
+                                  ]
+                              },
+                              {
+                                  "description": "Pass",
+                                  "enum": [
+                                      "Pass"
+                                  ]
+                              },
+                              {
+                                  "description": "Warn",
+                                  "enum": [
+                                      "Warn"
+                                  ]
+                              },
+                              {
+                                  "description": "Fail",
+                                  "enum": [
+                                      "Fail"
+                                  ]
+                              }
+                          ],
+                          "widget": "select"
+                      },
+                      "status2c": {
+                          "type": "string",
+                          "oneOf": [
+                              {
+                                  "description": "Select 'Fail' here",
+                                  "enum": [
+                                      ""
+                                  ]
+                              },
+                              {
+                                  "description": "Pass",
+                                  "enum": [
+                                      "Pass"
+                                  ]
+                              },
+                              {
+                                  "description": "Warn",
+                                  "enum": [
+                                      "Warn"
+                                  ]
+                              },
+                              {
+                                  "description": "Fail",
+                                  "enum": [
+                                      "Fail"
+                                  ]
+                              }
+                          ],
+                          "widget": "select"
+                      },
+                      "visibleComponent2b": {
+                          "type": "string",
+                          "description": "Visible component if status 'Warn' and 'Fail' are checked",
+                          "visibleIf": {
+                              "allOf": [
+                                  {
+                                      "/demo/visibleIfBinding2b/status2b": [
+                                          "Warn"
+                                      ]
+                                  },
+                                  {
+                                      "/demo/visibleIfBinding2b/status2c": [
+                                          "Fail"
+                                      ]
+                                  }
+                              ]
+                          }
+                      }
+                  }
+              },
+              "updateVisibiltyTest": {
+                  "type": "object",
+                  "description": "Test oneOf - Set age to 15, set last name to 'aaa'",
+                  "properties": {
+                      "age": {
+                          "id": "age",
+                          "name": "age",
+                          "title": "Age",
+                          "type": "string",
+                          "widget": {
+                              "id": "string"
+                          }
+                      },
+                      "firstName": {
+                          "id": "firstName",
+                          "name": "firstName",
+                          "title": "First Name",
+                          "type": "string",
+                          "visibleIfOperator": "and",
+                          "widget": {
+                              "id": "string"
+                          },
+                          "visibleIf": {
+                              "allOf": [
+                                  {
+                                      "lastName": "aaa"
+                                  },
+                                  {
+                                      "age": 15
+                                  }
+                              ]
+                          }
+                      },
+                      "lastName": {
+                          "id": "lastName",
+                          "name": "lastName",
+                          "title": "Last Name",
+                          "type": "string",
+                          "widget": {
+                              "id": "select"
+                          },
+                          "oneOf": [
+                              {
+                                  "description": "AAA",
+                                  "enum": [
+                                      "aaa"
+                                  ]
+                              },
+                              {
+                                  "description": "BBB",
+                                  "enum": [
+                                      "bbb"
+                                  ]
+                              }
+                          ]
+                      }
+                  }
+              }
+          }
+      }
+  }
+}
diff --git a/src/app/template-schema-example/template-schema-example.component.html b/src/app/template-schema-example/template-schema-example.component.html
index 9ee8d623..3f326505 100644
--- a/src/app/template-schema-example/template-schema-example.component.html
+++ b/src/app/template-schema-example/template-schema-example.component.html
@@ -2,7 +2,10 @@
     

Form:

- + Part 1 - Recipient Template:

Model:

-
{{model | json}}
+
{{value | json}}
diff --git a/src/app/template-schema-example/template-schema-example.component.spec.ts b/src/app/template-schema-example/template-schema-example.component.spec.ts index 911df53d..a2e539d3 100644 --- a/src/app/template-schema-example/template-schema-example.component.spec.ts +++ b/src/app/template-schema-example/template-schema-example.component.spec.ts @@ -21,7 +21,7 @@ describe('TemplateSchemaExampleComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ - SchemaFormModule, + SchemaFormModule.forRoot(), TemplateSchemaModule, HttpClientModule, FormsModule diff --git a/src/app/template-schema-example/template-schema-example.component.ts b/src/app/template-schema-example/template-schema-example.component.ts index 56553239..acd68c0c 100644 --- a/src/app/template-schema-example/template-schema-example.component.ts +++ b/src/app/template-schema-example/template-schema-example.component.ts @@ -8,6 +8,11 @@ import { Component, OnInit } from '@angular/core'; export class TemplateSchemaExampleComponent implements OnInit { model: any = {}; + /** + * Using a separate variable for showing the model prevents from: + * `Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value:` + */ + value; constructor() { } @@ -18,4 +23,18 @@ export class TemplateSchemaExampleComponent implements OnInit { ngOnInit() { } + setValue(value) { + if (undefined === this.value) { + /** + * If the first time the variable is set, then setting timeout will prevents error: + * `Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value:` + */ + setTimeout(() => { + this.value = value; + }, 0); + return + } + this.value = value; + } + }