Skip to content
This repository has been archived by the owner on Jul 11, 2019. It is now read-only.

Commit

Permalink
feature: MaterialFormComponent with validation check
Browse files Browse the repository at this point in the history
  • Loading branch information
MikeMitterer committed Oct 22, 2015
1 parent 8817177 commit 185edc9
Show file tree
Hide file tree
Showing 13 changed files with 448 additions and 29 deletions.
15 changes: 15 additions & 0 deletions lib/assets/styles/forms/_forms.scss
Expand Up @@ -330,6 +330,21 @@ $_form_default_button_width : $_form_default_label_width !default;
}
}

.mdl-textfield.is-invalid .mdl-textfield__error {
visibility : hidden;
}

&.is-dirty {
.mdl-textfield.is-invalid .mdl-textfield__error {
visibility : visible;
}
}


.debug & {
&.is-invalid { background-color: rgba(255, 143, 38, 0.30); }
&.is-dirty { background-color: rgba(255, 143, 38, 0.60); }
}

@include shadow-2dp();
}
Expand Down
3 changes: 3 additions & 0 deletions lib/mdl.dart
Expand Up @@ -6,13 +6,15 @@ import "package:mdl/mdlapplication.dart";
import "package:mdl/mdltemplate.dart";
import "package:mdl/mdlformatter.dart";
import "package:mdl/mdldialog.dart";
import "package:mdl/mdlform.dart";

export "package:mdl/mdlcore.dart";
export "package:mdl/mdlcomponets.dart";
export "package:mdl/mdldirective.dart";
export "package:mdl/mdlapplication.dart";
export "package:mdl/mdltemplate.dart";
export "package:mdl/mdldialog.dart";
export "package:mdl/mdlform.dart";
export "package:mdl/mdlobservable.dart";
export "package:mdl/mdlformatter.dart";
export "package:mdl/mdldnd.dart";
Expand All @@ -24,6 +26,7 @@ void registerMdl() {
registerMdlDirectiveComponents();
registerMdlFormatterComponents();
registerMdlDialogComponents();
registerMdlFormComponents();

registerMdlComponents();
}
39 changes: 39 additions & 0 deletions lib/mdlform.dart
@@ -0,0 +1,39 @@
/**
* Copyright (c) 2015, Michael Mitterer (office@mikemitterer.at),
* IT-Consulting and Development Limited.
*
* All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

library mdlform;

import 'dart:html' as dom;
import 'dart:async';
import 'dart:collection';

import 'package:logging/logging.dart';
import 'package:validate/validate.dart';
import 'package:di/di.dart' as di;

import 'package:mdl/mdlcore.dart';
import 'package:mdl/mdlcomponets.dart';
import 'package:mdl/mdlapplication.dart';
import 'package:mdl/mdltemplate.dart';

part "src/form/components/MaterialFormComponent.dart";

void registerMdlFormComponents() {
registerMaterialFormComponent();
}
2 changes: 0 additions & 2 deletions lib/src/core/MdlComponentHandler.dart
Expand Up @@ -31,8 +31,6 @@ const String MDL_COMPONENT_PROPERTY = "mdlcomponent";
/// Property for the Components JsObject to save/register the component it component is a Widget
const String _MDL_WIDGET_PROPERTY = "mdlwidget";

class _RootContext { const _RootContext(); }

/// Store strings for class names defined by this component that are used in Dart.
class _MdlComponentHandlerCssClasses {

Expand Down
26 changes: 26 additions & 0 deletions lib/src/core/utils.dart
Expand Up @@ -164,4 +164,30 @@ void refreshComponentsInSubtree(final dom.HtmlElement element) {
});
}
}
}

/// Returns all MdlComponents in subtree
List<MdlComponent> getAllMdlComponents(final dom.HtmlElement element) {
Validate.notNull(element);

final List<MdlComponent> components = new List<MdlComponent>();

int counter = 0;
_iterateOverAllHTMLElements(final dom.HtmlElement element) {
if(element is dom.HtmlElement) {
element.children.forEach((final dom.Element element) => _iterateOverAllHTMLElements(element));
//_logger.info("E: $element ID: ${element.id} - classes: ${element.classes}");
if(isMdlComponent(element)) {
//_logger.shout("E: $element ID: ${element.id} - classes: ${element.classes}");
components.addAll(mdlComponents(element));
counter++;
}
}
}

_iterateOverAllHTMLElements(element);
//_logger.info("#${counter} Elements found");
//_logger.info("#${components.length} Components found");

return new UnmodifiableListView(components);
}
214 changes: 214 additions & 0 deletions lib/src/form/components/MaterialFormComponent.dart
@@ -0,0 +1,214 @@
/**
* Copyright (c) 2015, Michael Mitterer (office@mikemitterer.at),
* IT-Consulting and Development Limited.
*
* All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
part of mdlform;

/// Store strings for class names defined by this component that are used in
/// Dart. This allows us to simply change it in one place should we
/// decide to modify at a later date.
class _MaterialFormComponentCssClasses {

final String IS_UPGRADED = 'is-upgraded';

final String INVALID = 'is-invalid';
final String DIRTY = 'is-dirty';

final String SUBMIT_BUTTON = 'mdl-button--submit';

const _MaterialFormComponentCssClasses(); }

/// Store constants in one place so they can be updated easily.
class _MaterialFormComponentConstant {

static const String WIDGET_SELECTOR = "mdl-form";

const _MaterialFormComponentConstant();
}

/// Basic DI configuration for this Component or Service
/// Usage:
/// class MainModule extends di.Module {
/// MainModule() {
/// install(new MaterialFormComponentModule());
/// }
/// }
class MaterialFormComponentModule extends di.Module {
MaterialFormComponentModule() {
// bind(DeviceProxy);
}
}

enum _MaterialFormState {
VALID, INVALID
}

/**
* Upgrades mdl-form und does some validation checks.
*
* The CSS-Class 'is-invalid' is set if not all requirement defined via HTML are met.
* The CSS-Class 'is-dirty' will be set the first time one of the input elements gets an "onChange" event
*
* If MaterialFormComponent finds mdl-button--submit it enables/disables this
* element depending on the validation check.
*
* Usage:
* <form method="post" class="right mdl-form mdl-form-registration demo-registration">
* <h5 class="mdl-form__title">Register for launch</h5>
* <div class="mdl-form__content">
* <div class="mdl-textfield mdl-js-textfield">
* <input class="mdl-textfield__input" type="text" id="sample1" required />
* <label class="mdl-textfield__label" for="sample1">Name</label>
* <span class="mdl-textfield__error">This field must not be empty</span>
* </div>
* <div class="mdl-textfield mdl-js-textfield">
* <input class="mdl-textfield__input" type="email" id="email" required />
* <label class="mdl-textfield__label" for="email">Email</label>
* </div>
* <label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" for="remember_me">
* <input type="checkbox" id="remember_me" class="mdl-checkbox__input" required />
* <span class="mdl-checkbox__label">Remember me</span>
* </label>
* </div>
* <div class="mdl-form__actions">
* <button class="mdl-button mdl-js-button mdl-button--submit mdl-button--raised
* mdl-button--primary mdl-js-ripple-effect" disabled>
* Login
* </button>
* </div>
* </form>
*/
class MaterialFormComponent extends MdlComponent {
final Logger _logger = new Logger('mdlform.MaterialFormComponent');

//static const _MaterialFormComponentConstant _constant = const _MaterialFormComponentConstant();
static const _MaterialFormComponentCssClasses _cssClasses = const _MaterialFormComponentCssClasses();

final List<MdlComponent> _components = new List<MdlComponent>();
final List<MaterialButton> _submitButtons = new List<MaterialButton>();

MaterialFormComponent.fromElement(final dom.HtmlElement element,final di.Injector injector)
: super(element,injector) {

_init();

}

static MaterialFormComponent widget(final dom.HtmlElement element) => mdlComponent(element,MaterialFormComponent) as MaterialFormComponent;

/// Iterates through all MdlComponent found in this form, checks if all the requirements are fulfilled (set via HTML)
/// If the check returns true the "is-invalid" class is removed
/// Normally it's not necessary to call this function manually. This is done automatically if one of the
/// MdlComponents gets an "onChange" event
void updateStatus() {
final bool isFormValid = _isFormValid();

_setFormState(isFormValid ? _MaterialFormState.VALID : _MaterialFormState.INVALID);
_setSubmitButtonState(isFormValid ? _MaterialFormState.VALID : _MaterialFormState.INVALID);
}

/// Returns true if all requirements are fulfilled
bool get isValid => _isFormValid();

// - EventHandler -----------------------------------------------------------------------------


//- private -----------------------------------------------------------------------------------

void _init() {
_logger.fine("MaterialFormComponent - init");

_components.clear();
_components.addAll(getAllMdlComponents(element));

_components.forEach((final MdlComponent component) {
if(component is MaterialButton && component.classes.contains(_cssClasses.SUBMIT_BUTTON)) {
_submitButtons.add(component as MaterialButton);
}
});

_components.forEach((final MdlComponent component) {

eventStreams.add(component.hub.onChange.listen((final dom.Event event) {
_logger.info("$component changed!");

final bool isFormValid = _isFormValid();

_setFormState(isFormValid ? _MaterialFormState.VALID : _MaterialFormState.INVALID);
_setSubmitButtonState(isFormValid ? _MaterialFormState.VALID : _MaterialFormState.INVALID);

element.classes.add(_cssClasses.DIRTY);
}));

});

updateStatus();
element.classes.add(_cssClasses.IS_UPGRADED);
}

bool _isFormValid() {
bool state = true;

_components.forEach((final MdlComponent component) {
if(component.hub is dom.InputElement) {
if(!(component.hub as dom.InputElement).checkValidity()) {
_logger.fine("Checked ${component.hub.id}");
state = false;
return;
}
}
});
return state;
}

void _setFormState(final _MaterialFormState state) {

if(state == _MaterialFormState.VALID) {
element.classes.remove(_cssClasses.INVALID);
} else {
element.classes.add(_cssClasses.INVALID);
}
}

void _setSubmitButtonState(final _MaterialFormState state) {
_submitButtons.forEach((final MaterialButton button) {
button.enabled = state == _MaterialFormState.VALID;
});
}



}

/// registration-Helper
void registerMaterialFormComponent() {
final MdlConfig config = new MdlWidgetConfig<MaterialFormComponent>(
_MaterialFormComponentConstant.WIDGET_SELECTOR,
(final dom.HtmlElement element,final di.Injector injector) => new MaterialFormComponent.fromElement(element,injector)
);

// If you want <mdl-form></mdl-form> set selectorType to SelectorType.TAG.
// If you want <div mdl-form></div> set selectorType to SelectorType.ATTRIBUTE.
// By default it's used as a class name. (<div class="mdl-form"></div>)
config.selectorType = SelectorType.CLASS;

/// With priority 2 we make sure that all children are registered before this components gets initialized
config.priority = 2;

componentHandler().register(config);
}

29 changes: 16 additions & 13 deletions samples/mdlx_forms/.sitegen/html/_content/index.html
Expand Up @@ -6,36 +6,39 @@
<!--<div class="linklist"><a href="index.html">With material components</a> | <a href="index-html-controls.html">Basic HTML elements</a></div>-->

<div class="firstblock">
<p>
justo. Morbi tincidunt vulputate urna vitae malesuada. Vivamus est erat, venenatis eu mauris ac, finibus
cursus lectus. Nunc ultrices libero vitae est egestas consectetur. Aliquam dapibus, mi quis sollicitudin
vestibulum, odio nulla ultrices erat, vel sagittis ligula sem et nunc.</p>

<h5>Form validation</h5>
<form method="post" class="right mdl-form mdl-form-registration demo-registration">
<h5 class="mdl-form__title">Register for launch</h5>
<div class="mdl-form__content">
<div class="mdl-textfield mdl-js-textfield">
<input class="mdl-textfield__input" type="text" id="sample1" />
<input class="mdl-textfield__input" type="text" id="sample1" required />
<label class="mdl-textfield__label" for="sample1">Name</label>
<span class="mdl-textfield__error">This field must not be empty</span>
</div>
<div class="mdl-textfield mdl-js-textfield">
<input class="mdl-textfield__input" type="text" id="email" />
<input class="mdl-textfield__input" type="email" id="email" required />
<label class="mdl-textfield__label" for="email">Email</label>
</div>
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" for="remember_me">
<input type="checkbox" id="remember_me" class="mdl-checkbox__input" />
<input type="checkbox" id="remember_me" class="mdl-checkbox__input" required />
<span class="mdl-checkbox__label">Remember me</span>
</label>
</div>
<div class="mdl-form__actions">
<button class="mdl-button mdl-js-button mdl-button--raised mdl-button--primary mdl-js-ripple-effect">Login</button>
<button class="mdl-button mdl-js-button mdl-button--submit mdl-button--raised
mdl-button--primary mdl-js-ripple-effect" disabled>
Login
</button>
</div>
</form>

<p>
justo. Morbi tincidunt vulputate urna vitae malesuada. Vivamus est erat, venenatis eu mauris ac, finibus
cursus lectus. Nunc ultrices libero vitae est egestas consectetur. Aliquam dapibus, mi quis sollicitudin
vestibulum, odio nulla ultrices erat, vel sagittis ligula sem et nunc.</p>
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et
dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet,
consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no
sea takimata sanctus est Lorem ipsum dolor sit amet.</p>

</div>

<form method="post" class="mdl-form">
Expand Down

0 comments on commit 185edc9

Please sign in to comment.