diff --git a/lib/directive/module.dart b/lib/directive/module.dart
index 7cf33b1e0..eb85f15ec 100644
--- a/lib/directive/module.dart
+++ b/lib/directive/module.dart
@@ -49,6 +49,7 @@ class NgDirectiveModule extends Module {
value(InputNumberDirective, null);
value(InputRadioDirective, null);
value(InputTextDirective, null);
+ value(InputPasswordDirective, null);
value(InputUrlDirective, null);
value(InputCheckboxDirective, null);
value(TextAreaDirective, null);
diff --git a/lib/directive/ng_model.dart b/lib/directive/ng_model.dart
index 54185a142..14d5da574 100644
--- a/lib/directive/ng_model.dart
+++ b/lib/directive/ng_model.dart
@@ -144,6 +144,27 @@ class InputTextDirective extends _InputTextlikeDirective {
}
}
+/**
+ * Usage:
+ *
+ *
+ *
+ * This creates a two way databinding between the expression specified in
+ * ng-model and the password input element in the DOM. If the ng-model value is
+ * `null`, it is treated as equivalent to the empty string for rendering
+ * purposes.
+ */
+@NgDirective(selector: 'input[type=password][ng-model]')
+class InputPasswordDirective extends _InputTextlikeDirective {
+ InputPasswordDirective(dom.Element inputElement, NgModel ngModel, Scope scope):
+ super(inputElement, ngModel, scope);
+
+ String get typedValue => inputElement.value;
+ set typedValue(String value) {
+ inputElement.value = (value == null) ? '' : value;
+ }
+}
+
/**
* Usage:
*
diff --git a/test/directive/ng_model_spec.dart b/test/directive/ng_model_spec.dart
index f37ec019f..29ce67611 100644
--- a/test/directive/ng_model_spec.dart
+++ b/test/directive/ng_model_spec.dart
@@ -72,6 +72,69 @@ describe('ng-model', () {
}));
});
+ describe('type="password"', () {
+ it('should update input value from model', inject(() {
+ _.compile('');
+ _.rootScope.$digest();
+
+ expect((_.rootElement as dom.InputElement).value).toEqual('');
+
+ _.rootScope.$apply('model = "misko"');
+ expect((_.rootElement as dom.InputElement).value).toEqual('misko');
+ }));
+
+ it('should render null as the empty string', inject(() {
+ _.compile('');
+ _.rootScope.$digest();
+
+ expect((_.rootElement as dom.InputElement).value).toEqual('');
+
+ _.rootScope.$apply('model = null');
+ expect((_.rootElement as dom.InputElement).value).toEqual('');
+ }));
+
+ it('should update model from the input value', inject(() {
+ _.compile('');
+ Probe probe = _.rootScope.p;
+ var ngModel = probe.directive(NgModel);
+ InputElement inputElement = probe.element;
+
+ inputElement.value = 'abc';
+ _.triggerEvent(inputElement, 'change');
+ expect(_.rootScope.model).toEqual('abc');
+
+ inputElement.value = 'def';
+ var input = probe.directive(InputPasswordDirective);
+ input.processValue();
+ expect(_.rootScope.model).toEqual('def');
+
+ }));
+
+ it('should write to input only if value is different', inject(() {
+ var scope = _.rootScope;
+ var model = new NgModel(scope, new NodeAttrs(new DivElement()));
+ var element = new dom.InputElement();
+ dom.query('body').append(element);
+ var input = new InputPasswordDirective(element, model, scope);
+
+ element.value = 'abc';
+ element.selectionStart = 1;
+ element.selectionEnd = 2;
+
+ model.render('abc');
+
+ expect(element.value).toEqual('abc');
+ expect(element.selectionStart).toEqual(1);
+ expect(element.selectionEnd).toEqual(2);
+
+ model.render('xyz');
+
+ expect(element.value).toEqual('xyz');
+ expect(element.selectionStart).toEqual(1);
+ expect(element.selectionEnd).toEqual(2);
+ }));
+ });
+
describe('type="checkbox"', () {
it('should update input value from model', inject((Scope scope) {
var element = _.compile('');