From 21c725f1a12d1de758cab6e4c4fafc5c420eb565 Mon Sep 17 00:00:00 2001 From: Vojta Jina Date: Wed, 15 Feb 2012 17:16:02 -0800 Subject: refactor(forms): Even better forms - remove $formFactory completely - remove parallel scope hierarchy (forms, widgets) - use new compiler features (widgets, forms are controllers) - any directive can add formatter/parser (validators, convertors) Breaks no custom input types Breaks removed integer input type Breaks remove list input type (ng-list directive instead) Breaks inputs bind only blur event by default (added ng:bind-change directive) --- src/widget/form.js | 134 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 107 insertions(+), 27 deletions(-) (limited to 'src/widget/form.js') diff --git a/src/widget/form.js b/src/widget/form.js index deaf38d5..23b07107 100644 --- a/src/widget/form.js +++ b/src/widget/form.js @@ -1,5 +1,86 @@ 'use strict'; +FormController.$inject = ['$scope', 'name']; +function FormController($scope, name) { + var form = this, + errors = form.error = {}; + + // publish the form into scope + name(this); + + $scope.$on('$destroy', function(event, widget) { + if (!widget) return; + + if (widget.widgetId) { + delete form[widget.widgetId]; + } + forEach(errors, removeWidget, widget); + }); + + $scope.$on('$valid', function(event, error, widget) { + removeWidget(errors[error], error, widget); + + if (equals(errors, {})) { + form.valid = true; + form.invalid = false; + } + }); + + $scope.$on('$invalid', function(event, error, widget) { + addWidget(error, widget); + + form.valid = false; + form.invalid = true; + }); + + $scope.$on('$viewTouch', function() { + form.dirty = true; + form.pristine = false; + }); + + // init state + form.dirty = false; + form.pristine = true; + form.valid = true; + form.invalid = false; + + function removeWidget(queue, errorKey, widget) { + if (queue) { + widget = widget || this; // so that we can be used in forEach; + for (var i = 0, length = queue.length; i < length; i++) { + if (queue[i] === widget) { + queue.splice(i, 1); + if (!queue.length) { + delete errors[errorKey]; + } + } + } + } + } + + function addWidget(errorKey, widget) { + var queue = errors[errorKey]; + if (queue) { + for (var i = 0, length = queue.length; i < length; i++) { + if (queue[i] === widget) { + return; + } + } + } else { + errors[errorKey] = queue = []; + } + queue.push(widget); + } +} + +FormController.prototype.registerWidget = function(widget, alias) { + if (alias && !this.hasOwnProperty(alias)) { + widget.widgetId = alias; + this[alias] = widget; + } +}; + + /** * @ngdoc directive * @name angular.module.ng.$compileProvider.directive.form @@ -57,55 +138,54 @@ $scope.text = 'guest'; } -
-
- text: - Required! -
+
+ text: + Required! text = {{text}}
- myForm.input.$valid = {{myForm.input.$valid}}
- myForm.input.$error = {{myForm.input.$error}}
- myForm.$valid = {{myForm.$valid}}
- myForm.$error.REQUIRED = {{!!myForm.$error.REQUIRED}}
-
+ myForm.input.valid = {{myForm.input.valid}}
+ myForm.input.error = {{myForm.input.error}}
+ myForm.valid = {{myForm.valid}}
+ myForm.error.REQUIRED = {{!!myForm.error.REQUIRED}}
+ it('should initialize to model', function() { expect(binding('text')).toEqual('guest'); - expect(binding('myForm.input.$valid')).toEqual('true'); + expect(binding('myForm.input.valid')).toEqual('true'); }); it('should be invalid if empty', function() { input('text').enter(''); expect(binding('text')).toEqual(''); - expect(binding('myForm.input.$valid')).toEqual('false'); + expect(binding('myForm.input.valid')).toEqual('false'); }); */ -var ngFormDirective = ['$formFactory', function($formFactory) { +var ngFormDirective = [function() { return { + name: 'form', restrict: 'E', + scope: true, + inject: { + name: 'accessor' + }, + controller: FormController, compile: function() { return { - pre: function(scope, formElement, attr) { - var name = attr.name, - parentForm = $formFactory.forElement(formElement), - form = $formFactory(parentForm); - formElement.data('$form', form); - formElement.bind('submit', function(event){ + pre: function(scope, formElement, attr, controller) { + formElement.data('$form', controller); + formElement.bind('submit', function(event) { if (!attr.action) event.preventDefault(); }); - if (name) { - scope[name] = form; - } - watch('valid'); - watch('invalid'); - function watch(name) { - form.$watch('$' + name, function(value) { + + forEach(['valid', 'invalid', 'dirty', 'pristine'], function(name) { + scope.$watch(function() { + return controller[name]; + }, function(value) { formElement[value ? 'addClass' : 'removeClass']('ng-' + name); }); - } + }); } }; } -- cgit v1.2.3