Skip to content

Commit 629ecea

Browse files
jeff-phillips-18dgutride
authored andcommitted
Convert patternfly.form module directives to components. (#375)
* Convert patternfly.form module directives to components. * Change default usage for form groups to be in BS3 normal form Remove the default classes added to the label and input fields. This allows usage in any type of bootstrap form. Applications can simply add the classes via the pfLabelClass and pfInputClass bindings to use in a horizontal form. Added examples of both use cases.
1 parent d9b16c7 commit 629ecea

File tree

11 files changed

+314
-295
lines changed

11 files changed

+314
-295
lines changed
Lines changed: 6 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/**
22
* @ngdoc directive
3-
* @name patternfly.form.directive:pfFormButtons
3+
* @name patternfly.form.component:pfFormButtons
4+
* @restrict E
45
*
56
* @description
67
* Encapsulates the standard structure and styling for create and cancel buttons
@@ -20,18 +21,18 @@
2021
<div ng-controller="FormButtonCtrl">
2122
<p>Saved?</p>
2223
<p>{{ status }}</p>
23-
<form>
24+
<form name="testForm">
2425
<div class="form-group>
2526
<label class="control-label col-sm-2">Input</label>
26-
<input class="form-control col-sm-5" name="item" ng-model="input" type="text">
27+
<input class="form-control col-sm-5" name="item" ng-model="input" type="text" required>
2728
</div>
28-
<div pf-form-buttons pf-on-cancel="cancel()" pf-on-save="save(item)" pf-working="working"></div>
29+
<pf-form-buttons pf-on-cancel="cancel()" pf-on-save="save(item)" pf-working="working"></pf-form-buttons>
2930
</form>
3031
</div>
3132
</file>
3233
3334
<file name="script.js">
34-
angular.module( 'patternfly.form' ).controller( 'FormButtonCtrl', function( $scope, $timeout ) {
35+
angular.module( 'patternfly.form' ).controller( 'FormButtonCtrl', function( $scope, $timeout, $element ) {
3536
$scope.status = 'Not yet Saved'
3637
$scope.working = false;
3738
@@ -52,37 +53,3 @@
5253
</file>
5354
</example>
5455
*/
55-
angular.module('patternfly.form').directive('pfFormButtons', function () {
56-
'use strict';
57-
58-
return {
59-
replace: true,
60-
require: '^form',
61-
templateUrl: 'form/form-buttons/form-buttons.html',
62-
scope: {
63-
pfHandleCancel: '&pfOnCancel',
64-
pfHandleSave: '&pfOnSave',
65-
pfWorking: '=',
66-
pfButtonContainerClass: '@'
67-
},
68-
link: function (scope, iElement, iAttrs, controller) {
69-
if (scope.pfWorking === undefined) {
70-
scope.pfWorking = false;
71-
}
72-
73-
scope.isInvalid = function () {
74-
var invalid = controller.$invalid;
75-
76-
angular.forEach(controller, function (value) {
77-
if (value && value.$error) {
78-
if (value.$error.server) {
79-
invalid = false;
80-
}
81-
}
82-
});
83-
84-
return invalid;
85-
};
86-
}
87-
};
88-
});

src/form/examples/form-group.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* @ngdoc directive
3+
* @name patternfly.form.directive:pfFormGroup
4+
* @restrict E
5+
*
6+
* @description
7+
* Encapsulates the structure and styling for a label + input used within a
8+
* Bootstrap3 based form.
9+
*
10+
* This directive creates new scope.
11+
*
12+
* @param {string} pfLabel the text for the <label> element.
13+
* @param {string} pfFieldId the id of the form field. Default value is id of the form field element.
14+
* @param {string} pfLabelClass the class of the label element. Default value is "col-sm-2".
15+
* @param {string} pfInputClass the class of the input element. Default value is "col-sm-5".
16+
*
17+
* @example
18+
<example module="patternfly.form">
19+
20+
<file name="index.html">
21+
<div ng-controller="FormDemoCtrl">
22+
<p>Name: {{ item.name }}</p>
23+
<p>Description: {{ item.description }}</p>
24+
<form>
25+
<pf-form-group pf-label="Name" required>
26+
<input id="name" name="name" ng-model="item.name" type="text" required/>
27+
</pf-form-group>
28+
<pf-form-group pf-label="Description">
29+
<textarea id="description" name="description" ng-model="item.description">
30+
{{ item.description }}
31+
</textarea>
32+
</pf-form-group>
33+
</form>
34+
<p>Horizontal Form</p>
35+
<form class="form-horizontal">
36+
<pf-form-group pf-label="Name" required pf-label-class="col-sm-2" pf-input-class="col-sm-5">
37+
<input id="name" name="name" ng-model="item.name" type="text" required/>
38+
</pf-form-group>
39+
<pf-form-group pf-label="Description" pf-label-class="col-sm-2" pf-input-class="col-sm-5">
40+
<textarea id="description" name="description" ng-model="item.description">
41+
{{ item.description }}
42+
</textarea>
43+
</pf-form-group>
44+
</form>
45+
</div>
46+
</file>
47+
48+
<file name="script.js">
49+
angular.module( 'patternfly.form' ).controller( 'FormDemoCtrl', function( $scope ) {
50+
$scope.item = {
51+
name: 'Homer Simpson',
52+
description: 'I like donuts and Duff. Doh!'
53+
};
54+
});
55+
</file>
56+
</example>
57+
*/
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/**
2+
* @ngdoc directive
3+
* @name patternfly.form.directive:pfRemainingCharsCount
4+
*
5+
* @description
6+
* Directive for showing a characters remaining count and triggering warning and error</br>
7+
* behavior when passing specified thresholds. When the <code>chars-warn-remaining</code> threshold is passed, </br>
8+
* the <code>chars-warn-remaining-pf</code> css class is applied to the <code>count-fld</code>, which by default, turns </br>
9+
* the remaining count number <font color='red'>red</font>.</br>
10+
* By default, characters may be entered into the text field after the <code>chars-max-limit</code> limit has been reached,</br>
11+
* the remaining count number will become a negative value. Setting the <code>blockInputAtMaxLimit</code> to <em>true</em>,</br>
12+
* will block additional input into the text field after the max has been reached; additionally a right-click 'paste' will only </br>
13+
* paste characters until the maximum character limit is reached.
14+
*
15+
* @param {string} ng-model The scope model variable which contains the initial text for the text field. Required, but</br>
16+
* can be an emptly string ("").
17+
* @param {string} count-fld The id of the field to display the 'characters-remaining' count.
18+
* @param {string} chars-max-limit Number representing the maximum number of characters to allow before dispatching a<br/>
19+
* 'overCharsMaxLimit' event. When the number of characters falls below <code>chars-max-limit</code>, a 'underCharsMaxLimit'<br/>
20+
* event is dispatched.
21+
* @param {string} chars-warn-remaining Number of remaining characters to warn upon. The 'chars-warn-remaining-pf'<br/>
22+
* class will be applied to the <code>count-fld</code> when the remaining characters is less than the<br/>
23+
* <code>chars-warn-remaining</code> threshold. When/if the number of remaining characters becomes greater than the<br/>
24+
* <code>chars-warn-remaining</code> threshold, the 'chars-warn-remaining-pf' class is removed from the <code>count-fld</code> field.
25+
* @param {boolean=} block-input-at-max-limit If true, no more characters can be entered into the text field when the<br/>
26+
* <code>chars-max-limit</code> has been reached. If false (the default), characters may be entered into the text field after the<br/>
27+
* max. limit has been reached, but these additional characters will trigger the 'overCharsMaxLimit' event to be<br/>
28+
* dispatched. When <code>blockInputAtMaxLimit</code> is <em>true</em>, a right-click 'paste' will only paste<br/>
29+
* characters until the maximum character limit is reached.
30+
*
31+
* @example
32+
<example module="patternfly.example">
33+
<file name="index.html">
34+
<div ng-controller="DemoCtrl" style="display:inline-block; width: 100%;">
35+
36+
<style>
37+
textarea {
38+
resize: none;
39+
}
40+
</style>
41+
42+
<div class="container">
43+
<strong>Max limit: 20, warn when 5 or less remaining, disable button after max limit</strong>
44+
<div class="row">
45+
<div class="col-md-4">
46+
47+
<form>
48+
<div class="form-group">
49+
<label for="messageArea"></label>
50+
<textarea class="form-control" pf-remaining-chars-count id="messageArea_1" ng-model="messageArea1text" chars-max-limit="20" chars-warn-remaining="5"
51+
count-fld="charRemainingCntFld_1" name="text" placeholder="Type in your message" rows="5"></textarea>
52+
</div>
53+
<span class="pull-right chars-remaining-pf">
54+
<span id="charRemainingCntFld_1"></span>
55+
<button id="postBtn_1" ng-disabled="charsMaxLimitExceeded" type="submit" class="btn btn-default">Post New Message</button>
56+
</span>
57+
</form>
58+
59+
</div>
60+
</div>
61+
<br>
62+
<strong>Max limit: 10, warn when 2 or less remaining, block input after max limit</strong>
63+
<div class="row">
64+
<div class="col-md-4">
65+
<form>
66+
<div class="form-group">
67+
<label for="messageArea"></label>
68+
<textarea class="form-control" pf-remaining-chars-count id="messageArea_2" ng-model="messageArea2text" chars-max-limit="10" chars-warn-remaining="2"
69+
block-input-at-max-limit="true" count-fld="charRemainingCntFld_2" name="text"
70+
placeholder="Type in your message" rows="5"></textarea>
71+
</div>
72+
<span class="pull-left">
73+
<button id="postBtn_2" type="submit" class="btn btn-default">Submit</button>
74+
</span>
75+
<span class="pull-right chars-remaining-pf">
76+
<span id="charRemainingCntFld_2"></span>
77+
</span>
78+
</form>
79+
</div>
80+
</div>
81+
<br>
82+
<strong>Max limit: 10, warn when 5 or less remaining, block input after max limit</strong>
83+
<div class="row">
84+
<div class="col-md-4">
85+
<input id="input_3" pf-remaining-chars-count chars-max-limit="10" ng-model="messageInput3text" chars-warn-remaining="5" count-fld="charRemainingCntFld_3"
86+
block-input-at-max-limit="true"/>
87+
<span class="chars-remaining-pf"><span id="charRemainingCntFld_3" style="padding-left: 5px"></span>Remaining</span>
88+
</div>
89+
</div>
90+
</div>
91+
</file>
92+
93+
<file name="script.js">
94+
angular.module( 'patternfly.example', ['patternfly.form']);
95+
96+
angular.module( 'patternfly.example' ).controller( 'DemoCtrl', function( $scope ) {
97+
$scope.messageArea1text = "Initial Text";
98+
$scope.messageArea2text = "";
99+
$scope.messageInput3text = "";
100+
101+
$scope.charsMaxLimitExceeded = false;
102+
103+
// 'tfId' will equal the id of the text area/input field which
104+
// triggered the event
105+
$scope.$on('overCharsMaxLimit', function (event, tfId) {
106+
if(!$scope.charsMaxLimitExceeded){
107+
$scope.charsMaxLimitExceeded = true;
108+
}
109+
});
110+
111+
// 'tfId' will equal the id of the text area/input field which
112+
// triggered the event
113+
$scope.$on('underCharsMaxLimit', function (event, tfId) {
114+
if($scope.charsMaxLimitExceeded){
115+
$scope.charsMaxLimitExceeded = false;
116+
}
117+
});
118+
119+
});
120+
121+
</file>
122+
</example>
123+
*/
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
angular.module('patternfly.form').component('pfFormButtons', {
2+
3+
bindings: {
4+
pfHandleCancel: '&pfOnCancel',
5+
pfHandleSave: '&pfOnSave',
6+
pfWorking: '=',
7+
pfButtonContainerClass: '@'
8+
},
9+
require: {
10+
form: '^form'
11+
},
12+
templateUrl: 'form/form-buttons/form-buttons.html',
13+
controller: function () {
14+
'use strict';
15+
16+
var ctrl = this;
17+
18+
ctrl.$onInit = function () {
19+
if (ctrl.pfWorking === undefined) {
20+
ctrl.pfWorking = false;
21+
}
22+
23+
angular.extend(ctrl, {
24+
isInvalid: isInvalid
25+
});
26+
};
27+
28+
function isInvalid () {
29+
var invalid = ctrl.form.$invalid;
30+
31+
if (ctrl.form && ctrl.form.name && ctrl.form.name.$error) {
32+
if (ctrl.form.name.$error.server) {
33+
invalid = false;
34+
}
35+
}
36+
37+
return invalid;
38+
}
39+
}
40+
});

src/form/form-buttons/form-buttons.html

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
<div class="form-group">
2-
<div class="{{ pfButtonContainerClass }}">
2+
<div class="{{$ctrl.pfButtonContainerClass}}">
33
<div class="control-group buttons">
44
<button class="btn btn-default"
55
type="button"
6-
ng-click="pfHandleCancel()"
7-
ng-disabled="pfWorking"
6+
ng-click="$ctrl.pfHandleCancel()"
7+
ng-disabled="$ctrl.pfWorking"
88
translate>
99
Cancel
1010
</button>
1111

1212
<button class="btn btn-primary"
13-
ng-click="pfHandleSave(); pfWorking = true"
14-
ng-disabled="isInvalid() || pfWorking">
13+
ng-click="$ctrl.pfHandleSave(); $ctrl.pfWorking = true"
14+
ng-disabled="$ctrl.isInvalid() || $ctrl.pfWorking">
1515

16-
<i class="icon-spinner icon-spin" ng-show="pfWorking"></i>
17-
<span ng-show="pfWorking" translate>Saving...</span>
18-
<span ng-hide="pfWorking" translate>Save</span>
16+
<i class="icon-spinner icon-spin" ng-show="$ctrl.pfWorking"></i>
17+
<span ng-show="$ctrl.pfWorking" translate>Saving...</span>
18+
<span ng-hide="$ctrl.pfWorking" translate>Save</span>
1919
</button>
2020
</div>
2121
</div>
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
angular.module('patternfly.form').component('pfFormGroup', {
2+
3+
bindings: {
4+
pfLabel: '@',
5+
pfField: '@',
6+
pfLabelClass: '@',
7+
pfInputClass: '@'
8+
9+
},
10+
require: {
11+
form: '^form'
12+
},
13+
transclude: true,
14+
templateUrl: 'form/form-group/form-group.html',
15+
16+
controller: function ($element) {
17+
'use strict';
18+
19+
var ctrl = this;
20+
21+
ctrl.$onInit = function () {
22+
angular.extend(ctrl, {
23+
hasErrors: hasErrors
24+
});
25+
};
26+
27+
ctrl.$postLink = function () {
28+
var input = getInput($element);
29+
var type = input.attr('type');
30+
31+
if (['checkbox', 'radio', 'time'].indexOf(type) === -1) {
32+
input.addClass('form-control');
33+
}
34+
35+
if (!ctrl.pfField) {
36+
ctrl.pfField = input.attr('id');
37+
}
38+
39+
if (input.attr('required')) {
40+
$element.addClass('required');
41+
}
42+
43+
if (ctrl.form[ctrl.pfField]) {
44+
ctrl.error = ctrl.form[ctrl.pfField].$error;
45+
}
46+
};
47+
48+
function hasErrors () {
49+
return ctrl.form[ctrl.pfField] && ctrl.form[ctrl.pfField].$invalid && ctrl.form[ctrl.pfField].$dirty;
50+
}
51+
52+
function getInput (element) {
53+
// table is used for bootstrap3 date/time pickers
54+
var input = element.find('table');
55+
56+
if (input.length === 0) {
57+
input = element.find('input');
58+
59+
if (input.length === 0) {
60+
input = element.find('select');
61+
62+
if (input.length === 0) {
63+
input = element.find('textarea');
64+
}
65+
}
66+
}
67+
return input;
68+
}
69+
}
70+
});

0 commit comments

Comments
 (0)