mirror of
https://github.com/ansible/awx.git
synced 2026-05-08 01:47:35 -02:30
Add form-updating input validation for components
This commit is contained in:
@@ -2,9 +2,11 @@ function AddCredentialsController (credentialType) {
|
|||||||
let vm = this || {};
|
let vm = this || {};
|
||||||
|
|
||||||
vm.name = {
|
vm.name = {
|
||||||
|
state: {
|
||||||
|
required: true
|
||||||
|
},
|
||||||
label: {
|
label: {
|
||||||
text: 'Name',
|
text: 'Name',
|
||||||
required: true,
|
|
||||||
popover: {
|
popover: {
|
||||||
text: 'a, b, c'
|
text: 'a, b, c'
|
||||||
}
|
}
|
||||||
@@ -22,9 +24,11 @@ function AddCredentialsController (credentialType) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
vm.kind = {
|
vm.kind = {
|
||||||
|
state: {
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
label: {
|
label: {
|
||||||
text: 'Type',
|
text: 'Type',
|
||||||
required: true,
|
|
||||||
popover: {
|
popover: {
|
||||||
text: 'x, y, z'
|
text: 'x, y, z'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,13 +8,13 @@
|
|||||||
|
|
||||||
<at-panel-body>
|
<at-panel-body>
|
||||||
<at-form>
|
<at-form>
|
||||||
<at-input-text col="4" config="vm.name"></at-input-text>
|
<at-input-text tab="1" col="4" config="vm.name"></at-input-text>
|
||||||
<at-input-text col="4" config="vm.description"></at-input-text>
|
<at-input-text tab="2" col="4" config="vm.description"></at-input-text>
|
||||||
<at-input-select col="4" config="vm.kind"></at-input-select>
|
<at-input-select tab="3" col="4" config="vm.kind"></at-input-select>
|
||||||
|
|
||||||
<at-action-group col="12" pos="right">
|
<at-action-group col="12" pos="right">
|
||||||
<at-action config="vm.cancel"></at-action>
|
<at-action tab="4" config="vm.cancel"></at-action>
|
||||||
<at-action config="vm.save"></at-action>
|
<at-action tab="5" config="vm.save"></at-action>
|
||||||
</at-action-group>
|
</at-action-group>
|
||||||
</at-form>
|
</at-form>
|
||||||
</at-panel-body>
|
</at-panel-body>
|
||||||
|
|||||||
@@ -1,30 +1,33 @@
|
|||||||
let $state;
|
let $state;
|
||||||
|
|
||||||
function applyCancelProperties (scope) {
|
|
||||||
scope.text = scope.config.text || 'CANCEL';
|
|
||||||
scope.fill = 'Hollow';
|
|
||||||
scope.color = 'white';
|
|
||||||
scope.disabled = false;
|
|
||||||
scope.action = () => $state.go('^');
|
|
||||||
}
|
|
||||||
|
|
||||||
function applySaveProperties (scope) {
|
|
||||||
scope.text = 'SAVE';
|
|
||||||
scope.fill = '';
|
|
||||||
scope.color = 'green';
|
|
||||||
scope.disabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function link (scope, el, attrs, form) {
|
function link (scope, el, attrs, form) {
|
||||||
form.use('action', scope, el);
|
scope.config.state = scope.config.state || {};
|
||||||
|
let state = scope.config.state;
|
||||||
|
|
||||||
|
scope.form = form.use('action', state);
|
||||||
|
|
||||||
switch(scope.config.type) {
|
switch(scope.config.type) {
|
||||||
case 'cancel':
|
case 'cancel':
|
||||||
applyCancelProperties(scope);
|
setCancelDefaults(scope);
|
||||||
break;
|
break;
|
||||||
case 'save':
|
case 'save':
|
||||||
applySaveProperties(scope);
|
setSaveDefaults(scope);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCancelDefaults (scope) {
|
||||||
|
scope.text = 'CANCEL';
|
||||||
|
scope.fill = 'Hollow';
|
||||||
|
scope.color = 'white';
|
||||||
|
scope.action = () => $state.go('^');
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSaveDefaults (scope) {
|
||||||
|
scope.text = 'SAVE';
|
||||||
|
scope.fill = '';
|
||||||
|
scope.color = 'green';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<button class="btn at-Button{{ fill }}--{{ color }}" ng-disabled="disabled"
|
<button class="btn at-Button{{ fill }}--{{ color }}"
|
||||||
ng-class="{ 'at-Button--disabled': disabled }" ng-click="action()">
|
ng-disabled="config.type !== 'cancel' && !form.state.isValid"
|
||||||
{{ text }}
|
ng-class="{ 'at-Button--disabled': form.disabled }" ng-click="action()">
|
||||||
|
{{::text}}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,56 +1,70 @@
|
|||||||
function use (type, componentScope, componentElement) {
|
function use (type, component, el) {
|
||||||
let vm = this;
|
let vm = this;
|
||||||
|
|
||||||
let component;
|
let state;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'input':
|
case 'input':
|
||||||
component = vm.trackInput(componentElement);
|
state = vm.trackInput(component, el);
|
||||||
break;
|
break;
|
||||||
case 'action':
|
case 'action':
|
||||||
component = vm.trackAction(componentElement);
|
state = vm.trackAction(component, el);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error('An at-form cannot use component type:', type);
|
throw new Error('An at-form cannot use component type:', type);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentScope.meta = component;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
function trackInput (componentElement) {
|
function trackInput (component, el) {
|
||||||
let vm = this;
|
let vm = this;
|
||||||
|
|
||||||
let input = {
|
let form = {
|
||||||
el: componentElement,
|
state: vm.state,
|
||||||
tabindex: vm.inputs.length + 1
|
disabled: false
|
||||||
};
|
};
|
||||||
|
|
||||||
if (vm.inputs.length === 0) {
|
vm.inputs.push(component)
|
||||||
input.autofocus = true;
|
|
||||||
componentElement.find('input').focus();
|
return form;
|
||||||
|
}
|
||||||
|
|
||||||
|
function trackAction (component) {
|
||||||
|
let vm = this;
|
||||||
|
|
||||||
|
let form = {
|
||||||
|
state: vm.state,
|
||||||
|
disabled: false
|
||||||
|
};
|
||||||
|
|
||||||
|
vm.actions.push(component);
|
||||||
|
|
||||||
|
return form;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validate () {
|
||||||
|
let vm = this;
|
||||||
|
|
||||||
|
let isValid = true;
|
||||||
|
|
||||||
|
vm.inputs.forEach(input => {
|
||||||
|
if (!input.isValid) {
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
function check () {
|
||||||
|
let vm = this;
|
||||||
|
|
||||||
|
let isValid = vm.validate();
|
||||||
|
|
||||||
|
if (isValid !== vm.state.isValid) {
|
||||||
|
vm.state.isValid = isValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.inputs.push(input)
|
|
||||||
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
function trackAction (componentElement) {
|
|
||||||
let vm = this;
|
|
||||||
|
|
||||||
let action = {
|
|
||||||
el: componentElement
|
|
||||||
};
|
|
||||||
|
|
||||||
vm.actions.push(action);
|
|
||||||
|
|
||||||
return action;
|
|
||||||
}
|
|
||||||
|
|
||||||
function update () {
|
|
||||||
let vm = this;
|
|
||||||
|
|
||||||
vm.inputs.forEach(input => console.log(input));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function remove (id) {
|
function remove (id) {
|
||||||
@@ -62,13 +76,18 @@ function remove (id) {
|
|||||||
function AtFormController () {
|
function AtFormController () {
|
||||||
let vm = this;
|
let vm = this;
|
||||||
|
|
||||||
|
vm.state = {
|
||||||
|
isValid: false
|
||||||
|
};
|
||||||
|
|
||||||
vm.inputs = [];
|
vm.inputs = [];
|
||||||
vm.actions = [];
|
vm.actions = [];
|
||||||
|
|
||||||
vm.use = use;
|
vm.use = use;
|
||||||
vm.trackInput = trackInput;
|
vm.trackInput = trackInput;
|
||||||
vm.trackAction = trackAction;
|
vm.trackAction = trackAction;
|
||||||
vm.update = update;
|
vm.validate = validate;
|
||||||
|
vm.check = check;
|
||||||
vm.remove = remove;
|
vm.remove = remove;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<form>
|
<form>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<ng-transclude></ng-transclude>
|
<ng-transclude></ng-transclude>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<label class="at-InputLabel at-u-flat">
|
<label class="at-InputLabel at-u-flat">
|
||||||
<span ng-if="config.required" class="pull-left at-InputLabel-required">*</span>
|
<span ng-if="config.state.required" class="pull-left at-InputLabel-required">*</span>
|
||||||
<span class="pull-left">{{ config.text }}</span>
|
<span class="pull-left">{{::config.label.text}}</span>
|
||||||
<at-popover class="pull-left" config="config.popover"></at-popover>
|
<at-popover class="pull-left" config="config.label.popover"></at-popover>
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -2,16 +2,24 @@ let eventService;
|
|||||||
let pathService;
|
let pathService;
|
||||||
|
|
||||||
function link (scope, el, attrs, form) {
|
function link (scope, el, attrs, form) {
|
||||||
form.use('input', scope, el); // avoid passing scope? assign to scope.meta instead or reference form properties in view
|
scope.config.state = scope.config.state || {};
|
||||||
|
|
||||||
let input = el.find('input')[0];
|
let input = el.find('input')[0];
|
||||||
let select = el.find('select')[0];
|
let select = el.find('select')[0];
|
||||||
|
let state = scope.config.state;
|
||||||
|
|
||||||
|
setDefaults();
|
||||||
|
|
||||||
|
scope.form = form.use('input', state);
|
||||||
|
|
||||||
let listeners = eventService.addListeners(scope, [
|
let listeners = eventService.addListeners(scope, [
|
||||||
[input, 'focus', () => select.focus()],
|
[input, 'focus', () => select.focus],
|
||||||
[select, 'mousedown', () => scope.open = !scope.open],
|
[select, 'mousedown', () => scope.$apply(scope.open = !scope.open)],
|
||||||
[select, 'focus', () => input.classList.add('at-Input--focus')],
|
[select, 'focus', () => input.classList.add('at-Input--focus')],
|
||||||
[select, 'change', () => scope.open = false],
|
[select, 'change', () => {
|
||||||
|
scope.open = false;
|
||||||
|
check();
|
||||||
|
}],
|
||||||
[select, 'blur', () => {
|
[select, 'blur', () => {
|
||||||
input.classList.remove('at-Input--focus');
|
input.classList.remove('at-Input--focus');
|
||||||
scope.open = scope.open && false;
|
scope.open = scope.open && false;
|
||||||
@@ -20,13 +28,38 @@ function link (scope, el, attrs, form) {
|
|||||||
|
|
||||||
scope.$on('$destroy', () => eventService.remove(listeners));
|
scope.$on('$destroy', () => eventService.remove(listeners));
|
||||||
|
|
||||||
/*
|
function setDefaults () {
|
||||||
* Should notify form on:
|
if (scope.tab === 1) {
|
||||||
* - valid (required, passes validation) state change
|
select.focus();
|
||||||
*
|
}
|
||||||
* Should get from form:
|
|
||||||
* - display as disabled
|
state.isValid = state.isValid || false;
|
||||||
*/
|
state.validate = state.validate ? validate.bind(null, state.validate) : validate;
|
||||||
|
state.check = state.check || check;
|
||||||
|
state.message = state.message || '';
|
||||||
|
state.required = state.required || false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validate (fn) {
|
||||||
|
let isValid = true;
|
||||||
|
|
||||||
|
if (state.required && !state.value) {
|
||||||
|
isValid = false;
|
||||||
|
} else if (fn && !fn(scope.config.input)) {
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
function check () {
|
||||||
|
let isValid = state.validate();
|
||||||
|
|
||||||
|
if (isValid !== state.isValid) {
|
||||||
|
state.isValid = isValid;
|
||||||
|
form.check();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function atInputSelect (_eventService_, _pathService_) {
|
function atInputSelect (_eventService_, _pathService_) {
|
||||||
@@ -42,7 +75,8 @@ function atInputSelect (_eventService_, _pathService_) {
|
|||||||
link,
|
link,
|
||||||
scope: {
|
scope: {
|
||||||
config: '=',
|
config: '=',
|
||||||
col: '@'
|
col: '@',
|
||||||
|
tab: '@'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
<div class="col-sm-{{ col }}">
|
<div class="col-sm-{{ col }}">
|
||||||
<div class="form-group at-u-flat">
|
<div class="form-group at-u-flat">
|
||||||
<at-input-label config="config.label"></at-input-label>
|
<at-input-label config="config"></at-input-label>
|
||||||
<div class="at-InputGroup at-InputSelect">
|
<div class="at-InputGroup at-InputSelect">
|
||||||
<input type="text" class="form-control at-Input at-InputSelect-input"
|
<input type="text" class="form-control at-Input at-InputSelect-input"
|
||||||
ng-attr-autofocus="{{ meta.autofocus || undefined }}"
|
|
||||||
placeholder="{{ config.placeholder | uppercase }}" ng-model="config.input" />
|
|
||||||
|
|
||||||
<select class="form-control at-InputSelect-select" ng-model="config.input"
|
placeholder="{{ config.placeholder | uppercase }}"
|
||||||
tabindex="{{ meta.tabindex }}">
|
ng-model="config.state.value" ng-disabled="form.disabled"
|
||||||
<optgroup ng-repeat="group in config.data" label="{{ group.category | uppercase }}">
|
ng-change="config.state.check" />
|
||||||
|
|
||||||
|
<select class="form-control at-InputSelect-select" ng-model="config.state.value"
|
||||||
|
tabindex="{{::tab}}" ng-attr-autofocus="{{ tab == 1 || undefined }}"
|
||||||
|
ng-disabled="form.disabled">
|
||||||
|
<optgroup ng-repeat="group in config.data" label="{{::group.category | uppercase }}">
|
||||||
<option ng-repeat="item in group.data" value="{{ item.name }}">
|
<option ng-repeat="item in group.data" value="{{ item.name }}">
|
||||||
{{ item.name }}
|
{{ item.name }}
|
||||||
</option>
|
</option>
|
||||||
|
|||||||
@@ -1,5 +1,44 @@
|
|||||||
function link (scope, el, attrs, form) {
|
function link (scope, el, attrs, form) {
|
||||||
form.use('input', scope, el);
|
scope.config.state = scope.config.state || {};
|
||||||
|
let state = scope.config.state;
|
||||||
|
let input = el.find('input')[0];
|
||||||
|
|
||||||
|
setDefaults();
|
||||||
|
|
||||||
|
scope.form = form.use('input', state, input);
|
||||||
|
|
||||||
|
function setDefaults () {
|
||||||
|
if (scope.tab === '1') {
|
||||||
|
input.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
state.isValid = state.isValid || false;
|
||||||
|
state.validate = state.validate ? validate.bind(null, state.validate) : validate;
|
||||||
|
state.check = state.check || check;
|
||||||
|
state.message = state.message || '';
|
||||||
|
state.required = state.required || false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validate (fn) {
|
||||||
|
let isValid = true;
|
||||||
|
|
||||||
|
if (state.required && !state.value) {
|
||||||
|
isValid = false;
|
||||||
|
} else if (fn && !fn(scope.config.input)) {
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
function check () {
|
||||||
|
let isValid = state.validate();
|
||||||
|
|
||||||
|
if (isValid !== state.isValid) {
|
||||||
|
state.isValid = isValid;
|
||||||
|
form.check();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function atInputText (pathService) {
|
function atInputText (pathService) {
|
||||||
@@ -12,7 +51,8 @@ function atInputText (pathService) {
|
|||||||
link,
|
link,
|
||||||
scope: {
|
scope: {
|
||||||
config: '=',
|
config: '=',
|
||||||
col: '@'
|
col: '@',
|
||||||
|
tab: '@'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
<div class="col-sm-{{ col }}">
|
<div class="col-sm-{{::col}}">
|
||||||
<div class="form-group at-u-flat">
|
<div class="form-group at-u-flat">
|
||||||
<at-input-label config="config.label"></at-input-label>
|
<at-input-label config="config"></at-input-label>
|
||||||
<input type="text" class="form-control at-Input" ng-model="config.input"
|
<input type="text" class="form-control at-Input" ng-model="config.state.value"
|
||||||
ng-attr-autofocus="{{ meta.autofocus || undefined }}" tabindex="{{ meta.tabindex }}"
|
ng-attr-autofocus="{{tab == 1 || undefined }}"
|
||||||
placeholder="{{ config.placeholder }}" />
|
tabindex="{{::tab}}" placeholder="{{::config.placeholder}}"
|
||||||
|
ng-change="config.state.check()" ng-disabled="form.disabled" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,6 +6,6 @@
|
|||||||
<div class="at-Popover-arrow">
|
<div class="at-Popover-arrow">
|
||||||
<i class="fa fa-caret-left fa-2x"></i>
|
<i class="fa fa-caret-left fa-2x"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="at-Popover-content">{{ config.text }}</div>
|
<div class="at-Popover-content">{{::config.text}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,11 +8,9 @@ function addListeners (scope, list) {
|
|||||||
return listeners;
|
return listeners;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addListener (scope, el, name, fn, type) {
|
function addListener (scope, el, name, fn) {
|
||||||
type = type || '$apply';
|
|
||||||
|
|
||||||
let listener = {
|
let listener = {
|
||||||
fn: () => scope[type](fn),
|
fn,
|
||||||
name,
|
name,
|
||||||
el
|
el
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user