Skip to content

Commit b6df99a

Browse files
committed
Added support for dot-delimited paths to access and match nested item values.
Renamed provider properties. Applied JSCS to test spec. Tagged 0.1.0
1 parent 09437a8 commit b6df99a

File tree

9 files changed

+125
-72
lines changed

9 files changed

+125
-72
lines changed

Gruntfile.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,11 @@ module.exports = function (grunt) {
112112
},
113113
jscs: {
114114
all: {
115-
src: '<%= jshint.all.src %>',
115+
src: [
116+
'Gruntfile.js',
117+
'src/<%= pkg.name %>.js',
118+
'test/unit/spec/<%= pkg.name %>.spec.js',
119+
],
116120
options: {
117121
config: '.jscs.json',
118122
},

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ A module providing an [AngularJS](http://angularjs.org/) filter which can be use
2222
```
2323
angular.module('myApp')
2424
.config(function (uiTreeFilterSettingsProvider) {
25-
uiTreeFilterSettingsProvider.supportedFields = ['title', 'description', 'username'];
25+
uiTreeFilterSettingsProvider.addresses = ['title', 'description', 'username'];
2626
});
2727
```
2828

@@ -68,9 +68,10 @@ Filter can be used in the template to as an argument of the `ng-show` or `ng-if`
6868

6969
## Configuration reference
7070

71-
- `supportedFields` (default: `['title']`): properties of notes against which pattern will be matched
71+
- `addresses` (default: `['title']`): properties of notes against which pattern will be matched.
72+
Deep filed access is supported: one can provide `foo.bar.baz` to match against item.foo.bar.baz value.
7273
- `regexFlags` (default: `'gi'`): [Regular expression flags](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Parameters) applied during he matching
73-
- `descendantCollectionField` (default: `'items'`): property of the node that holds nodes descendants
74+
- `descendantCollection` (default: `'items'`): name of item property that holds item descendants
7475

7576
## Performance
7677

bower.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "angular-ui-tree-filter",
3-
"version": "0.0.2",
3+
"version": "0.1.0",
44
"homepage": "https://github.com/ee/angular-ui-tree-filter",
55
"authors": [
66
"Jarek Rencz", "Jarek Rencz <[email protected]>"

demo/dist/angular-ui-tree-filter.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/angular-ui-tree-filter.js

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@
1010

1111
var uiTreeFilterSettings = this;
1212

13-
this.supportedFields = ['title'];
13+
this.addresses = ['title'];
1414
this.regexFlags = 'gi';
15-
this.descendantCollectionField = 'items';
15+
this.descendantCollection = 'items';
1616

1717
this.$get = function () {
1818
return {
19-
supportedFields: uiTreeFilterSettings.supportedFields,
19+
addresses: uiTreeFilterSettings.addresses,
2020
regexFlags: uiTreeFilterSettings.regexFlags,
21-
descendantCollectionField: uiTreeFilterSettings.descendantCollectionField,
21+
descendantCollection: uiTreeFilterSettings.descendantCollection,
2222
};
2323
};
2424
})
@@ -31,50 +31,68 @@
3131
* Iterates through given collection if flag is not true and sets a flag to true on first match.
3232
*
3333
* @param {Array} collection
34-
* @param {string} fieldName
3534
* @param {string} pattern
36-
* @param {boolean} flag
35+
* @param {string} address
36+
*
3737
* @returns {boolean}
3838
*/
39-
function visit(collection, pattern, fieldName, flag) {
40-
flag = flag || false;
41-
if (!flag && collection) {
42-
collection.forEach(function (collectionItem) {
43-
flag = flag ? true : testForField(collectionItem, fieldName, pattern);
44-
});
45-
}
46-
return flag;
39+
function visit(collection, pattern, address) {
40+
collection = collection || [];
41+
var foundSoFar = false;
42+
43+
collection.forEach(function (collectionItem) {
44+
foundSoFar = foundSoFar || testForField(collectionItem, pattern, address);
45+
});
46+
47+
return foundSoFar;
48+
}
49+
50+
/**
51+
* Resolves object value from dot-delimited address.
52+
*
53+
* @param object
54+
* @param path
55+
* @returns {*}
56+
*/
57+
function resolveAddress(object, path) {
58+
var parts = path.split('.');
59+
return parts.length < 2 ? object[parts[0]] : resolveAddress(object[parts[0]], parts.slice(1).join('.'));
4760
}
4861

4962
/**
5063
* Checks if object or its children matches a pattern on a given field
5164
*
52-
* @param {object} item
65+
* First it resolves the property address and gets the value.
66+
* If the value is a string it matches it against provided pattern.
67+
* If item matches because its property matches it's children are not checked.
68+
* Otherwise all item descendants are checked as well
69+
*
70+
* @param {Object} item
5371
* @param {string} pattern
54-
* @param {string} fieldName
72+
* @param {string} address property name or dot-delimited path to property.
5573
*
5674
* @returns {boolean}
5775
*/
58-
function testForField(item, pattern, fieldName) {
59-
var foundInField = item[fieldName] ?
60-
!!item[fieldName].match(new RegExp(pattern, uiTreeFilterSettings.regexFlags)) :
76+
function testForField(item, pattern, address) {
77+
var value = resolveAddress(item, address);
78+
var found = typeof value === 'string' ?
79+
!!value.match(new RegExp(pattern, uiTreeFilterSettings.regexFlags)) :
6180
false;
62-
63-
return visit(item[uiTreeFilterSettings.descendantCollectionField], fieldName, pattern, foundInField);
81+
return found || visit(item[uiTreeFilterSettings.descendantCollection], pattern, address);
6482
}
6583

6684
/**
67-
* Checks if pattern matches any of supported fields
85+
* Checks if pattern matches any of addresses
6886
*
6987
* @param {object} item
7088
* @param {string} pattern
7189
*
7290
* @returns {boolean}
7391
*/
74-
return function (item, pattern, supportedFields) {
75-
supportedFields = supportedFields || uiTreeFilterSettings.supportedFields;
76-
return supportedFields.reduce(function (foundInAnyField, fieldName) {
77-
return foundInAnyField ? true : testForField(item, pattern, fieldName);
92+
return function (item, pattern, addresses) {
93+
addresses = addresses || uiTreeFilterSettings.addresses;
94+
return pattern === undefined || addresses.reduce(function (foundSoFar, fieldName) {
95+
return foundSoFar || testForField(item, pattern, fieldName);
7896
}, false);
7997
};
8098
}]);

dist/angular-ui-tree-filter.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "angular-ui-tree-filter",
3-
"version": "0.0.2",
3+
"version": "0.1.0",
44
"description": "A module providing an AngularJS filter which can be used with angular-ui-tree to match tree nodes",
55
"scripts": {
66
"test": "grunt test"

src/angular-ui-tree-filter.js

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@
1010

1111
const uiTreeFilterSettings = this;
1212

13-
this.supportedFields = ['title'];
13+
this.addresses = ['title'];
1414
this.regexFlags = 'gi';
15-
this.descendantCollectionField = 'items';
15+
this.descendantCollection = 'items';
1616

1717
this.$get = function () {
1818
return {
19-
supportedFields: uiTreeFilterSettings.supportedFields,
19+
addresses: uiTreeFilterSettings.addresses,
2020
regexFlags: uiTreeFilterSettings.regexFlags,
21-
descendantCollectionField: uiTreeFilterSettings.descendantCollectionField,
21+
descendantCollection: uiTreeFilterSettings.descendantCollection,
2222
};
2323
};
2424
})
@@ -31,50 +31,68 @@
3131
* Iterates through given collection if flag is not true and sets a flag to true on first match.
3232
*
3333
* @param {Array} collection
34-
* @param {string} fieldName
3534
* @param {string} pattern
36-
* @param {boolean} flag
35+
* @param {string} address
36+
*
3737
* @returns {boolean}
3838
*/
39-
function visit(collection, pattern, fieldName, flag) {
40-
flag = flag || false;
41-
if (!flag && collection) {
42-
collection.forEach(function (collectionItem) {
43-
flag = flag ? true : testForField(collectionItem, fieldName, pattern);
44-
});
45-
}
46-
return flag;
39+
function visit(collection, pattern, address) {
40+
collection = collection || [];
41+
let foundSoFar = false;
42+
43+
collection.forEach(function (collectionItem) {
44+
foundSoFar = foundSoFar || testForField(collectionItem, pattern, address);
45+
});
46+
47+
return foundSoFar;
48+
}
49+
50+
/**
51+
* Resolves object value from dot-delimited address.
52+
*
53+
* @param object
54+
* @param path
55+
* @returns {*}
56+
*/
57+
function resolveAddress(object, path) {
58+
const parts = path.split('.');
59+
return parts.length < 2 ? object[parts[0]] : resolveAddress(object[parts[0]], parts.slice(1).join('.'));
4760
}
4861

4962
/**
5063
* Checks if object or its children matches a pattern on a given field
5164
*
52-
* @param {object} item
65+
* First it resolves the property address and gets the value.
66+
* If the value is a string it matches it against provided pattern.
67+
* If item matches because its property matches it's children are not checked.
68+
* Otherwise all item descendants are checked as well
69+
*
70+
* @param {Object} item
5371
* @param {string} pattern
54-
* @param {string} fieldName
72+
* @param {string} address property name or dot-delimited path to property.
5573
*
5674
* @returns {boolean}
5775
*/
58-
function testForField(item, pattern, fieldName) {
59-
const foundInField = item[fieldName] ?
60-
!!item[fieldName].match(new RegExp(pattern, uiTreeFilterSettings.regexFlags)) :
76+
function testForField(item, pattern, address) {
77+
const value = resolveAddress(item, address);
78+
const found = typeof value === 'string' ?
79+
!!value.match(new RegExp(pattern, uiTreeFilterSettings.regexFlags)) :
6180
false;
62-
63-
return visit(item[uiTreeFilterSettings.descendantCollectionField], fieldName, pattern, foundInField);
81+
return found || visit(item[uiTreeFilterSettings.descendantCollection], pattern, address);
6482
}
6583

6684
/**
67-
* Checks if pattern matches any of supported fields
85+
* Checks if pattern matches any of addresses
6886
*
6987
* @param {object} item
7088
* @param {string} pattern
7189
*
7290
* @returns {boolean}
7391
*/
74-
return function (item, pattern, supportedFields) {
75-
supportedFields = supportedFields || uiTreeFilterSettings.supportedFields;
76-
return supportedFields.reduce(function (foundInAnyField, fieldName) {
77-
return foundInAnyField ? true : testForField(item, pattern, fieldName);
92+
return function (item, pattern, addresses) {
93+
addresses = addresses || uiTreeFilterSettings.addresses;
94+
return pattern === undefined || addresses.reduce(function (foundSoFar, fieldName) {
95+
return foundSoFar || testForField(item, pattern, fieldName);
7896
}, false);
7997
};
8098
});

test/unit/spec/angular-ui-tree-filter.spec.js

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ describe('Module: ui.tree-filter', function () {
55

66
beforeEach(module('ui.tree-filter'));
77

8-
beforeEach(module(function(uiTreeFilterSettingsProvider) {
9-
uiTreeFilterSettingsProvider.supportedFields = ['title'];
8+
beforeEach(module(function (uiTreeFilterSettingsProvider) {
9+
uiTreeFilterSettingsProvider.addresses = ['title'];
1010
}));
1111

1212
beforeEach(function () {
@@ -32,17 +32,17 @@ describe('Module: ui.tree-filter', function () {
3232
id: 212,
3333
title: '2.1.1.1. bubble-burst',
3434
items: [],
35-
}
36-
]
37-
}
38-
]
35+
},
36+
],
37+
},
38+
],
3939
},
4040
{
4141
id: 22,
4242
title: '2.2. barehand-atomsplitting',
4343
items: [],
44-
}
45-
]
44+
},
45+
],
4646
},
4747
{
4848
id: 3,
@@ -63,7 +63,7 @@ describe('Module: ui.tree-filter', function () {
6363

6464
it('should match item on level 1', function () {
6565
const matchedString = 'romantic';
66-
66+
6767
expect(uiTreeFilter(sampleTree[0], matchedString)).toBe(false);
6868
expect(uiTreeFilter(sampleTree[1], matchedString)).toBe(false);
6969
expect(uiTreeFilter(sampleTree[1].items[0], matchedString)).toBe(false);
@@ -115,7 +115,7 @@ describe('Module: ui.tree-filter', function () {
115115

116116
it('should match regular expression (entire path)', function () {
117117
// 2.1, 2.1.1, 2.1.1.1 and 2 since it's part of the path, as well as 2.2
118-
const matchedString = "[0-9]\.[0-9]\..[a-z]";
118+
const matchedString = '[0-9]\.[0-9]\..[a-z]';
119119

120120
expect(uiTreeFilter(sampleTree[0], matchedString)).toBe(false);
121121
expect(uiTreeFilter(sampleTree[1], matchedString)).toBe(true);
@@ -135,4 +135,16 @@ describe('Module: ui.tree-filter', function () {
135135
expect(uiTreeFilter(sampleTree[0], matchedString, ['title', 'description'])).toBe(true);
136136
});
137137

138+
it('should match dot-delimited paths to value', function () {
139+
sampleTree[0].nested = {
140+
property: 'nested property value',
141+
};
142+
const matchedString = 'nested';
143+
144+
expect(uiTreeFilter(sampleTree[0], matchedString)).toBe(false);
145+
expect(uiTreeFilter(sampleTree[0], matchedString, ['title', 'description'])).toBe(false);
146+
expect(uiTreeFilter(sampleTree[0], matchedString, ['title', 'description', 'nested'])).toBe(false);
147+
expect(uiTreeFilter(sampleTree[0], matchedString, ['title', 'description', 'nested.property'])).toBe(true);
148+
});
149+
138150
});

0 commit comments

Comments
 (0)