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