Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

feat(ng-model-options) Added ng-model-options #974

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion benchmark/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ packages:
barback:
description: barback
source: hosted
version: "0.13.0"
version: "0.12.0"
benchmark_harness:
description: benchmark_harness
source: hosted
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ packages:
barback:
description: barback
source: hosted
version: "0.13.0"
version: "0.12.0"
browser:
description: browser
source: hosted
Expand Down
2 changes: 1 addition & 1 deletion example/web/hello_world.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<body hello-world-controller>

<h3>Hello {{ctrl.name}}!</h3>
name: <input type="text" ng-model="ctrl.name">
name: <input type="text" ng-model="ctrl.name" ng-model-options="{ debounce: {'default': 500, 'blur': 0} }">

<script type="application/dart" src="hello_world.dart"></script>
<script src="packages/browser/dart.js"></script>
Expand Down
4 changes: 4 additions & 0 deletions lib/directive/module.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ library angular.directive;

import 'package:di/di.dart';
import 'dart:html' as dom;
import 'dart:convert' as convert;
import 'dart:async' as async;
import 'package:intl/intl.dart';
import 'package:angular/core/annotation.dart';
import 'package:angular/core/module_internal.dart';
Expand Down Expand Up @@ -51,6 +53,7 @@ part 'ng_non_bindable.dart';
part 'ng_model_select.dart';
part 'ng_form.dart';
part 'ng_model_validators.dart';
part 'ng_model_options.dart';

class DecoratorFormatter extends Module {
DecoratorFormatter() {
Expand Down Expand Up @@ -82,6 +85,7 @@ class DecoratorFormatter extends Module {
value(ContentEditable, null);
value(NgBindTypeForDateLike, null);
value(NgModel, null);
value(NgModelOptions, null);
value(NgValue, null);
value(NgTrueValue, new NgTrueValue());
value(NgFalseValue, new NgFalseValue());
Expand Down
130 changes: 64 additions & 66 deletions lib/directive/ng_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,9 @@ class NgModel extends NgControl implements AttachAware {
Watch _watch;
bool _watchCollection;

NgModel(this._scope, NgElement element, Injector injector, NodeAttrs attrs,
NgModel(this._scope, NgElement element, Injector injector, NodeAttrs attrs,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra white space

Animate animate)
: super(element, injector, animate)
{
: super(element, injector, animate) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{ on new line

_expression = attrs["ng-model"];
watchCollection = false;

Expand Down Expand Up @@ -145,11 +144,10 @@ class NgModel extends NgControl implements AttachAware {
if (_watch!=null) _watch.remove();
if (_watchCollection) {
_watch = _scope.watch(_expression, (changeRecord, _) {
onChange(changeRecord is CollectionChangeRecord
? changeRecord.iterable
: changeRecord);
},
collection: true);
onChange(changeRecord is CollectionChangeRecord
? changeRecord.iterable
: changeRecord);
}, collection: true);
} else if (_expression != null) {
_watch = _scope.watch(_expression, onChange);
}
Expand Down Expand Up @@ -196,6 +194,7 @@ class NgModel extends NgControl implements AttachAware {

get viewValue => _viewValue;
void set viewValue(value) {
//delay set
_viewValue = value;
modelValue = value;
}
Expand All @@ -204,7 +203,7 @@ class NgModel extends NgControl implements AttachAware {
void set modelValue(value) {
try {
value = converter.parse(value);
} catch(e) {
} catch (e) {
value = null;
}
_modelValue = value;
Expand Down Expand Up @@ -294,26 +293,28 @@ class InputCheckbox {
final NgModel ngModel;
final NgTrueValue ngTrueValue;
final NgFalseValue ngFalseValue;
final NgModelOptions ngModelOptions;
final Scope scope;

InputCheckbox(dom.Element this.inputElement, this.ngModel,
this.scope, this.ngTrueValue, this.ngFalseValue) {
InputCheckbox(dom.Element this.inputElement, this.ngModel, this.scope, this.ngTrueValue, this.ngFalseValue, this.ngModelOptions) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

long line

ngModel.render = (value) {
scope.rootScope.domWrite(() {
inputElement.checked = ngTrueValue.isValue(value);
});
};
inputElement
..onChange.listen((_) {
ngModel.viewValue = inputElement.checked
? ngTrueValue.value : ngFalseValue.value;
})
..onBlur.listen((e) {
..onChange.listen((_)=>ngModelOptions.executeChangeFunc(() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing ws around =>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dosent need it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be (_) => ngModelOptions.executeChangeFuncaccording to Dart CC, see https://www.dartlang.org/articles/style-guide/

ngModel.viewValue = inputElement.checked ? ngTrueValue.value : ngFalseValue.value;
}))
..onBlur.listen((_)=>ngModelOptions.executeBlurFunc(() {
ngModel.markAsTouched();
});
}));
}
}




/**
* Usage:
*
Expand All @@ -336,37 +337,41 @@ class InputCheckbox {
class InputTextLike {
final dom.Element inputElement;
final NgModel ngModel;
final NgModelOptions ngModelOptions;
final Scope scope;
String _inputType;


get typedValue => (inputElement as dynamic).value;
void set typedValue(value) {
(inputElement as dynamic).value = (value == null) ? '' : value.toString();
}

InputTextLike(this.inputElement, this.ngModel, this.scope) {
InputTextLike(this.inputElement, this.ngModel, this.scope, this.ngModelOptions) {
ngModel.render = (value) {
scope.rootScope.domWrite(() {
if (value == null) value = '';

var currentValue = typedValue;
if (value != currentValue && !(value is num && currentValue is num &&
value.isNaN && currentValue.isNaN)) {
typedValue = value;
if (value != currentValue && !(value is num && currentValue is num && value.isNaN && currentValue.isNaN)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert

typedValue = value;
}
});
};

inputElement
..onChange.listen(processValue)
..onInput.listen(processValue)
..onBlur.listen((e) {
..onChange.listen((event) => ngModelOptions.executeChangeFunc(() => processValue(event)))
..onInput.listen((event) => ngModelOptions.executeInputFunc(() => processValue(event)))
..onBlur.listen((_) => ngModelOptions.executeBlurFunc(() => () {
ngModel.markAsTouched();
});
}));
}

void processValue([_]) {
var value = typedValue;

if (value != ngModel.viewValue) ngModel.viewValue = value;

ngModel.validate();
}
}
Expand All @@ -393,6 +398,7 @@ class InputTextLike {
class InputNumberLike {
final dom.InputElement inputElement;
final NgModel ngModel;
final NgModelOptions ngModelOptions;
final Scope scope;


Expand All @@ -413,21 +419,20 @@ class InputNumberLike {
}
}

InputNumberLike(dom.Element this.inputElement, this.ngModel, this.scope) {
InputNumberLike(dom.Element this.inputElement, this.ngModel, this.scope, this.ngModelOptions) {
ngModel.render = (value) {
scope.rootScope.domWrite(() {
if (value != typedValue
&& (value == null || value is num && !value.isNaN)) {
if (value != typedValue && (value == null || value is num && !value.isNaN)) {
typedValue = value;
}
});
};
inputElement
..onChange.listen(relaxFnArgs(processValue))
..onInput.listen(relaxFnArgs(processValue))
..onBlur.listen((e) {
..onChange.listen((event) => ngModelOptions.executeChangeFunc(() => processValue()))
..onInput.listen((event) => ngModelOptions.executeInputFunc(() => processValue()))
..onBlur.listen((event) => ngModelOptions.executeBlurFunc(() => () {
ngModel.markAsTouched();
});
}));
}

void processValue() {
Expand Down Expand Up @@ -473,19 +478,20 @@ class NgBindTypeForDateLike {
@NgAttr('ng-bind-type')
void set idlAttrKind(final String _kind) {
String kind = _kind == null ? DEFAULT : _kind.toLowerCase();
if (!VALID_VALUES.contains(kind))
throw "Unsupported ng-bind-type attribute value '$_kind'; "
"it should be one of $VALID_VALUES";
if (!VALID_VALUES.contains(kind)) throw "Unsupported ng-bind-type attribute value '$_kind'; " "it should be one of $VALID_VALUES";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert

_idlAttrKind = kind;
}

String get idlAttrKind => _idlAttrKind;

dynamic get inputTypedValue {
switch (idlAttrKind) {
case DATE: return inputValueAsDate;
case NUMBER: return inputElement.valueAsNumber;
default: return inputElement.value;
case DATE:
return inputValueAsDate;
case NUMBER:
return inputElement.valueAsNumber;
default:
return inputElement.value;
}
}

Expand Down Expand Up @@ -568,28 +574,21 @@ class NgBindTypeForDateLike {
* dropped.
*/

@Decorator(selector: 'input[type=date][ng-model]',
module: InputDateLike.moduleFactory)
@Decorator(selector: 'input[type=time][ng-model]',
module: InputDateLike.moduleFactory)
@Decorator(selector: 'input[type=datetime][ng-model]',
module: InputDateLike.moduleFactory)
@Decorator(selector: 'input[type=datetime-local][ng-model]',
module: InputDateLike.moduleFactory)
@Decorator(selector: 'input[type=month][ng-model]',
module: InputDateLike.moduleFactory)
@Decorator(selector: 'input[type=week][ng-model]',
module: InputDateLike.moduleFactory)
@Decorator(selector: 'input[type=date][ng-model]', module: InputDateLike.moduleFactory)
@Decorator(selector: 'input[type=time][ng-model]', module: InputDateLike.moduleFactory)
@Decorator(selector: 'input[type=datetime][ng-model]', module: InputDateLike.moduleFactory)
@Decorator(selector: 'input[type=datetime-local][ng-model]', module: InputDateLike.moduleFactory)
@Decorator(selector: 'input[type=month][ng-model]', module: InputDateLike.moduleFactory)
@Decorator(selector: 'input[type=week][ng-model]', module: InputDateLike.moduleFactory)
class InputDateLike {
static Module moduleFactory() => new Module()..factory(NgBindTypeForDateLike,
(Injector i) => new NgBindTypeForDateLike(i.get(dom.Element)));
static Module moduleFactory() => new Module()..factory(NgBindTypeForDateLike, (Injector i) => new NgBindTypeForDateLike(i.get(dom.Element)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert

final dom.InputElement inputElement;
final NgModel ngModel;
final NgModelOptions ngModelOptions;
final Scope scope;
NgBindTypeForDateLike ngBindType;

InputDateLike(dom.Element this.inputElement, this.ngModel, this.scope,
this.ngBindType) {
InputDateLike(dom.Element this.inputElement, this.ngModel, this.scope, this.ngBindType, this.ngModelOptions) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

too long

if (inputElement.type == 'datetime-local') {
ngBindType.idlAttrKind = NgBindTypeForDateLike.NUMBER;
}
Expand All @@ -599,11 +598,11 @@ class InputDateLike {
});
};
inputElement
..onChange.listen(relaxFnArgs(processValue))
..onInput.listen(relaxFnArgs(processValue))
..onBlur.listen((e) {
..onChange.listen((event) => ngModelOptions.executeChangeFunc(() => processValue()))
..onInput.listen((event) => ngModelOptions.executeInputFunc(() => processValue()))
..onBlur.listen((_)=> ngModelOptions.executeBlurFunc(() => () {
ngModel.markAsTouched();
});
}));
}

dynamic get typedValue => ngBindType.inputTypedValue;
Expand Down Expand Up @@ -679,7 +678,9 @@ class NgValue {
NgValue(this.element);

@NgOneWay('ng-value')
void set value(val) { this._value = val; }
void set value(val) {
this._value = val;
}
dynamic get value => _value == null ? (element as dynamic).value : _value;
}

Expand Down Expand Up @@ -741,17 +742,14 @@ class NgFalseValue {
* `009`, `00A`, `00Z`, `010`, and so on using more than 3 characters for the
* name when the counter overflows.
*/
@Decorator(
selector: 'input[type=radio][ng-model]',
module: NgValue.moduleFactory)
@Decorator(selector: 'input[type=radio][ng-model]', module: NgValue.moduleFactory)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert

class InputRadio {
final dom.RadioButtonInputElement radioButtonElement;
final NgModel ngModel;
final NgValue ngValue;
final Scope scope;

InputRadio(dom.Element this.radioButtonElement, this.ngModel,
this.scope, this.ngValue, NodeAttrs attrs) {
InputRadio(dom.Element this.radioButtonElement, this.ngModel, this.scope, this.ngValue, NodeAttrs attrs) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

too long

// If there's no "name" set, we'll set a unique name. This ensures
// less surprising behavior about which radio buttons are grouped together.
if (attrs['name'] == '' || attrs['name'] == null) {
Expand Down Expand Up @@ -784,8 +782,8 @@ class InputRadio {
*/
@Decorator(selector: '[contenteditable][ng-model]')
class ContentEditable extends InputTextLike {
ContentEditable(dom.Element inputElement, NgModel ngModel, Scope scope)
: super(inputElement, ngModel, scope);
ContentEditable(dom.Element inputElement, NgModel ngModel, Scope scope, NgModelOptions modelOptions)
: super(inputElement, ngModel, scope, modelOptions);

// The implementation is identical to InputTextLike but use innerHtml instead of value
String get typedValue => (inputElement as dynamic).innerHtml;
Expand Down
55 changes: 55 additions & 0 deletions lib/directive/ng_model_options.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
part of angular.directive;

@Decorator(selector: 'input[ng-model-options]')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some doc about the purpose of this class would be great!

class NgModelOptions {
int _debounceDefaultValue = 0;
int _debounceBlurValue = null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

= null is not required

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is int default value not 0?
sorry for some of these question am normally c# developer

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you don't have to be sorry for asking question ;)

in Dart everything default to null (Types are only informative and not taken into account in production code)

int _debounceChangeValue = null;
int _debounceInputValue = null;

static const String _debounceDefaultKey = "default";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use _UPPER_CASE for const names

static const String _debounceBlurKey = "blur";
static const String _debounceChangeKey = "change";
static const String _debounceInputKey = "input";

NgModelOptions(NodeAttrs attrs) {
print("options: " + attrs["ng-model-options"].replaceFirst("debounce", "'debounce'").replaceAll("'", "\""));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

print

Map options = convert.JSON.decode(attrs["ng-model-options"].replaceFirst("debounce", "'debounce'").replaceAll("'", "\""));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't we just have valid JSON as input ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only thing i was trying to achieve to match the angularjs spec

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually you should check how this is done in ng_pluralize.dart IMO

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the comment has been removed: please check ng-pluralize

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what should i check ng-pluralize for?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some attributes have a JSON format

On 04/29/2014 01:21 AM, jrote1 wrote:

In lib/directive/ng_model_options.dart:

+part of angular.directive;
+
+@decorator(selector: 'input[ng-model-options]')
+class NgModelOptions {

  • int _debounceDefaultValue = 0;
  • int _debounceBlurValue;
  • int _debounceChangeValue;
  • int _debounceInputValue;
  • static const String _DEBOUNCE_DEFAULT_KEY = "default";
  • static const String _DEBOUNCE_BLUR_KEY = "blur";
  • static const String _DEBOUNCE_CHANGE_KEY = "change";
  • static const String _DEBOUNCE_INPUT_KEY = "input";
  • NgModelOptions(NodeAttrs attrs) {
  • Map options = convert.JSON.decode(attrs["ng-model-options"].replaceFirst("debounce", "'debounce'").replaceAll("'", """));

what should i check ng-pluralize for?


Reply to this email directly or view it on GitHub
https://github.com/angular/angular.dart/pull/974/files#r12073468.


if (options["debounce"].containsKey(_debounceDefaultKey)) _debounceDefaultValue = options["debounce"][_debounceDefaultKey];
if (options["debounce"].containsKey(_debounceBlurKey)) _debounceBlurValue = options["debounce"][_debounceBlurKey];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

most ifs are useless (returns null when not in map)

if (options["debounce"].containsKey(_debounceChangeKey)) _debounceChangeValue = options["debounce"][_debounceChangeKey];
if (options["debounce"].containsKey(_debounceInputKey)) _debounceInputValue = options["debounce"][_debounceInputKey];
}

async.Timer _blurTimer;
void executeBlurFunc(func()) {
if (_blurTimer != null && !_blurTimer.isActive) _blurTimer.cancel();

var delay = _debounceBlurValue == null ? _debounceDefaultValue : _debounceBlurValue;
_runFuncDebounced(delay, func, (timer)=>_blurTimer = timer);
}

async.Timer _changeTimer;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pls move properties at the top of the class, before methods

void executeChangeFunc(func()) {
if (_changeTimer != null && !_changeTimer.isActive) _changeTimer.cancel();

var delay = _debounceChangeValue == null ? _debounceDefaultValue : _debounceChangeValue;
_runFuncDebounced(delay, func, (timer)=>_changeTimer = timer);
}

async.Timer _inputTimer;
void executeInputFunc(func()) {
if (_inputTimer != null && _inputTimer.isActive) _inputTimer.cancel();

var delay = _debounceInputValue == null ? _debounceDefaultValue : _debounceInputValue;
_runFuncDebounced(delay, func, (timer) => _inputTimer = timer);
}

void _runFuncDebounced(int delay, func(), setTimer(async.Timer timer)){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the last arg could be the timer. Then you can cancel it here

if(delay == 0)
func();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add missing {}

else
setTimer(new async.Timer(new Duration(milliseconds: delay), func));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about dropping the 3rd args & returning the timer

}
}
Loading