Aurelia Validation - one of two fields must be mandatory - aurelia

<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label class="control-label">SKU</label>
<input disabled.bind="readonly" type="text" class="form-control" value.bind="production.Sku1">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label class="control-label">SKU</label>
<input disabled.bind="readonly" type="text" class="form-control" value.bind="production.Sku2">
</div>
</div>
</div>
I have the textboxes above, either sku1 must be mandatory or sku2. I know how to do this in what seems like everything but Aurelia.
I was hoping it would be something simple like
this.validator = this.validation.on(this)
.ensure('production.StockStatusId').isGreaterThan(0).withMessage('is required')
.ensure('production.Sku1').isNotEmpty().Or.ensure('production.Sku2').isNotEmpty();
I have touched on if statements but unsure what the computedFrom would be
UPDATE
I was hoping this would work, however it isn't. Anyone know why?
.ensure('production.Sku1', (config) => {config.computedFrom(['HasProvidedEitherSku'])})
.passes(() => { return this.HasProvidedEitherSku }).withMessage("(Need to provide a SKU)")
get HasProvidedEitherSku(){
if ((this.production.Sku1 === undefined || this.production.Sku1 === null) && (this.production.Sku2 === undefined || this.production.Sku2 === null)){
return false;
} else {
return true;
}
}
UPDATE
This does work, in a way. However both show the error straight away however the error is only cleared on the one that has become valid. I understand why as the error message is attached to each one sep, however I dont know how to stop this
.ensure('production.Sku1', (config) => {config.computedFrom(['HasProvidedEitherSku'])})
.if(() => { return this.HasProvidedEitherSku })
.isNotEmpty().withMessage('a SKU is required')
.endIf()
.ensure('production.Sku2', (config) => {config.computedFrom(['HasProvidedEitherSku'])})
.if(() => { return this.HasProvidedEitherSku })
.isNotEmpty().withMessage(‘a SKU is required')
.endIf();

Here is what I have used:
HTML
<div class="col-sm-6">
<div class="form-group">
<label class="control-label">SKU</label>
<input disabled.bind="readonly" type="text" class="form-control" value.bind="Sku1">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label class="control-label"> SKU</label>
<input disabled.bind="readonly" type="text" class="form-control" value.bind="Sku2">
</div>
</div>
Validation
.ensure('Sku1', (config) => {config.computedFrom(['Sku1, Sku2'])})
.if(() => { return this.HasProvidedEitherSku1OrSku2 === false })
.isNotEmpty().withMessage(‘at least one sku is required')
.hasLengthBetween(0, 50)
.endIf()
.ensure('Sku2', (config) => {config.computedFrom(['Sku1, Sku2'])})
.if(() => { return this.HasProvidedEitherSku1OrSku2 === false })
.isNotEmpty().withMessage(‘at least one sku is required’)
.hasLengthBetween(0, 50)
.endIf();
Validation Method
get HasProvidedEitherSku1OrSku2 (){
if ((this.Sku1 === undefined || this.Sku1 === null || this.Sku1 === '') && (this.Sku2=== undefined || this.Sku2=== null || this.Sku2=== '')){
return false;
} else {
return true;
}
}

Related

how can I update a vue child-component from the vuex-store

My application shows a window where player can enter name and passcode to enter
When the player exists and has a card, I want to show the card as well.
When the player exists, I make the card visible. Then in 'created'I call fetchSpelerCards. This is successful but shows in the VUE console as pending...
I hope some experienced vue user reads this and can help me with a hint, reference or explanation.
For that I have in the following code:
<h2>Meld je aan</h2>
<form #submit.prevent="register" class="mb-3">
<div class="form-group">
<input type="text" class="form-control m-2" placeholder="naam" v-model="name">
</div>
<div class="form-group">
<input type="text" class="form-control m-2" placeholder="kies inlogcode" v-model="pass_code">
</div>
<button type="submit" #click="checkSpeler()" class="btn btn-primary btn-block" style="color:white">Save</button>
</form>
<p class="alert alert-danger" v-if="errorMessage !== ''"> {{errorMessage}} </p>
<p class="alert alert-success" v-if="successMessage !== ''"> {{successMessage}} </p>
<CardsSpeler v-if="spelerCorrect"></CardsSpeler>
</div>
</template>
The component looks as follows:
<h2>Cards 1</h2>
<form #submit.prevent="addCard" class="mb-3">
<div class="form-group">
<input type="text" class="form-control" placeholder="title" v-model="card.title">
</div>
<div class="form-group">
<textarea class="form-control" placeholder="description" v-model="card.description">
</textarea>
</div>
<div>
<input type="file" v-on:change="onFileChange" ref="fileUpload" id="file_picture_input">
</div>
<button type="submit" class="btn btn-primary btn-block" style="color:white">Save</button>
</form>
<div class="card card-body mb-2" v-for="card in cards" v-bind:key="card.id">
<h3> {{currentSpelerCard.title}} </h3>
<p> {{currentSpelerCard.description}}</p>
<img class="img-circle" style="width:150px" v-bind:src="currentSpelerCard.picture" alt="Card Image">
</div>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default {
mounted(){
console.log('component mounted');
},
computed: {
...mapState([
'currentSpeler' ,'currentSpelerCard'
]),
},
data() {
return{
cardExists:false,
successMessage:'',
errorMessage:'',
}
},
created(){
this.fetchSpelerCards();
},
methods: {
...mapActions([ 'getGames', 'addGame', 'fetchSpelerCards' ]),
fetchSpelerCards(){
this.$store.dispatch('fetchSpelerCards', this.currentSpeler.speler.id )
.then(res => {
this.cardExists = true;
this.successMessage = res;
console.log(res);
})
.catch(err => {
this.errorMessage = err;
this.cardExists = false;
});
},
The corresponding action, in actions.js is:
export const fetchSpelerCards = ({commit}, speler_id) => {
return new Promise((resolve, reject) => {
let status = '';
let data ={};
fetch(`api/cardsBySpeler/${speler_id}`)
.then(res => {
status = res.status;
data = res.json();
})
.then(res=>{
if ( status === 200) {
commit('SET_PLAYER_CARD', data);
resolve('Kaart gevonden');
}
else {
reject('Er is geen kaart beschikbaar')
}
});
})
}
In the vuex-store I see (viewed with VUE add-on of chrome browser):
currentSpelerCard: Promise
Yet, the response of the fetch command was successful, and the card was pulled in, as I see also in the console: status 200, I can see name, title, image address etc..
I was under the assumption that, when the promise eventually resolves, the store is updated and the card becomes available because of the:
computed: { ...mapState([ 'currentSpeler' ,'currentSpelerCard' ]),
Can anyone help me and explain what I am doing wrong?
fetchSpelerCards in Vuex commits SET_PLAYER_CARD with data, this will be a pending promise. You need to await the promise.
You can solve this in a few different ways.
Making the function async and await res.json() would be the easiest.
...
fetch(`api/cardsBySpeler/${speler_id}`)
.then(async res => {
status = res.status;
data = await res.json();
})
...

validate form inputs bootstrap4

I have this code in my vue template
<div class="form-row" :class="{'was-validated': this.checkPassword()}">
<div class="col-6">
<label>Password</label>
<input :type="showPassword ? 'text' : 'password'" class="form-control" ref="password" required v-model="password">
<div class="valid-feedback" v-if="!error">
Password match
</div>
<div class="invalid-feedback" v-else>
Password not match
</div>
</div>
<div class="col-6">
<label>Confirm password</label>
<div class="input-group">
<input :type="showPassword ? 'text' : 'password'" class="form-control" ref="passwordCheck" required v-model="passwordCheck" #change="checkPassword()">
<div class="input-group-append">
<button class="btn btn-secondary" #click.prevent="copyToClipboard()"><i class="fas fa-clipboard"></i></button>
</div>
</div>
</div>
</div>
I want to show like the bootstrap4 documentation a green input field if the password matches or a red one if the password aren't matching. I'm trying by adding the was-validated class to the form-row if the demanded method return true but when the view where the password inputs are rendered the two input fields are always red. How I can fix this to give the correct feedback to the user?
Please always share all the relevant parts of the component otherwise, it's hard to tell where the problem resides. Here, you haven't shared your <script> section. Anyway, I guess this should put you on the right track.
<template>
<div class="form-row" :class="{'was-validated': this.checkPassword()}">
<div class="col-6">
<label>Password</label>
<label>
<input :type="showPassword ? 'text' : 'password'" class="form-control" :class="getPasswordClass()"
ref="password" required v-model="password">
</label>
</div>
<div class="col-6">
<label>Confirm password</label>
<div class="input-group">
<label>
<input :type="showPassword ? 'text' : 'password'" class="form-control" :class="getPasswordClass()"
ref="passwordCheck" required v-model="passwordCheck" #input="checkPassword()">
</label>
<div class="input-group-append">
<button class="btn btn-secondary" #click.prevent="copyToClipboard()"><i
class="fas fa-clipboard"></i></button>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
password: null,
passwordCheck: null,
error: true,
showPassword: true,
}
},
methods: {
checkPassword() {
this.error = this.password !== this.passwordCheck;
},
getPasswordClass() {
if (!this.password || !this.passwordCheck) {
return '';
}
return this.error ? 'is-invalid' : 'is-valid'
},
copyToClipboard() {
//
}
}
}
</script>
So, in my opinion, you should only set the dynamic class (is-valid or is-invalid) when both inputs are provided. In this example I've added that to both password and passwordCheck fields but I think it's enough to just apply it to the passwordCheck because that's the one checked against the password.
If you only want to check after user leaves the field you could adjust the code like this:
In the template remove:
:class="{'was-validated': this.checkPassword()}"
And update:
#blur="checkPassword()
In the data() add:
blurred: false,
In the methods update:
methods: {
checkPassword() {
this.blurred = true;
this.error = this.password !== this.passwordCheck;
},
getPasswordClass() {
if (!this.blurred) {
return '';
}
return this.error ? 'is-invalid' : 'is-valid'
},
...
}

Method Updating Data Twice on Form Submission

I have a component that is a form that I input a name and it updates a value on a field on the backend based on which input the name is. Right now I have two inputs (host and scout) and they work fine if I just fill one input. My problem is, when I fill both inputs, the name on the host input will always get updated twice while the name on the scout field will work just fine. Not sure if I was clear enough.
Here is the code for the component so far
<template>
<div class="add-wave">
<h3>Add Wave</h3>
<div class="row">
<form #click.prevent="addwave()" class="col s12">
<div class="row">
<div class="input-field col s12">
<input type="text" v-model="host" />
<label class="active">Host</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<input type="text" v-model="scout" />
<label class="active">Scout</label>
</div>
</div>
<button type="submit" class="btn">Submit</button>
<router-link to="/member" class="btn grey">Cancel</router-link>
</form>
</div>
</div>
</template>
<script>
import { db, fv } from "../data/firebaseInit";
export default {
data() {
return {
host: null,
scout: null
};
},
methods: {
addwave() {
this.addhost();
db.collection("members")
.where("name", "==", this.scout)
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
doc.ref.update({
scout: fv.increment(1),
total: fv.increment(1)
});
});
});
},
addhost() {
db.collection("members")
.where("name", "==", this.host)
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
doc.ref.update({
host: fv.increment(1),
total: fv.increment(1)
});
});
});
}
}
};
</script>
I'm not sure why is it updating twice only when I fill both input fields.

JSFiddle not recognizing Angular

This JSfiddle does not seem to be recognizing Angular, even though that library has been selected in the JS pane.
http://jsfiddle.net/knot22/rxqh8jtc/12/
Why isn't it working?
Code is posted below, as required by SO; however, it is probably most beneficial to navigate to the fiddle to see all the settings.
Here is the HTML:
<html ng-app="myApp">
<script src="//cdnjs.cloudflare.com/ajax/libs/validate.js/0.12.0/validate.min.js"></script>
<div ng-controller="formulaCtrlr as vm" >
<form name="vm.formContainer.form" autocomplete="off">
<div class="form-group" ng-class="{'has-error': vm.formContainer.form.FatA.$dirty && vm.formContainer.form.FatA.$invalid}">
<label for="FatA" class="col-sm-2 control-label">Fat A</label>
<input name="FatA" type="text" class="form-control col-sm-10 input-sm" ng-required="true" ng-model-options="{updateOn: 'blur'}" ui-validate="{numberCheck: 'vm.numberCheck($value)', fatRangeCheck: 'vm.fatRangeCheck($value)'}" ng-model="vm.formulaInput.Fats[0]" /><span>%</span>
<span class="error" ng-show="vm.formContainer.form.FatA.$dirty && vm.formContainer.form.FatA.$invalid">Invalid entry.</span>
<span class="error" ng-show="vm.formContainer.form.FatA.$dirty && vm.formContainer.form.FatA.$error.numberCheck">{{vm.errorMessages.numberCheck}}</span>
<span class="error" ng-show="vm.formContainer.form.FatA.$dirty && vm.formContainer.form.FatA.$error.fatRangeCheck">{{vm.errorMessages.fatRangeCheck}}</span>
</div>
<div class="form-group" ng-class="{'has-error': vm.formContainer.form.FatB.$dirty && vm.formContainer.form.FatB.$invalid}">
<label for="FatB" class="col-sm-2 control-label">Fat B</label>
<input name="FatB" type="text" class="form-control col-sm-10 input-sm" ng-required="true" ng-model-options="{updateOn: 'blur'}" ui-validate="{numberCheck: 'vm.numberCheck($value)', fatRangeCheck: 'vm.fatRangeCheck($value)'}" ng-model="vm.formulaInput.Fats[1]" /><span>%</span>
<span class="error" ng-show="vm.formContainer.form.FatB.$dirty && vm.formContainer.form.FatB.$invalid">Invalid entry.</span>
<span class="error" ng-show="vm.formContainer.form.FatB.$dirty && vm.formContainer.form.FatB.$error.numberCheck">{{vm.errorMessages.numberCheck}}</span>
<span class="error" ng-show="vm.formContainer.form.FatB.$dirty && vm.formContainer.form.FatB.$error.fatRangeCheck">{{vm.errorMessages.fatRangeCheck}}</span>
</div>
<div class="col-sm-offset-2">
<input type="reset" value="Clear" class="btn btn-default" ng-click="vm.clear()" ng-disabled="vm.formContainer.form.$pristine" />
</div>
</form>
<div>formula input: {{vm.formulaInput}}</div>
</div>
</html>
Here is the JS:
angular.module('myApp', ['ui.validate'])
.controller("formulaCtrlr", ['$scope', function ($scope) {
var vm = this;
vm.formContainer = {
form: {}
}
var originalFormContainer = angular.copy(vm.formContainer); //used for clear function below (to clear form)
vm.formulaInput = {};
vm.formulaInput.Fats = [];
vm.clear = function () {
//vm.formulaInput.Fats = [];
//vm.formContainer.form.$setPristine();
vm.formContainer = angular.copy(originalFormContainer);
}
vm.errorMessages = {
numberCheck: 'Value must be a number.',
fatRangeCheck: 'Number must be between 0 and 100.'
}
vm.numberCheck = function (value) {
var result = !(isNaN(parseFloat(value)));
return result;
//return !(isNaN(parseFloat(value)));
}
vm.fatRangeCheck = function (value) {
var result = (value && value > 0.0 && value < 100.0);
return result;
//return (value && value > 0.0 && value < 100.0);
}
}]);
After adding
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-validate/1.2.3/validate.min.js"></script> to the HTML it worked.

How to add custom validator to reactive forms in Angular5

I have the following passwordvalidator which I don't know how to attach into the html. The way I am invoking it now it's not working loginForm.controls.password.errors.passwordValidator
See below in the actual code.
import { FormControl } from "#angular/forms";
export class PasswordValidator {
static getPasswordValidator() {
return function passwordValidator(control: FormControl): { [s: string]: boolean } {
// Write code here..
if (!control.value.match(/^((?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).{6,})/)) {
return { invalidPassword: true };
}
return null;
}
}
}
Then this is how I am using it in login.ts
ngOnInit() {
this.loginForm = this.fb.group({
username: ['', [Validators.required, Validators.email]],
password: ['',
Validators.compose([
Validators.required,
PasswordValidator.getPasswordValidator()
]
)]
});
}
But can't find out how to add it to the formcontrol in login.html
<mat-form-field class="example-full-width">
<input id="password" formControlName="password" matInput placeholder="Password">
</mat-form-field>
<br>
<div *ngIf="loginForm.controls.password.invalid && (loginForm.controls.password.dirty || loginForm.controls.password.touched)"
class="alert alert-danger">
<div class="error mat-body-2" *ngIf="loginForm.controls.password.errors.required">
You must fill out your password
</div>
<div class="error mat-body-2" *ngIf="loginForm.controls.password.errors.passwordValidator && !loginForm.controls.password.errors.required">
Invalid email password
</div>
You should check if the key invalidPassword exist in errors of that controls or not like that
<mat-form-field class="example-full-width">
<input id="password" formControlName="password" matInput placeholder="Password">
</mat-form-field>
<br>
<div *ngIf="loginForm.controls.password.invalid && (loginForm.controls.password.dirty || loginForm.controls.password.touched)"
class="alert alert-danger">
<div class="error mat-body-2" *ngIf="loginForm.controls.password.errors.required">
You must fill out your password
</div>
<div class="error mat-body-2" *ngIf="loginForm.controls.password.errors.invalidPassword && !loginForm.controls.password.errors.required">
Invalid email password
</div>