Skip to content

Commit a5a6ce5

Browse files
refact(chapter-10): add helper directives and tests for field
1 parent 6109742 commit a5a6ce5

File tree

11 files changed

+119
-48
lines changed

11 files changed

+119
-48
lines changed

1820EN_10_Code/04_field_directive/directive.js

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
angular.module('field-directive', ['input.html', 'textarea.html', 'select.html'])
1+
angular.module('field-directive', ['input.html', 'textarea.html', 'select.html', 'validationMessages.html'])
22

33
.directive('field', function($compile, $http, $templateCache, $interpolate) {
44

@@ -65,6 +65,13 @@ angular.module('field-directive', ['input.html', 'textarea.html', 'select.html']
6565
// and the bind-validation-message directive will be able to access it
6666
},
6767
compile: function(element, attrs) {
68+
if ( attrs.ngRepeat || attrs.ngSwitch || attrs.uiIf ) {
69+
throw new Error('The ng-repeat, ng-switch and ui-if directives are not supported on the same element as the field directive.');
70+
}
71+
if ( !attrs.ngModel ) {
72+
throw new Error('The ng-model directive must appear on the field element');
73+
}
74+
6875
// Extract the label and validation message info from the directive's original element
6976
var messageMap = getValidationMessageMap(element);
7077
var labelContent = extractLabelContent(element, attrs);
@@ -96,16 +103,8 @@ angular.module('field-directive', ['input.html', 'textarea.html', 'select.html']
96103
// We can't use interpolation in the template for directives such as ng-model
97104
var inputElement = findInputElement(newElement);
98105
angular.forEach(attrs.$attr, function (original, normalized) {
99-
switch ( normalized ) {
100-
case 'ngRepeat':
101-
case 'ngSwitch':
102-
case 'uiIf':
103-
throw new Error(normalized + ' directives are not supported on the same element as the field directive.');
104-
default:
105-
var value = element.attr(original);
106-
inputElement.attr(original, value);
107-
break;
108-
}
106+
var value = element.attr(original);
107+
inputElement.attr(original, value);
109108
});
110109

111110
// Wire up the input (id and name) and its label (for).
@@ -122,11 +121,8 @@ angular.module('field-directive', ['input.html', 'textarea.html', 'select.html']
122121
// Place our template as a child of the original element
123122
element.append(newElement);
124123

125-
// Now that our template has been compiled and linked
126-
// we can access the <input> element's ngModelController
127-
childScope.$evalAsync(function(scope) {
128-
scope.$field = inputElement.controller('ngModel');
129-
});
124+
// Now that our template has been compiled and linked we can access the <input> element's ngModelController
125+
childScope.$field = inputElement.controller('ngModel');
130126
});
131127
};
132128
}
@@ -158,4 +154,11 @@ angular.module('field-directive', ['input.html', 'textarea.html', 'select.html']
158154
});
159155
}
160156
};
157+
})
158+
159+
.directive('validationMessages', function() {
160+
return {
161+
restrict: 'E',
162+
templateUrl: 'validationMessages.html'
163+
};
161164
});
Lines changed: 81 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,96 @@
11
describe('field directive', function () {
2-
var scope, $compile, element;
2+
var scope, $compile, element, $httpBackend;
33

44
beforeEach(module('field-directive'));
5-
beforeEach(inject(function ($rootScope, _$compile_) {
5+
beforeEach(inject(function ($rootScope, _$compile_, _$httpBackend_) {
66
scope = $rootScope;
77
$compile = _$compile_;
8+
$httpBackend = _$httpBackend_;
89
}));
910

1011
describe('templates', function() {
11-
it('loads up a specified template', function() {});
12-
it('loads up a template called "input" if none specified', function() {});
13-
it('handles a missing template', function() {});
12+
it('loads up a specified template', function() {
13+
var element = $compile('<field field-label="X" ng-model="x" template="select.html"></field>')(scope);
14+
scope.$digest();
15+
expect(element.find('select')[0]).toBeDefined();
16+
});
17+
18+
it('loads up a template called "input.html" if none specified', function() {
19+
var element = $compile('<field field-label="X" ng-model="x"></field>')(scope);
20+
scope.$digest();
21+
expect(element.find('input')[0]).toBeDefined();
22+
});
23+
24+
it('handles a missing template', function() {
25+
$httpBackend.expectGET('notDefined.html').respond(404, []);
26+
expect(function() {
27+
var element = $compile('<field field-label="X" ng-model="x" template="notDefined.html"></field>')(scope);
28+
$httpBackend.flush();
29+
}).toThrow();
30+
});
31+
32+
it('appends the template onto the field element', function() {
33+
var element = $compile('<field field-label="X" ng-model="x"></field>')(scope);
34+
scope.$digest();
35+
expect(element.prop('nodeName')).toBe('FIELD');
36+
expect(element.find('input')[0]).toBeDefined();
37+
});
38+
39+
it('applies name and id to the template\'s input element', function() {
40+
var element = $compile('<field field-label="X" ng-model="x.y"></field>')(scope);
41+
scope.$digest();
42+
var inputElement = element.find('input');
43+
expect(inputElement.attr('name')).toContain('x_y');
44+
expect(inputElement.attr('id')).toContain('x_y');
45+
});
46+
47+
it('applies a for attribute to the template\'s label element', function() {
48+
var element = $compile('<field field-label="X" ng-model="x.y"></field>')(scope);
49+
scope.$digest();
50+
var labelElement = element.find('label');
51+
expect(labelElement.attr('for')).toContain('x_y');
52+
});
1453
});
1554
describe('attributes', function() {
16-
it('copies attributes from the directive to the input/select/textarea element of the template', function() {});
17-
it('interpolates the label attribute into the label element (of the template), if provided', function() {});
55+
it('copies attributes from the directive to the input/select/textarea element of the template', function() {
56+
var element = $compile('<field field-label="X" ng-model="x.y" ng-maxlength="3" x-y-z></field>')(scope);
57+
scope.$digest();
58+
var inputElement = element.find('input');
59+
expect(inputElement.attr('ng-maxlength')).toBe('3');
60+
expect(inputElement.attr('x-y-z')).toBe('');
61+
});
62+
63+
it('raises an error if the field element contains ng-repeat, ng-switch or ui-if', function() {
64+
expect(function() {
65+
var element = $compile('<field field-label="X" ng-model="x.y" ng-repeat="a in b" x-y-z></field>')(scope);
66+
}).toThrow();
67+
expect(function() {
68+
var element = $compile('<field field-label="X" ng-model="x.y" ng-switch="a" x-y-z></field>')(scope);
69+
}).toThrow();
70+
expect(function() {
71+
var element = $compile('<field field-label="X" ng-model="x.y" ui-if="a" x-y-z></field>')(scope);
72+
}).toThrow();
73+
});
74+
75+
it('interpolates the field-label attribute into the label element (of the template), if provided', function() {
76+
77+
});
1878
it('transcludes the label element (child of the directive) contents into the label element (of the template), if provided', function() {});
1979
it('errors if neither an attribute nor element provide a label', function() {});
2080
});
81+
describe('fieldController', function() {
82+
it('puts all validator element stuff into fieldController.messageMap', function() {});
83+
84+
});
85+
describe('scope', function() {
86+
it('puts the ngModelController of the input element onto the scope as $field', function() {});
87+
});
88+
});
89+
90+
describe('validation-messages directive', function() {
91+
92+
});
93+
94+
describe('bind-validation-message directive', function() {
2195

2296
});

1820EN_10_Code/04_field_directive/index.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@
1313
<script src="template/input.html.js"></script>
1414
<script src="template/textarea.html.js"></script>
1515
<script src="template/select.html.js"></script>
16+
<script src="template/validationMessages.html.js"></script>
1617
</head>
17-
<body ng-init="myModel = { items: [ { model:10, label: 'Model 1'}, { model: 20, label: 'Model 2'}] }">
18+
<body ng-init="myModel = {}">
1819
<form name="form" class="form-inline" novalidate>
1920
<field ng-model="myModel.myNumber" type="number" >
2021
<label>Labels which can <span ng-repeat="i in [0,1]">{{i}}</span> contain directives</label>

1820EN_10_Code/04_field_directive/template/input.html

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22
<label class="control-label" >{{label}}</label>
33
<div class="controls">
44
<input>
5-
<span class="help-inline"
6-
ng-repeat="(key, error) in $field.$error"
7-
ng-show="error && $field.$dirty"
8-
bind-validation-message="{{key}}"></span>
5+
<validation-messages></validation-messages>
96
</div>
107
</div>

1820EN_10_Code/04_field_directive/template/input.html.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@ angular.module("input.html", []).run(["$templateCache", function($templateCache)
44
" <label class=\"control-label\" >{{label}}</label>" +
55
" <div class=\"controls\">" +
66
" <input>" +
7-
" <span class=\"help-inline\" " +
8-
" ng-repeat=\"(key, error) in $field.$error\"" +
9-
" ng-show=\"error && $field.$dirty\"" +
10-
" bind-validation-message=\"{{key}}\"></span>" +
7+
" <validation-messages></validation-messages>" +
118
" </div>" +
129
"</div>");
1310
}]);

1820EN_10_Code/04_field_directive/template/select.html

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22
<label class="control-label">{{label}}</label>
33
<div class="controls">
44
<select></select>
5-
<span class="help-inline"
6-
ng-repeat="(key, error) in $field.$error"
7-
ng-show="error && $field.$dirty"
8-
bind-validation-message="{{key}}"></span>
5+
<validation-messages></validation-messages>
96
</div>
107
</div>

1820EN_10_Code/04_field_directive/template/select.html.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@ angular.module("select.html", []).run(["$templateCache", function($templateCache
44
" <label class=\"control-label\">{{label}}</label>" +
55
" <div class=\"controls\">" +
66
" <select></select>" +
7-
" <span class=\"help-inline\" " +
8-
" ng-repeat=\"(key, error) in $field.$error\"" +
9-
" ng-show=\"error && $field.$dirty\"" +
10-
" bind-validation-message=\"{{key}}\"></span>" +
7+
" <validation-messages></validation-messages>" +
118
" </div>" +
129
"</div>");
1310
}]);

1820EN_10_Code/04_field_directive/template/textarea.html

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22
<label class="control-label">{{label}}</label>
33
<div class="controls">
44
<textarea></textarea>
5-
<span class="help-inline"
6-
ng-repeat="(key, error) in $field.$error"
7-
ng-show="error && $field.$dirty"
8-
bind-validation-message="{{key}}"></span>
5+
<validation-messages></validation-messages>
96
</div>
107
</div>

1820EN_10_Code/04_field_directive/template/textarea.html.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@ angular.module("textarea.html", []).run(["$templateCache", function($templateCac
44
" <label class=\"control-label\">{{label}}</label>" +
55
" <div class=\"controls\">" +
66
" <textarea></textarea>" +
7-
" <span class=\"help-inline\" " +
8-
" ng-repeat=\"(key, error) in $field.$error\"" +
9-
" ng-show=\"error && $field.$dirty\"" +
10-
" bind-validation-message=\"{{key}}\"></span>" +
7+
" <validation-messages></validation-messages>" +
118
" </div>" +
129
"</div>");
1310
}]);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<span class="help-inline"
2+
ng-repeat="(key, error) in $field.$error"
3+
ng-show="error && $field.$dirty"
4+
bind-validation-message="{{key}}"></span>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
angular.module("validationMessages.html", []).run(["$templateCache", function($templateCache) {
2+
$templateCache.put("validationMessages.html",
3+
"<span class=\"help-inline\" " +
4+
" ng-repeat=\"(key, error) in $field.$error\"" +
5+
" ng-show=\"error && $field.$dirty\"" +
6+
" bind-validation-message=\"{{key}}\"></span>");
7+
}]);

0 commit comments

Comments
 (0)