Skip to content
This repository has been archived by the owner on Sep 16, 2022. It is now read-only.

Commit

Permalink
Create an AbstractControlGroup and AbstractForm which will allow diff…
Browse files Browse the repository at this point in the history
…erent infrastructure to create different implementation of ControlGroups and Forms with different backing values.

Example usage: cl/205017920

Global Presubmit: https://test.corp.google.com/OCL:204254973:BASE:205149190:1531953387644:95fc22b0

PiperOrigin-RevId: 205178408
  • Loading branch information
TedSander authored and matanlurey committed Jul 19, 2018
1 parent 77ef296 commit 50c9e5e
Show file tree
Hide file tree
Showing 22 changed files with 120 additions and 147 deletions.
6 changes: 6 additions & 0 deletions angular_forms/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
### New Features

* Add AbstractControlGroup and AbstractNgForm to allow infrastructure to
create their own form systems that can be backed by types such as a proto,
or have different control group logic.

### Breaking Changes

* Use value from AbstractControl for valueChanges event instead of internal
Expand Down
8 changes: 7 additions & 1 deletion angular_forms/lib/angular_forms.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export 'src/directives.dart'
setUpControlGroup,
formDirectives,
AbstractControlDirective,
AbstractNgForm,
ChangeFunction,
CheckboxControlValueAccessor,
ControlContainer,
Expand Down Expand Up @@ -48,7 +49,12 @@ export 'src/directives.dart'
ValidatorFn;
export 'src/form_builder.dart' show FormBuilder;
export 'src/model.dart'
show AbstractControl, Control, ControlGroup, ControlArray;
show
AbstractControl,
Control,
AbstractControlGroup,
ControlGroup,
ControlArray;
export 'src/validators.dart' show NG_VALIDATORS, Validators;

/// Shorthand set of providers used for building Angular forms.
Expand Down
2 changes: 1 addition & 1 deletion angular_forms/lib/src/directives.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export 'directives/ng_control_group.dart' show NgControlGroup;
export 'directives/ng_control_name.dart' show NgControlName;
// ignore: deprecated_member_use
export 'directives/ng_control_status.dart' show NgControlStatus;
export 'directives/ng_form.dart' show NgForm;
export 'directives/ng_form.dart' show NgForm, AbstractNgForm;
export 'directives/ng_form_control.dart' show NgFormControl;
export 'directives/ng_form_model.dart' show NgFormModel;
export 'directives/ng_model.dart' show NgModel;
Expand Down
20 changes: 10 additions & 10 deletions angular_forms/lib/src/directives/abstract_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,23 @@ import 'ng_control.dart' show NgControl;
import 'ng_control_group.dart' show NgControlGroup;

/// A base implementation of [Form].
abstract class AbstractForm extends ControlContainer implements Form {
final _ngSubmit = new StreamController<ControlGroup>.broadcast(sync: true);
final _ngBeforeSubmit =
new StreamController<ControlGroup>.broadcast(sync: true);
abstract class AbstractForm<T extends AbstractControlGroup>
extends ControlContainer<T> implements Form {
final _ngSubmit = new StreamController<T>.broadcast(sync: true);
final _ngBeforeSubmit = new StreamController<T>.broadcast(sync: true);

ControlGroup get form;
T get form;

/// An event fired with the user has triggered a form submission.
@Output()
Stream<ControlGroup> get ngSubmit => _ngSubmit.stream;
Stream<T> get ngSubmit => _ngSubmit.stream;

/// An event that is fired before the main form submission event.
///
/// This is intended to be used to set form values or perform validation on
/// submit instead of when a value changes.
@Output()
Stream<ControlGroup> get ngBeforeSubmit => _ngBeforeSubmit.stream;
Stream<T> get ngBeforeSubmit => _ngBeforeSubmit.stream;

@HostListener('submit')
void onSubmit(Event event) {
Expand All @@ -45,7 +45,7 @@ abstract class AbstractForm extends ControlContainer implements Form {
Form get formDirective => this;

@override
ControlGroup get control => form;
T get control => form;

@override
List<String> get path => [];
Expand All @@ -54,8 +54,8 @@ abstract class AbstractForm extends ControlContainer implements Form {
Control getControl(NgControl dir) => form?.findPath(dir.path) as Control;

@override
ControlGroup getControlGroup(NgControlGroup dir) =>
(form?.findPath(dir.path) as ControlGroup);
AbstractControlGroup getControlGroup(NgControlGroup dir) =>
form?.findPath(dir.path) as AbstractControlGroup;

@override
void updateModel(NgControl dir, dynamic value) {
Expand Down
7 changes: 4 additions & 3 deletions angular_forms/lib/src/directives/control_container.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import '../model.dart' show ControlGroup;
import '../model.dart' show AbstractControlGroup;
import 'abstract_control_directive.dart' show AbstractControlDirective;
import 'form_interface.dart' show Form;

/// A directive that contains multiple [NgControl]s contained in a
/// [ControlGroup].
/// [AbstractControlGroup].
///
/// Only used by the forms package.
abstract class ControlContainer extends AbstractControlDirective<ControlGroup> {
abstract class ControlContainer<T extends AbstractControlGroup>
extends AbstractControlDirective<T> {
/// Get the form to which this container belongs.
Form get formDirective;
}
7 changes: 4 additions & 3 deletions angular_forms/lib/src/directives/form_interface.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import '../model.dart' show Control, ControlGroup;
import '../model.dart' show Control, AbstractControlGroup;
import 'ng_control.dart' show NgControl;
import 'ng_control_group.dart' show NgControlGroup;

Expand All @@ -21,8 +21,9 @@ abstract class Form {
/// Remove a group of controls from this form.
void removeControlGroup(NgControlGroup dir);

/// Look up the [ControlGroup] associated with a particular [NgControlGroup].
ControlGroup getControlGroup(NgControlGroup dir);
/// Look up the [AbstractControlGroup] associated with a particular
/// [NgControlGroup].
AbstractControlGroup getControlGroup(NgControlGroup dir);

/// Update the model for a particular control with a new value.
void updateModel(NgControl dir, dynamic value);
Expand Down
3 changes: 2 additions & 1 deletion angular_forms/lib/src/directives/ng_control_group.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ import 'validators.dart' show ValidatorFn;
],
exportAs: 'ngForm',
)
class NgControlGroup extends ControlContainer implements OnInit, OnDestroy {
class NgControlGroup extends ControlContainer<ControlGroup>
implements OnInit, OnDestroy {
final ValidatorFn validator;
final ControlContainer _parent;

Expand Down
32 changes: 24 additions & 8 deletions angular_forms/lib/src/directives/ng_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import 'dart:async';
import 'package:meta/meta.dart';
import 'package:angular/angular.dart';

import '../model.dart' show AbstractControl, ControlGroup, Control;
import '../model.dart'
show AbstractControl, AbstractControlGroup, ControlGroup, Control;
import '../validators.dart' show NG_VALIDATORS;
import 'abstract_form.dart' show AbstractForm;
import 'control_container.dart' show ControlContainer;
Expand Down Expand Up @@ -75,24 +76,39 @@ import 'shared.dart' show setUpControl, setUpControlGroup, composeValidators;
exportAs: 'ngForm',
visibility: Visibility.all,
)
class NgForm extends AbstractForm {
ControlGroup form;

class NgForm extends AbstractNgForm<ControlGroup> {
NgForm(@Optional() @Self() @Inject(NG_VALIDATORS) List<dynamic> validators) {
form = new ControlGroup({}, composeValidators(validators));
}

@override
ControlGroup createGroup(NgControlGroup _) => ControlGroup({});
}

/// Abstract class to easily create forms that are template driven.
///
/// This allows an implementing form to easily specify how to create control
/// groups, and controls. Allowing for infrastructure to create form systems
/// that are backed by different types such as protos.
abstract class AbstractNgForm<T extends AbstractControlGroup>
extends AbstractForm<T> {
T form;

@Input('ngDisabled')
set disabled(bool isDisabled) {
toggleDisabled(isDisabled);
}

Map<String, AbstractControl> get controls => form.controls;

T createGroup(NgControlGroup dir);
// This is separate to allow clients to override the logic if they wish.
Control createControl(NgControl _) => Control();

@override
void addControl(NgControl dir) {
var container = findContainer(dir.path);
var ctrl = new Control();
var ctrl = createControl(dir);
container.addControl(dir.name, ctrl);
scheduleMicrotask(() {
setUpControl(ctrl, dir);
Expand All @@ -114,7 +130,7 @@ class NgForm extends AbstractForm {
@override
void addControlGroup(NgControlGroup dir) {
var container = findContainer(dir.path);
var group = new ControlGroup({});
var group = createGroup(dir);
container.addControl(dir.name, group);
scheduleMicrotask(() {
setUpControlGroup(group, dir);
Expand All @@ -141,8 +157,8 @@ class NgForm extends AbstractForm {
}

@protected
ControlGroup findContainer(List<String> path) {
T findContainer(List<String> path) {
path.removeLast();
return path.isEmpty ? form : (form.findPath(path) as ControlGroup);
return path.isEmpty ? form : (form.findPath(path) as T);
}
}
2 changes: 1 addition & 1 deletion angular_forms/lib/src/directives/ng_form_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ import 'validators.dart' show ValidatorFn;
exportAs: 'ngForm',
visibility: Visibility.all,
)
class NgFormModel extends AbstractForm implements AfterChanges {
class NgFormModel extends AbstractForm<ControlGroup> implements AfterChanges {
final ValidatorFn _validator;

bool _formChanged = false;
Expand Down
4 changes: 2 additions & 2 deletions angular_forms/lib/src/directives/shared.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'dart:html';
import 'dart:js_util' as js_util;

import '../model.dart' show Control, ControlGroup;
import '../model.dart' show Control, AbstractControlGroup;
import '../validators.dart' show Validators;
import 'abstract_control_directive.dart' show AbstractControlDirective;
import 'checkbox_value_accessor.dart' show CheckboxControlValueAccessor;
Expand Down Expand Up @@ -43,7 +43,7 @@ void setUpControl(Control control, NgControl dir) {
dir.valueAccessor.registerOnTouched(() => control.markAsTouched());
}

void setUpControlGroup(ControlGroup control, NgControlGroup dir) {
void setUpControlGroup(AbstractControlGroup control, NgControlGroup dir) {
if (control == null) _throwError(dir, 'Cannot find control');
control.validator = Validators.compose([control.validator, dir.validator]);
}
Expand Down
109 changes: 59 additions & 50 deletions angular_forms/lib/src/model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import 'directives/validators.dart' show ValidatorFn;
AbstractControl _find(AbstractControl control, List<String> path) {
if (path == null || path.isEmpty) return null;
return path.fold(control, (v, name) {
if (v is ControlGroup) {
if (v is AbstractControlGroup) {
return v.controls[name];
} else if (v is ControlArray) {
var index = int.parse(name);
Expand Down Expand Up @@ -474,27 +474,9 @@ class Control<T> extends AbstractControl<T> {
/// `ControlGroup` is one of the three fundamental building blocks used to
/// define forms in Angular, along with [Control] and [ControlArray].
/// [ControlArray] can also contain other controls, but is of variable length.
class ControlGroup extends AbstractControl<Map<String, dynamic>> {
final Map<String, AbstractControl> controls;

ControlGroup(this.controls, [ValidatorFn validator]) : super(validator) {
_setParentForControls(this, controls.values);
}

/// Add a control to this group.
void addControl(String name, AbstractControl control) {
controls[name] = control;
control.setParent(this);
}

/// Remove a control from this group.
void removeControl(String name) {
controls.remove(name);
}

/// Check whether there is a control with the given name in the group.
bool contains(String controlName) =>
controls.containsKey(controlName) && controls[controlName].enabled;
class ControlGroup extends AbstractControlGroup<Map<String, dynamic>> {
ControlGroup(Map<String, AbstractControl> controls, [ValidatorFn validator])
: super(controls, validator);

@override
void updateValue(Map<String, dynamic> value,
Expand All @@ -519,43 +501,16 @@ class ControlGroup extends AbstractControl<Map<String, dynamic>> {
_value = _reduceValue();
}

@override
bool _anyControls(bool condition(AbstractControl c)) {
for (var name in controls.keys) {
if (contains(name) && condition(controls[name])) return true;
}
return false;
}

@override
bool _allControlsHaveStatus(String status) {
if (controls.isEmpty) return this.status == status;

for (var name in controls.keys) {
if (controls[name].status != status) return false;
}
return true;
}

@override
void _forEachChild(void callback(AbstractControl c)) {
for (var control in controls.values) {
callback(control);
}
}

Map<String, dynamic> _reduceValue() {
final res = <String, dynamic>{};
for (var name in controls.keys) {
if (_included(name) || disabled) {
if (included(name) || disabled) {
res[name] = controls[name].value;
}
}
return res;
}

bool _included(String controlName) => controls[controlName]?.enabled ?? false;

void _checkAllValuesPresent(Map<String, dynamic> value) {
if (value == null) return;

Expand All @@ -577,6 +532,60 @@ class ControlGroup extends AbstractControl<Map<String, dynamic>> {
}
}

/// Generic control group that allows creating your own group that is backed
/// by a value that is not a Map.
abstract class AbstractControlGroup<T> extends AbstractControl<T> {
final Map<String, AbstractControl> controls;

AbstractControlGroup(this.controls, [ValidatorFn validator])
: super(validator) {
_setParentForControls(this, controls.values);
}

/// Add a control to this group.
void addControl(String name, AbstractControl control) {
controls[name] = control;
control.setParent(this);
}

/// Remove a control from this group.
void removeControl(String name) {
controls.remove(name);
}

/// Check whether there is a control with the given name in the group.
bool contains(String controlName) =>
controls.containsKey(controlName) && controls[controlName].enabled;

@override
bool _anyControls(bool condition(AbstractControl c)) {
for (var name in controls.keys) {
if (contains(name) && condition(controls[name])) return true;
}
return false;
}

@override
bool _allControlsHaveStatus(String status) {
if (controls.isEmpty) return this.status == status;

for (var name in controls.keys) {
if (controls[name].status != status) return false;
}
return true;
}

@override
void _forEachChild(void callback(AbstractControl c)) {
for (var control in controls.values) {
callback(control);
}
}

@protected
bool included(String controlName) => controls[controlName]?.enabled ?? false;
}

/// Defines a part of a form, of variable length, that can contain other
/// controls.
///
Expand Down
5 changes: 0 additions & 5 deletions doc/developing/generated-code.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
# AngularDart's generated code

<!-- !g3-begin(For internal use only) -->
[TOC]

<!--* freshness: { owner: 'matanl' reviewed: '2018-06-21' } *-->
<!-- !g3-end -->

This is a document focused on the _internals_ of the generated
(`.template.dart`) files aimed primarily at _developers_ of AngularDart (versus
Expand Down
Loading

0 comments on commit 50c9e5e

Please sign in to comment.