Validate forms the Angular way. Also: Validate on submit.
This commit is contained in:
parent
caf732a04c
commit
2a0b2add0d
|
@ -59,7 +59,6 @@
|
||||||
<script src="scripts/directives/tokenForm.js"></script>
|
<script src="scripts/directives/tokenForm.js"></script>
|
||||||
|
|
||||||
<script src="scripts/validation/constraints.js"></script>
|
<script src="scripts/validation/constraints.js"></script>
|
||||||
<script src="scripts/validation/validator.js"></script>
|
|
||||||
<!-- endbuild -->
|
<!-- endbuild -->
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
angular.module('ffffng')
|
angular.module('ffffng')
|
||||||
.directive('fNodeForm', function () {
|
.directive('fNodeForm', function () {
|
||||||
var ctrl = function ($scope, $timeout, Constraints, Validator, _, config, $window) {
|
var ctrl = function ($scope, $timeout, Constraints, $element, _, config, $window) {
|
||||||
$scope.config = config;
|
$scope.config = config;
|
||||||
angular.extend($scope, {
|
angular.extend($scope, {
|
||||||
center: {
|
center: {
|
||||||
|
@ -76,19 +76,13 @@ angular.module('ffffng')
|
||||||
$scope.markers = {};
|
$scope.markers = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
var isValid = _.reduce(Constraints.node, function (isValids, constraint, field) {
|
$scope.constraints = Constraints.node;
|
||||||
isValids[field] = Validator.forConstraint(constraint, true);
|
|
||||||
return isValids;
|
var submitted = false;
|
||||||
}, {});
|
|
||||||
var areValid = Validator.forConstraints(Constraints.node);
|
|
||||||
|
|
||||||
$scope.hasError = function (field) {
|
$scope.hasError = function (field) {
|
||||||
var value = $scope.node[field];
|
var input = $scope.nodeForm[field];
|
||||||
return !isValid[field](value);
|
return input.$invalid && submitted;
|
||||||
};
|
|
||||||
|
|
||||||
$scope.hasAnyError = function () {
|
|
||||||
return !areValid($scope.node);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var duplicateError = {
|
var duplicateError = {
|
||||||
|
@ -98,6 +92,18 @@ angular.module('ffffng')
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.onSubmit = function (node) {
|
$scope.onSubmit = function (node) {
|
||||||
|
submitted = true;
|
||||||
|
|
||||||
|
if ($scope.nodeForm.$invalid) {
|
||||||
|
var firstInvalid = _.filter($element.find('form').find('input'), function (input) {
|
||||||
|
return $scope.nodeForm[input.name].$invalid;
|
||||||
|
})[0];
|
||||||
|
if (firstInvalid) {
|
||||||
|
$window.scrollTo(0, $window.pageYOffset + firstInvalid.getBoundingClientRect().top - 100);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$scope.error = null;
|
$scope.error = null;
|
||||||
$scope.save(node).error(function (response, code) {
|
$scope.save(node).error(function (response, code) {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
|
@ -109,7 +115,7 @@ angular.module('ffffng')
|
||||||
}
|
}
|
||||||
$window.scrollTo(0, 0);
|
$window.scrollTo(0, 0);
|
||||||
});
|
});
|
||||||
};
|
}.bind(this);
|
||||||
|
|
||||||
$scope.updateMap($scope.node.coords);
|
$scope.updateMap($scope.node.coords);
|
||||||
withValidCoords($scope.node.coords, function (lat, lng) {
|
withValidCoords($scope.node.coords, function (lat, lng) {
|
||||||
|
|
|
@ -2,17 +2,17 @@
|
||||||
|
|
||||||
angular.module('ffffng')
|
angular.module('ffffng')
|
||||||
.directive('fTokenForm', function () {
|
.directive('fTokenForm', function () {
|
||||||
var ctrl = function ($scope, Constraints, Validator) {
|
var ctrl = function ($scope, Constraints) {
|
||||||
var isValid = Validator.forConstraint(Constraints.token);
|
$scope.constraints = Constraints;
|
||||||
$scope.hasError = function () {
|
$scope.submitted = false;
|
||||||
var value = $scope.token;
|
|
||||||
if (value === undefined) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return !isValid(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.doSubmit = function (token) {
|
$scope.doSubmit = function (token) {
|
||||||
|
$scope.submitted = true;
|
||||||
|
|
||||||
|
if ($scope.tokenForm.$invalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$scope.error = null;
|
$scope.error = null;
|
||||||
$scope.onSubmit(token)
|
$scope.onSubmit(token)
|
||||||
.error(function (response, code) {
|
.error(function (response, code) {
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
../../../shared/validation/validator.js
|
|
|
@ -40,3 +40,7 @@ a[target="_blank"]:after {
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
margin-left: 0.1em;
|
margin-left: 0.1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.has-error .help-block {
|
||||||
|
color: $text-color;
|
||||||
|
}
|
||||||
|
|
|
@ -1,31 +1,31 @@
|
||||||
<form method="post" role="form" ng-submit="onSubmit(node)" ng-disabled="hasAnyError()" novalidate>
|
<form name="nodeForm" method="post" role="form" ng-submit="onSubmit(node)" novalidate>
|
||||||
<div class="main-error" ng-if="error">{{error}}</div>
|
<div class="main-error" ng-if="error">{{error}}</div>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<div class="node-data">
|
<div class="node-data">
|
||||||
<h3>Knotendaten</h3>
|
<h3>Knotendaten</h3>
|
||||||
<div class="hostname">
|
<div class="hostname" ng-class="{'has-error' : hasError('hostname')}">
|
||||||
<label for="hostname">Knotenname</label>
|
<label for="hostname">Knotenname</label>
|
||||||
<f-help text="Das ist der Name, der auch auf der Karte auftaucht."></f-help>
|
<f-help text="Das ist der Name, der auch auf der Karte auftaucht."></f-help>
|
||||||
<input type="text" id="hostname" placeholder="z. B. Lisas-Freifunk" ng-model="node.hostname" />
|
<input type="text" id="hostname" name="hostname" placeholder="z. B. Lisas-Freifunk" ng-model="node.hostname" ng-pattern="constraints.hostname.regex" ng-required="!constraints.hostname.optional" />
|
||||||
<span class="feedback" ng-if="hasError('hostname')">
|
<span class="feedback" ng-if="hasError('hostname')">
|
||||||
Knotennamen dürfen maximal 32 Zeichen lang sein und nur Klein- und Großbuchstaben, sowie Ziffern, - und _ enthalten.
|
Knotennamen dürfen maximal 32 Zeichen lang sein und nur Klein- und Großbuchstaben, sowie Ziffern, - und _ enthalten.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="key">
|
<div class="key" ng-class="{'has-error' : hasError('key')}">
|
||||||
<label for="key">VPN-Schlüssel (bitte nur weglassen, wenn Du weisst, was Du tust)</label>
|
<label for="key">VPN-Schlüssel (bitte nur weglassen, wenn Du weisst, was Du tust)</label>
|
||||||
<f-help text="Dieser Schlüssel wird verwendet, um die Verbindung Deines Routers zu den Gateway-Servern abzusichern."></f-help>
|
<f-help text="Dieser Schlüssel wird verwendet, um die Verbindung Deines Routers zu den Gateway-Servern abzusichern."></f-help>
|
||||||
<input type="text" id="key" placeholder="Dein 64-stelliger VPN-Schlüssel" ng-model="node.key" />
|
<input type="text" id="key" name="key" placeholder="Dein 64-stelliger VPN-Schlüssel" ng-model="node.key" ng-pattern="constraints.key.regex" ng-required="!constraints.key.optional" />
|
||||||
<span class="feedback" ng-if="hasError('key')">
|
<span class="feedback" ng-if="hasError('key')">
|
||||||
Der angegebene VPN-Schlüssel ist ungültig.
|
Der angegebene VPN-Schlüssel ist ungültig.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="mac">
|
<div class="mac" ng-class="{'has-error' : hasError('mac')}">
|
||||||
<label for="mac">MAC-Adresse</label>
|
<label for="mac">MAC-Adresse</label>
|
||||||
<f-help text="
|
<f-help text="
|
||||||
Die MAC-Adresse (kurz "MAC") steht üblicherweise auf dem Aufkleber auf der Unterseite deines Routers.
|
Die MAC-Adresse (kurz "MAC") steht üblicherweise auf dem Aufkleber auf der Unterseite deines Routers.
|
||||||
Sie wird verwendet, um die Daten Deines Routers auf der Karte korrekt zuzuordnen.
|
Sie wird verwendet, um die Daten Deines Routers auf der Karte korrekt zuzuordnen.
|
||||||
"></f-help>
|
"></f-help>
|
||||||
<input type="text" id="mac" placeholder="z. B. 12:34:56:78:9a:bc oder 123456789abc" ng-model="node.mac" />
|
<input type="text" id="mac" name="mac" placeholder="z. B. 12:34:56:78:9a:bc oder 123456789abc" ng-model="node.mac" ng-pattern="constraints.mac.regex" ng-required="!constraints.mac.optional" />
|
||||||
<span class="feedback" ng-if="hasError('mac')">
|
<span class="feedback" ng-if="hasError('mac')">
|
||||||
Die angegebene MAC-Adresse ist ungültig.
|
Die angegebene MAC-Adresse ist ungültig.
|
||||||
</span>
|
</span>
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
<div class="node-position">
|
<div class="node-position">
|
||||||
<h3>Wo soll Dein Router stehen?</h3>
|
<h3>Wo soll Dein Router stehen?</h3>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="coords col-md-4">
|
<div class="coords col-md-4" ng-class="{'has-error' : hasError('coords')}">
|
||||||
<p class="help-block">
|
<p class="help-block">
|
||||||
Wenn Du möchtest, dass Dein Knoten an der richtigen Stelle auf der
|
Wenn Du möchtest, dass Dein Knoten an der richtigen Stelle auf der
|
||||||
<a href="{{ config.map.mapUrl }}" target="_blank">Knotenkarte</a> angezeigt wird,
|
<a href="{{ config.map.mapUrl }}" target="_blank">Knotenkarte</a> angezeigt wird,
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
an die Stelle, wo Dein Knoten erscheinen soll. Durch erneutes Klicken kannst Du die Position jederzeit
|
an die Stelle, wo Dein Knoten erscheinen soll. Durch erneutes Klicken kannst Du die Position jederzeit
|
||||||
anpassen.
|
anpassen.
|
||||||
</p>
|
</p>
|
||||||
<input type="text" id="coords" class="{{node.coords ? 'has-coords' : ''}}" placeholder="z. B. {{config.coordsSelector.lat}} {{config.coordsSelector.lng}}" ng-model="node.coords" ng-blur="updateMap" />
|
<input type="text" id="coords" name="coords" class="{{node.coords ? 'has-coords' : ''}}" placeholder="z. B. {{config.coordsSelector.lat}} {{config.coordsSelector.lng}}" ng-model="node.coords" ng-pattern="constraints.coords.regex" ng-required="!constraints.coords.optional" ng-blur="updateMap" />
|
||||||
<i class="reset-coords" ng-if="node.coords" ng-click="resetCoords()"></i>
|
<i class="reset-coords" ng-if="node.coords" ng-click="resetCoords()"></i>
|
||||||
<span class="feedback" ng-if="hasError('coords')">
|
<span class="feedback" ng-if="hasError('coords')">
|
||||||
Bitte gib die Koordinaten wie folgt an, Beispiel: {{config.coordsSelector.lat}} {{config.coordsSelector.lng}}
|
Bitte gib die Koordinaten wie folgt an, Beispiel: {{config.coordsSelector.lat}} {{config.coordsSelector.lng}}
|
||||||
|
@ -63,18 +63,18 @@
|
||||||
</p>
|
</p>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="nickname">
|
<div class="nickname" ng-class="{'has-error' : hasError('nickname')}">
|
||||||
<label for="nickname">Nickname / Name</label>
|
<label for="nickname">Nickname / Name</label>
|
||||||
<input type="text" id="nickname" placeholder="z. B. Lisa" ng-model="node.nickname" />
|
<input type="text" id="nickname" name="nickname" placeholder="z. B. Lisa" ng-model="node.nickname" ng-pattern="constraints.nickname.regex" ng-required="!constraints.nickname.optional" />
|
||||||
<span class="feedback" ng-if="hasError('nickname')">
|
<span class="feedback" ng-if="hasError('nickname')">
|
||||||
Nicknames dürfen maximal 64 Zeichen lang sein und nur Klein- und Großbuchstaben, sowie Ziffern, - und _ enthalten. Umlaute sind erlaubt.
|
Nicknames dürfen maximal 64 Zeichen lang sein und nur Klein- und Großbuchstaben, sowie Ziffern, - und _ enthalten. Umlaute sind erlaubt.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="email">
|
<div class="email" ng-class="{'has-error' : hasError('email')}">
|
||||||
<label for="email">E-Mail-Adresse</label>
|
<label for="email">E-Mail-Adresse</label>
|
||||||
<input type="email" id="email" placeholder="z. B. lisa@{{config.community.domain}}" ng-model="node.email" />
|
<input type="email" id="email" name="email" placeholder="z. B. lisa@{{config.community.domain}}" ng-model="node.email" ng-pattern="constraints.email.regex" ng-required="!constraints.email.optional" />
|
||||||
<span class="feedback" ng-if="hasError('email')">
|
<span class="feedback" ng-if="hasError('email')">
|
||||||
Die angegebene E-Mail-Adresse ist ungültig.
|
Die angegebene E-Mail-Adresse ist ungültig.
|
||||||
</span>
|
</span>
|
||||||
|
@ -83,7 +83,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<button class="save {{action}}" type="submit" ng-disabled="hasAnyError()" popover="I appeared on mouse enter!" popover-trigger="mouseenter" ng-switch="action">
|
<button class="save {{action}}" type="submit" ng-switch="action">
|
||||||
<span ng-switch-when="create"><i class="fa fa-dot-circle-o"></i> Knoten anmelden</span>
|
<span ng-switch-when="create"><i class="fa fa-dot-circle-o"></i> Knoten anmelden</span>
|
||||||
<span ng-switch-when="update"><i class="fa fa-pencil"></i> Daten ändern</span>
|
<span ng-switch-when="update"><i class="fa fa-pencil"></i> Daten ändern</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
<form method="post" role="form" ng-submit="doSubmit(token)" ng-disabled="hasError()" novalidate>
|
<form name="tokenForm" method="post" role="form" ng-submit="doSubmit(token)" ng-disabled="hasError()" novalidate>
|
||||||
<div class="main-error" ng-if="error">{{error}}</div>
|
<div class="main-error" ng-if="error">{{error}}</div>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<div class="token">
|
<div class="token">
|
||||||
<label for="token">Token</label>
|
<label for="token">Token</label>
|
||||||
<input type="text" id="token" placeholder="Dein 16-stelliger Token" ng-model="token" />
|
<input type="text" name="token" id="token" placeholder="Dein 16-stelliger Token" ng-model="token" ng-pattern="constraints.token.regex" ng-required="!constraints.token.optional" />
|
||||||
<span class="feedback" ng-if="hasError()">
|
<span class="feedback" ng-if="tokenForm.token.$invalid && submitted">
|
||||||
Das Token ist ein 16-stelliger Wert bestehend aus 0-9 und a-f.
|
Das Token ist ein 16-stelliger Wert bestehend aus 0-9 und a-f.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<button class="submit" type="submit" ng-disabled="!token || hasError()">
|
<button class="submit" type="submit">
|
||||||
<i class="fa fa-pencil"></i> Knotendaten ändern
|
<i class="fa fa-pencil"></i> Knotendaten ändern
|
||||||
</button>
|
</button>
|
||||||
<button class="cancel" type="reset" ng-click="onCancel()">
|
<button class="cancel" type="reset" ng-click="onCancel()">
|
||||||
|
|
|
@ -20,7 +20,7 @@ require('./resources/nodeResource');
|
||||||
require('./services/nodeService');
|
require('./services/nodeService');
|
||||||
|
|
||||||
require('../shared/validation/constraints');
|
require('../shared/validation/constraints');
|
||||||
require('../shared/validation/validator');
|
require('./validation/validator');
|
||||||
|
|
||||||
angular.injector(['ffffng']).invoke(function (config, app, Router) {
|
angular.injector(['ffffng']).invoke(function (config, app, Router) {
|
||||||
Router.init();
|
Router.init();
|
||||||
|
|
Loading…
Reference in a new issue