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

Methods usable as filters #934

Closed
Show file tree
Hide file tree
Changes from all 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
7 changes: 6 additions & 1 deletion lib/change_detection/dirty_checking_change_detector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,7 @@ class DirtyCheckingRecord<H> implements Record<H>, WatchRecord<H> {
} else {
_mode = _MODE_GETTER_;
_getter = _fieldGetterFactory.getter(obj, field);
currentValue = _getter(obj);
}
}

Expand Down Expand Up @@ -489,7 +490,11 @@ class DirtyCheckingRecord<H> implements Record<H>, WatchRecord<H> {
}

var last = currentValue;
if (!identical(last, current)) {
// We use == to check if previous and current value are equal in case we're dealing with
// methods because
// var a = method1(); var b = method1();
// (a == b) == true, while identical(a, b) == false
if (current is Function ? last != current : !identical(last, current)) {
if (last is String && current is String &&
last == current) {
// This is false change in strings we need to recover, and pretend it
Expand Down
11 changes: 6 additions & 5 deletions lib/change_detection/dirty_checking_change_detector_dynamic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ class DynamicFieldGetterFactory implements FieldGetterFactory {
final isMethodInvoke = true;

bool isMethod(Object object, String name) {
try {
return method(object, name) != null;
} catch (e, s) {
return false;
}
InstanceMirror im = reflect(object);
Map methods = {};
im.type.instanceMembers.forEach((Symbol symbol, MethodMirror methodMirror) {
if(methodMirror.isRegularMethod) methods[symbol] = methodMirror;
});
return methods.containsKey(new Symbol(name));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Could a cache make sense here?


Function method(Object object, String name) {
Expand Down
107 changes: 105 additions & 2 deletions test/change_detection/dirty_checking_change_detector_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import '../_specs.dart';
import 'package:angular/change_detection/change_detection.dart';
import 'package:angular/change_detection/dirty_checking_change_detector.dart';
import 'package:angular/change_detection/dirty_checking_change_detector_static.dart';
import 'package:angular/change_detection/dirty_checking_change_detector_dynamic.dart';
import 'dart:collection';
import 'dart:math';

Expand All @@ -14,19 +15,80 @@ void main() {
"first": (o) => o.first,
"age": (o) => o.age,
"last": (o) => o.last,
"toString": (o) => o.toString
"toString": (o) => o.toString,
"isUnderAge": (o) => o.isUnderAge,
"isUnderAgeAsVariable": (o) => o.isUnderAgeAsVariable
Copy link
Contributor

Choose a reason for hiding this comment

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

append a "," to avoid diff noise ?

});

beforeEach(() {
detector = new DirtyCheckingChangeDetector<String>(getterFactory);
});

describe('StaticFieldGetterFactory', () {
DirtyCheckingChangeDetector<String> detector;
var user = new _User('Marko', 'Vuksanovic', 30);
FieldGetterFactory getterFactory = new StaticFieldGetterFactory({
"first": (o) => o.first,
"age": (o) => o.age,
"last": (o) => o.last,
"toString": (o) => o.toString,
"isUnderAge": (o) => o.isUnderAge,
"isUnderAgeAsVariable": (o) => o.isUnderAgeAsVariable,
"list": (o) => o.list,
"map": (o) => o.map
});

beforeEach(() {
detector = new DirtyCheckingChangeDetector<String>(getterFactory);
});

it('should detect methods', () {
var obj = new _User();
expect(getterFactory.isMethod(obj, 'toString')).toEqual(true);
expect(getterFactory.isMethod(obj, 'age')).toEqual(false);
});

it('should return true is method is real method', () {
expect(getterFactory.isMethod(user, 'isUnderAge')).toEqual(true);
});

it('should return false is field is a function', () {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mhevery Should this actually return true?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no, it shouldn't :)

expect(getterFactory.isMethod(user, 'isUnderAgeAsVariable')).toEqual(false);
});

it('should return false is field is a list', () {
expect(getterFactory.isMethod(user, 'list')).toEqual(false);
});

it('should return false is field is a map', () {
expect(getterFactory.isMethod(user, 'map')).toEqual(false);
});
});

describe('Dynamic GetterFactory', () {
DirtyCheckingChangeDetector<String> detector;
var user = new _User('Marko', 'Vuksanovic', 30);
FieldGetterFactory getterFactory = new DynamicFieldGetterFactory();

beforeEach(() {
detector = new DirtyCheckingChangeDetector<String>(getterFactory);
});

it('should return true is method is real method', () {
expect(getterFactory.isMethod(user, 'isUnderAge')).toEqual(true);
});

it('should return false is field is a function', () {
expect(getterFactory.isMethod(user, 'isUnderAgeAsVariable')).toEqual(false);
});

it('should return false is field is a list', () {
expect(getterFactory.isMethod(user, 'list')).toEqual(false);
});

it('should return false is field is a map', () {
expect(getterFactory.isMethod(user, 'map')).toEqual(false);
});
});

describe('object field', () {
Expand Down Expand Up @@ -657,6 +719,38 @@ void main() {
});
});

describe('function watching', () {
it('should detect no changes when watching a function', () {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mhevery Does this test align with what you think the behavior should be?

Copy link
Contributor

Choose a reason for hiding this comment

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

yes since getting isUnderAge has closurized the method and subsequent calls do nothing.

var user = new _User('marko', 'vuksanovic', 15);
Iterator changeIterator;

detector..watch(user, 'isUnderAge', null);
changeIterator = detector.collectChanges();
expect(changeIterator.moveNext()).toEqual(false);

user.age = 17;
changeIterator = detector.collectChanges();
expect(changeIterator.moveNext()).toEqual(false);

user.age = 30;
changeIterator = detector.collectChanges();
expect(changeIterator.moveNext()).toEqual(false);
});

it('should detect change when watching a property function', () {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mhevery Is this what you had in mind about changing property functions?

var user = new _User('marko', 'vuksanovic', 30);
Iterator changeIterator;

detector..watch(user, 'isUnderAgeAsVariable', null);
changeIterator = detector.collectChanges();
expect(changeIterator.moveNext()).toEqual(false);

user.isUnderAgeAsVariable = () => false;
changeIterator = detector.collectChanges();
expect(changeIterator.moveNext()).toEqual(true);
});
});

describe('DuplicateMap', () {
DuplicateMap map;
beforeEach(() => map = new DuplicateMap());
Expand Down Expand Up @@ -693,8 +787,17 @@ class _User {
String first;
String last;
num age;
var isUnderAgeAsVariable;
List<String> list = ['foo', 'bar', 'baz'];
Map map = {'foo': 'bar', 'baz': 'cux'};

_User([this.first, this.last, this.age]);
_User([this.first, this.last, this.age]) {
isUnderAgeAsVariable = isUnderAge;
}

bool isUnderAge() {
Copy link
Contributor

Choose a reason for hiding this comment

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

=>

return age != null ? age < 18 : false;
}
}

Matcher toEqualCollectionRecord({collection, previous, additions, moves, removals}) =>
Expand Down
10 changes: 10 additions & 0 deletions test/directive/ng_repeat_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,16 @@ main() {
expect(element.querySelectorAll('span').length).toEqual(2);
});

it('should support function as a formatter', () {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mhevery This should be alternative to the isPalindrome test from hello_world. This one is a bit simpler but should achieve the same goal/purpose.

scope.context['isEven'] = (num) => num % 2 == 0;
var element = compile(
'<div ng-show="true">'
'<span ng-repeat="r in [1, 2, 3, 4] | filter:isEven">{{r}}</span>'
'</div>');
scope.apply();
expect(element.text).toEqual('24');
});


describe('track by', () {
it(r'should track using expression function', () {
Expand Down