Use $formFactory
to create a new instance of a Form
controller or to find the nearest form instance for a given DOM element.
The form instance is a collection of widgets, and is responsible for life cycle and validation of widget.
Keep in mind that both form and widget instances are scopes
.
$formFactory([parentForm]);
parentForm(optional) – {Form=} –
The form which should be the parent form of the new form controller.
If none specified default to the rootForm
.
{Form}
– A new Form
instance.
Static method on $formFactory
service.
Retrieve the closest form for a given element or defaults to the root
form. Used by the
form
element.
element – {Element} –
The element where the search for form should initiate.
Static property on $formFactory
Each application (root scope) gets a root form which is the top-level parent of all forms.
This example shows how one could write a widget which would enable data-binding on
contenteditable
feature of HTML.
<script> function EditorCntl() { this.html = '<b>Hello</b> <i>World</i>!'; } HTMLEditorWidget.$inject = ['$element', 'htmlFilter']; function HTMLEditorWidget(element, htmlFilter) { var self = this; this.$parseModel = function() { // need to protect for script injection try { this.$viewValue = htmlFilter(this.$modelValue || '').get(); if (this.$error.HTML) { // we were invalid, but now we are OK. this.$emit('$valid', 'HTML'); } } catch (e) { // if HTML not parsable invalidate form. this.$emit('$invalid', 'HTML'); } } this.$render = function() { element.html(this.$viewValue); } element.bind('keyup', function() { self.$apply(function() { self.$emit('$viewChange', element.html()); }); }); } angular.directive('ng:contenteditable', function() { return ['$formFactory', '$element', function ($formFactory, element) { var exp = element.attr('ng:contenteditable'), form = $formFactory.forElement(element), widget; element.attr('contentEditable', true); widget = form.$createWidget({ scope: this, model: exp, controller: HTMLEditorWidget, controllerArgs: {$element: element}}); // if the element is destroyed, then we need to notify the form. element.bind('$destroy', function() { widget.$destroy(); }); }]; }); </script> <form name='editorForm' ng:controller="EditorCntl"> <div ng:contenteditable="html"></div> <hr/> HTML: <br/> <textarea ng:model="html" cols=80></textarea> <hr/> <pre>editorForm = {{editorForm}}</pre> </form>
it('should enter invalid HTML', function() { expect(element('form[name=editorForm]').prop('className')).toMatch(/ng-valid/); input('html').enter('<'); expect(element('form[name=editorForm]').prop('className')).toMatch(/ng-invalid/); });