I have an Angular2.0.0-beta.9 and Typescript 1.7 app. In this app I am creating a custom validator that uses more than one supplied parameter to determine if a control is valid.
I am using this answer as inspiration
Here is the constructor of the class for my form:
constructor(fBuilder: FormBuilder) {
// initialize shift custom validators
this._sVal = new ShiftValidator; // a custom validator class I import
// build form controls
this.shiftForm = fBuilder.group({
'TradeDate': ['2016-03-23', Validators.required],
// other fields removed for brevity
'SupervisorRankID': [0, (c: Control) => this._sVal.valSelectOptional(c, this.requireSupervisor())]
});
}
Here is the validator class:
export class ShiftValidator {
// other validations removed for brevity
valSelectOptional(c: Control, required: boolean): { [s: string]: boolean } {
if (Number(c.value) < 1 && required) {
return { 'invalidSelection': true };
}
}
}
Here is the method / function I use to return the boolean value for the validator's second parameter:
requireSupervisor(): boolean {
if (this.currentTrade === undefined) { return false; }
\\ NOTE: currentTrade is a custom class / object that is imported into this module
if (this.currentTrade !== undefined) {
return this.currentTrade.SupervisorApproval;
} else {
return false;
}
}
The Problem
This validator is only "firing" when the booelan value I pass is changed to true. When I change the value of requireSupervisor to be false, the validator does not trigger.
Question
Can someone help me figure out why the validator does not trigger every time the value of its parameters change?
EDIT 1:
I tried gunter's approach by adding a (ngModelChange)='requireSupervisor' to the check box and changing the requireSupervisor function to include a updateValueAndValidate on the whole control group:
requireSupervisor(): boolean {
if (this.currentTrade === undefined) {
this.validateSupervisor();
return false;
}
if (this.currentTrade !== undefined) {
this.validateSupervisor();
return this.currentTrade.SupervisorApproval;
} else {
this.validateSupervisor();
return false;
}
}
validateSupervisor(): void {
if (this.shiftForm !== undefined) {
this.shiftForm.updateValueAndValidity();
}
}
If I change the above validateSupervisor function to the following I get a maximum call stack exceeded error:
validateSupervisor(): void {
if (this.shiftForm !== undefined) {
this.shiftForm.controls['SupervisorRankID'].updateValueAndValidity();
}
}
PROBLEM:
The validator logic works, the problem is the validator logic is only triggered when the checkbox is clicked/selected. When you uncheck/deselect the check box the validator is not triggered.
Can someone help me figure out why the uncheck action of the check box does not fire the validator?
It seems that the only option is:
control.updateValueAndValidity()
though its trigger should be added carefully to avoid undesired change detection.
plunker
In this example switching required works fine, but if you try to do it directly from template required!=required it will fail.
AFAIK this is the expected behavior of a ControlGroup Validator, validation is fired for the altered controls only.
But you can manipulate this behavior using updateValueAndValidity as suggested by Gunter and kemsky, It just needs a little fine tuning
(<Control>this.shiftForm.controls['SupervisorRankID'])
.updateValueAndValidity({onlySelf: true, emitEvent: true})
// do this whenever requirement changes
Note: don't put this inside requireSupervisor() or validateSupervisor(), because that will cause recursion.
If onlySelf is true, this change will only affect the validation of this Control and not its parent component.
If emitEvent is true, this change will cause a valueChanges event on the Control to be emitted.
Both of these options default to false.
This should invoke validation:
this.shiftForm.controls['SupervisorRankID'].updateValueAndValidity();
might need a cast
(<Control>this.shiftForm.controls['SupervisorRankID'])
.updateValueAndValidity();
One thing you can try is to set the emitEvent on false for that formControl updateValueAndValidity method.
for example something like this:
this.shiftForm.controls['SupervisorRankID'].updateValueAndValidity({emitEvent : false});
from Angular docs:
If emitEvent is true, this change will cause a valueChanges event on
the FormControl to be emitted. This defaults to true (as it falls
through to updateValueAndValidity).
Also you have the possibility to onlySelf to false (is true by default) like this:
this.shiftForm.controls['SupervisorRankID'].updateValueAndValidity({onlySelf : true});
from Angular docs:
If onlySelf is true, this change will only affect the validation of
this FormControl and not its parent component. This defaults to false.
I was having a similar problem with a tricky and big form. emitEvent saves me.
Hope it helps you.
Learn more about formControls here: https://angular.io/docs/ts/latest/api/forms/index/FormControl-class.html
Related
Here is a link to my JS fiddle: https://jsfiddle.net/apasric4/v1qkmgyu/1/
function inputCheck(input) {
if (input.name==="email") {
console.log("email")
return isValidEmail
} else if (input.name==="password") {
return isValidPassword
console.log("pass")
} else if (input.name==="userName") {
return isValidUserName
console.log("user")
}
}
function isValidEmail (email) {
return /^[^#]+[#][^#.]+\.[a-z]+$/.test(email)
}
function isValidPassword(pass) {
return /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/.test(pass)
}
function isValidUserName(user) {
return /^[a-zA-Z0-9]+([_ -]?[a-zA-Z0-9])*$/.test(user)
}
function validation(e) {
e.preventDefault()
inputs.forEach(input=> createListener(inputCheck(input)))
}
function createListener(validator) {
return (e)=> {
const inputValue=e.target.value;
const valid=validator(inputValue)
console.log(valid)
}
}
I'm trying to create form validation using closures. I am trying to make my code as efficient as possible.
I want to loop over each input element (without selecting each individually), and apply an event listener to each one. The inputCheck function would return a validator function depending on the name attribute of each input, and the createListener function takes the value returned by inputCheck, which would be a specific type of validator, and then for testing purposes, console.log true or false.
So far, the only if branch that works in the inputCheck function is the first one associated with name attribute email. The other if branches won't work if I type values into other input elements and submit the form.
Can anyone tell me where I'm going wrong and how to improve my code?
I'm new to closures so I understand that this issue might seem relatively simple to most of you.
I can observe two things:
First, just like #VLAZ pointed out, two console.log in inputCheck are actually not executed since they are placed after return.
Second, createListener and validation are not quite right. createListener returns a function with one argument. validation forEach doesn't log anything because createListener returns a function, no function execution here.
There is another problem with the argument e of createListener. It seems like you treat it as an event, but based on your implementation, there is only one event, that is form submit event. So, I'd suggest to modify these two functions a little bit:
function validation(e) {
e.preventDefault()
inputs.forEach(input=> createListener(inputCheck(input))(input))
}
function createListener(validator) {
return (e)=> {
const inputValue=e.value;
const valid=validator(inputValue)
console.log(valid)
}
}
Then, the console prints out true or false based on the input value of each input field.
Please check whether the output is your intension or not https://jsfiddle.net/jqgbefhw/
Version ExtJs - 6.2.1
Considering the sample code specified below, i'm curious to know if there is a better approach for implementing where i can handle some checks.
Ext.define('MainApp.view.main.MainController', {
extend: 'Ext.app.ViewController',
...
listen: {
controller: {
// listen to some components events
'componentController':{
'event1': 'onEvent1',
'event2': 'onEvent2'
}
}
},
onEvent1: function(){
// can i avoid this and do something better ??
this.commonEventHandlingChecks();
// event 1 handling logic
},
onEvent2: function(){
// can i avoid this and do something better ??
this.commonEventHandlingChecks();
// event 2 handling logic
},
commonEventHandlingChecks: function(){
// some logic to do some custom validations
}
});
Instead of calling the method "commonEventHandlingChecks" on each and every listener i have in my controller, is there a better way to do all the common event handling checks. Probably by overriding some methods in the controller or Ext.util.Event
Ext.Mixin does have a mixinConfig before API that can add a function on the mixin and execute it. If that returns false then it won't execute the function is was put before. This is documented in the class description here (link to the 6.2.1 version since you said you were using it).
That would work except the mixin would have to know what methods on the class it's being mixed into need to be protected. This wouldn't scale very well if you were wanting to use a mixin in different classes. For this, I would do something a little more advanced but keep the same functionality as the before API gives you. This mixin would look like:
Ext.define('MyAuthMixin', {
extend: 'Ext.Mixin',
onClassMixedIn: function (targetClass) {
const proto = targetClass.prototype
const config = proto.config
const protectedMethods = config.protectedMethods || proto.protectedMethods
// change this method name if you want something else
const checkAuth = this.prototype.checkAuth
if (protectedMethods) {
Ext.Object.each(protectedMethods, function (key, value) {
if (value && proto[ key ]) {
targetClass.addMember(key, function () {
// execute the checkAuth methods
// change this variable to change the method name
if (checkAuth.apply(this, arguments) !== false) {
return this.callParent(arguments);
}
});
}
});
}
},
checkAuth: function () {
// return false to stop calling
return !!MyApp.$user
}
})
Don't be scared by that onClassMixedIn function. Basically it's putting the checkAuth method before the method it's being told to protect and if you return false in the checkAuth then it will not execute that protected method.
For an example of how to use it and see it in action, I have created this fiddle. The implementation in classes would be this part:
mixins: [
'MyAuthMixin'
],
config: {
// put in a config object so subclass and superclass merging
// which is also why it's an object as a subclass can disable
// a protected method
protectedMethods: {
'onEvent1': true,
'onEvent2': true
}
},
To not protect a method, you can leave it out or set it to false. Reason for setting to false would simply be a subclass could disable the check if it extends a class that has it turned on. This mixin will work for any class, not just a controller. It can be a component or singleton or store, any.
Hi I have question related with refreshing output of function which is used as binded to an attribute.
Lets say I have part of html that I want to hide in specific cases:
<div hidden$="[[hideElement()]]">
function is defined is Polymer object:
hideElement: function () {
if (this.deviceId == undefined) {
return false;
}
else if (typeof this.deviceId === 'string' || this.deviceId instanceof String) {
return true;
}
else {
return false;
}
},
What I see is that element is not hiding after output of this function is changing.
I probably miss something but I am not sure what.
This is answered in the Polymer Data binding documentation.
The computed binding declaration includes a computing function name,
followed by a list of dependencies, in parenthesis.
Your issue is that the computed function is called once but it has no way of knowing in the future when the function value will change since you provide it no arguments. You need to include a property as a dependency so it knows when your function value may change.
For example:
<div hidden$="[[hideElement(deviceId)]]"></div>
Thus you need to declare a property
Polymer({
is: 'my-element',
properties: {
deviceId: String
},
hideElement: function(deviceId) {
return deviceId ? true : false;
}
})
If you look at other polymer elements they typically call the function _isHidden as opposed to hideElement to indicate that it will take a True/False value.
Since we have declared deviceId as a string this will significantly simplify your hideElement function. "", null, and undefined all evaluate as false.
The problem is hidden property does not call hideElement again, so value of hidden never changes.
You should bind hidden to a boolean and then change the value of that boolean. This way whenever the boolean property will change value of hidden also change
<div hidden$="[[hideElement]]">
and javascript will be something like
properties:{
hideElement:{
type:Boolean
value:false
},
},
hideValue:function(){
if (this.deviceId == undefined) {
this.hideElement= false;
return;
}
else if (typeof this.deviceId === 'string' || this.deviceId instanceof String) {
this.hideElement=true;
return;
}
else {
this.hideElement=false;
return;
}
}
The Code:
my-view-model.html
<input on-scan.bind="onAirbillScanned" value.bind="airbill"/>
on-scan.ts
attached() {
var scannerOptions = { onComplete: this.onScan.bind(this), preventDefault: true };
(<any>$(this.element)).scannerDetection(scannerOptions);
-- Code to add a signal to the value binding.
}
onScan(scannedValue, data) {
if (typeof (this.value) == 'function') {
let updatedScanValue = this.value(scannedValue);
if (updatedScanValue !== undefined)
this.element.value = updatedScanValue;
else
this.element.value = scannedValue;
-- Code to Call the Signal
}
}
The Problem:
I have a custom attribute that allows me to detect a scan, modify the scanned-in data and set it to be the value of the input element.
However, I need to update aurelia with the updated value.
I can just fire off an 'input' event to make this happen. But I have found side effects when random 'input' events are fired.
I would rather use the signal system outlined here: http://aurelia.io/docs.html#/aurelia/binding/1.0.0-beta.1.1.3/doc/article/binding-binding-behaviors
But the problem is that I need the signal to be on the value.bind binding.
The Question:
Is there a way (using my access to the element that the binding is on) to update the value.binding to have a signal that I can call to get the binding to update?
Basically I am looking for something like this:
addSignal(element, property, signal) {...}
..
addSignal(this.element, 'value', 'scanFinished');
And it will update the input's value binding to look like this:
<input on-scan.bind="onAirbillScanned" value.bind="airbill & signal: 'scanFinished'"/>
But more than just re-writing the html, it would have to update Aurelia to know about the signal.
Or is there a signal value that Aurelia adds to all bindings for scenarios like this?
NOTE: It would be awesome if all aurelia bindings had a AureliaBinding signal defined so you could target that control and send an event that will have no side effects other than to update the binding.
I think you're having trouble because you're at the tipping point where it's time to switch from a custom attribute to an approach that uses a custom element.
You can circumvent the whole issue by doing something like this:
scanner.html
<template>
<input ref="input" value.bind="value">
</template>
scanner.js
import {bindingMode} from 'aurelia-framework';
export class Scanner {
#bindable({ defaultBindingMode: bindingMode.twoWay }) value;
#bindable scanModifier = x => x;
input: HTMLInputElement;
attached() {
let scannerOptions = {
onComplete: value => this.value = this.scanModifier(value),
preventDefault: true
};
(<any>$(this.input)).scannerDetection(scannerOptions);
}
detached() {
(<any>$(this.input)).scannerDetection('destroy');
}
}
usage:
<require from="./scanner"></require>
<scanner value.bind="airbill" scan-modifier.call="onAirbillScanned($event)"></scanner>
This could still be done with a custom attribute but it seems more natural to me this way. What do you think?
I am making a condition that verifies some class and depending on the value, the respectable submit input is stored into a variable:
_btnAjax = "";
if (_aVar.hasClass("one")) {
_btnAjax = $("#one");
}
if (_aVar.hasClass("two")) {
_btnAjax = $("#two");
}
and then, using the .on('click' function(e){}); on that variable:
_btnAjax.on('click', function(e) {
// some Ajax
}
The problem is that I receive the error TypeError: _btnAjax.on is not a function
I already made exactly the same thing on a <li></li>, but either <button></button> or <input type='submit'/> don't work.
The reason that fails is because neither of your two conditions are true.
For example, if _aVar does not have a class of one AND it does not have a class of two then _btnAjax is a string in your code.
Double check that your UI has the right classes.
In addition, make sure you handle the other case.
Try writing your code more like this:
var _btnAjax;
if (_aVar.hasClass("one")) {
_btnAjax = $("#one");
} else if (_aVar.hasClass("two")) {
_btnAjax = $("#two");
} else {
// Do something to handle the fact that neither case was true.
// You can return early, throw an error, or set _btnAjax to
// an empty jQuery object.
}
You're trying to use a jQuery function (.on)
Try this:
$(_btnAjax).on('click', function(e) {
// some ajax
}
Although I think you should use a id or class selector like:
// selector should be whatever your button has as id or class property.
// (preferably an id since this won't conflict with other classes)
$('#btnAjax').on('click', function(e) {
// some ajax
}