I'm trying to implement a validation in which i want to disable the button if a specific value entered by user matches the value returned from a service, below is my code:
In the component, i call the service which returns the usernames like below, here is the console log for (UserNames):
0:{Name: "John", userId: "23432"}
1:{Name: "Smith", userId: "12323"}
2:{Name: "Alan", userId: "5223"}
3:{Name: "Jenny", userId: "124"}
in the template, i use NgFor to iterate over the usernames like below
<div *ngFor="let id of UserNames let i = index;">
<input type="radio" name="radio" [checked]="UserNames.userid" (click)="Submit(UserInput)"> <span>Submit</span>
</div>
What i want to achieve is if i enter 23432 the button should disabled because the service already returns userId with this value, unless a new user id entered the button should be enabled.
So the general case of disabling a submit button in the way you're describing would look like:
<button type="submit" [disabled]="someValidationFn()" ...>
and someValidationFn() would, according to the use case you described, contain something like
return UserNames.find(name => { return name.userId === userInput;}));
where userInput is a separate property in the component bound to some user-entered value, presumably via an open text input like
<input name="userInput" [(ngModel)]="userInput" type="text" placeholder="Enter some user id">
But, from the markup snippet you pasted*, I'm not clear that you have that "text" input separate from the radio button group. If the radio button group is meant to have submit actions attached to its individual buttons (it shouldn't), then you're actually guaranteed that the user selection will contain a userId which exists in your UserNames array: the only inputs you're offering are based on the data which came from your service in the first place.
Based on the use case you're describing, I'm not sure why you'd have the radio button group. It sounds like you would just want that text input field with a validation method to make sure that user input does not already exist in the UserNames.
Because I wrote a bunch of abstract snippets there, I thought it might be helpful to show some basic html and js where I put it all together:
// html
<form submit="someSubmitAction()">
<input name="userInput" [(ngModel)]="userInput" type="text" placeholder="Enter some user id">
<button type="submit" [disabled]="someValidationFn()">Submit</button>
</form>
// js
/* don't forget your #Component annotation and class declaration -- I'm assuming these exist and are correct. The class definition encloses all the following logic. */
public userInput: string;
public UserNames: any[];
/* then some service method which grabs the existing UserNames on component's construction or initialization and stores them in UserNames */
public someValidationFn() {
return UserNames.find(name => { return name.userId === userInput;}));
}
public someSubmitAction() {
/* presumably some other service method which POSTs the data */
}
*speaking of the snippet you pasted, there are a couple of errors there:
*ngFor="let id of UserNames <-- you won't get an id by referencing into the UserNames array here; you'll get a member of the UserNames array in each iteration -- i.e., you'd get {Name: "John", userId: "23432"}, then {Name: "Smith", userId: "12323"}, and so on. That's not necessarily an error, but I'm assuming that, b/c you used id as your variable name, you were expecting just the userId field. Otherwise you'd have to use {{id.userId}} in each iteration to access the actual id.
And bob.mazzo mentions another issue with the use of the [checked] attribute
Related
Learning puppeteer... thought Instagram would be a good starting point.
Trying to see if my account in Instagram is configured to be a "Private Account" under Privacy and Security. My confusion is that the HTML for checked and unchecked is just about the same:
Checked:
<div class="qlmO5"><div class="bGWmh" id="accountPrivacy"><label class="U17kh PLphk" for="f2ef028392b78b8"><input class="tlZCJ" id="f2ef028392b78b8" type="checkbox" value="" checked=""><div class="mwD2G"></div>Private Account</label></div></div>
Unchecked:
<div class="qlmO5"><div class="bGWmh" id="accountPrivacy"><label class="U17kh PLphk" for="f111b1f63405c24"><input class="tlZCJ" id="f111b1f63405c24" type="checkbox" value="" checked=""><div class="mwD2G"></div>Private Account</label></div></div>
I thought it might be the <input>'s id value but that does not change based on the value of the checkbox. I also tried diffing the entire html of checked vs unchecked and they were identical.
In this case, am I supposed to take a screenshot and see if I can match an image of "☑ Private Account"? I feel that this may be possible, but not necessary.
HTML is indeed identical in both cases, but the DOM tree isn't. When private account "checkbox" is checked, the .mwD2G div gets a :before element which does the styling, so you can check for it in page.evaluate:
window.getComputedStyle(document.querySelector(".mwD2G"), ":before").getPropertyValue("content")
So if the box is checked, the result of the query is " ", if unchecked, none.
const privateProfile = await page.evaluate(() =>
window.getComputedStyle(document.querySelector(".mwD2G"), ":before")
.getPropertyValue("content") === "none" ? false : true)
My current input looks like this
<input type="email" name="email"
ng-pattern="emailRegex"
ng-change="emailChanged()"
required />
My ng-pattern="ctrl.emailRegex" validates if an email is valid or not
/^[a-zA-Z0-9._%+-]+#[a-zA-Z0-9.-]+\.[a-zA-Z]{1,63}$/;
But I would like to block info#, admin#, help#, sales# emails, so I changed the regex to
/^(?!(?:info|admin|help|sales)#)[a-zA-Z0-9._%+-]+#[a-zA-Z0-9.-]+\.[a-zA-Z]{1,63}$/
So far so good, but I would like to show
Invalid email
to "invalid#!!!!!.com!"
and
info#, admin#, help#, sales# emails are not allowed
to info#test.com
How can I have 2 ng-pattern in the same input?
Thanks
You can validate only one pattern for an input. And, even if you can sort of do it somehow by using a directive, it would be too dirty a solution. Instead, I would recommend validating the input against regex(es) inside the function of ng-change and use formName.inputName.$setValidity to set custom validity of the input. This lets you have a fallback if one pattern is passed.
So, for example, ctrl.emailChanged could probably have something like this,
ctrl.emailChanged = function() {
var emailPattern = /^[a-zA-Z0-9._%+-]+#[a-zA-Z0-9.-]+\.[a-zA-Z]{1,63}$/;
var customValidateEmail = /^(?!(?:info|admin|help|sales)#)[a-zA-Z0-9._%+-]+#[a-zA-Z0-9.-]+\.[a-zA-Z]{1,63}$/;
if(!emailPattern.test(ctrl.registrationForm.email)) {
// Invalid email
} else if (customValidateEmail.test(ctrl.registrationForm.email)) {
// handle accordingly
}
// rest of the things
...
}
Alternatively, you can move the validation logic to another function and just call it from emailChanged.
I need to access the input field in the below html. The way the page is setup I need to chain using the 'Address Line 1' text and then sending text to the input field. The input field id changes and so doesn't the layout of the fields depending on user preference. I am struggling. If you need some more information feel free to ask I did not want to overload with too much information.
<td class="labelCol requiredInput">
<label for="00N36000000xina"><span class="assistiveText">*</span>Address Line 1</label>
</td>
<td class="dataCol col02">
<div class="requiredInput">
<div class="requiredBlock"></div>
<input id="00N36000000xina" maxlength="255" name="00N36000000xina" size="20" tabindex="4" type="text">
</div>
</td>
I have accessed like this:
element(by.css('div.pbSubsection:nth-child(3) > table:nth-child(1) > tbody:nth-child(1) > tr:nth-child(2) > td:nth-child(2) > input'))
However depending on where the user puts the fields it can move around. So what I was hoping was to be able to access the label\ and use that to pinpoint its input field.
I don't know protractor but I cobbled together some code that hopefully will work or be close and I'll give you the thought process and some info and hopefully you can use it to fix my code, if needed, and solve the problem.
Start by finding an element by XPath, "//label[text()='Address Line 1']". This searches for a LABEL tag that contains "Address Line 1". Once you find that element, get the label attribute. From your HTML, this label is the id for the INPUT element you want. Now use the id to find the element and do with it what you want.
id = element(by.xpath("//label[text()='Address Line 1']")).getAttribute("label")
input = element(by.id(id))
input.sendkeys("some text")
Haven't tested this myself, but you could try something like this:
// $ is shorthand for element(by.css())
$('div.assistiveText').getAttribute('for').then(function (val) {
// locate the <input> by the value of the attribute on <label>
element(by.id(val)).sendKeys('abc'); // replace sendKeys with your intended function
});
Or if that first locator on the label isn't specific enough, swap out $('div.assistiveText') for element(by.cssContainingText('Address Line 1'))
I tried it for other attributes (I don't have a for attribute anywhere in my app) and it seemed to work for me.
Try this:
List<WebElement> elementList = driver.findElements(By.cssSelector("tbody > tr"));
for (WebElement element : elementList) {
if(element.findElement(By.cssSelector("td.labelCol > label")).getText().equalsIgnoreCase("Address Line 1")) {
element.findElement(By.cssSelector("input[type='text']")).sendKeys("textToInput");
}
}
I'm trying to create my own validation for password confirm, and putting my error on $error. this is my code:
html:
<input ng-model="user.password2" type="password" name="password2" required
ng-keyup="confirmPassword(user.password, user.password2)">
<div ng-messages="register.password2.$error" ng-if="register.password2.$dirty">
<div ng-message="required">Password is required</div>
<div ng-message="passwordsDontMatch">Passwords don't match</div>
</div>
JS:
$scope.confirmPassword = function (pass1, pass2) {
if (angular.isUndefined(pass1) || angular.isUndefined(pass2) || pass1.trim() != pass2.trim()) {
$scope.register.password2.$error["passwordsDontMatch"] = true;
} else {
delete $scope.register.password2.$error["passwordsDontMatch"];
}
console.log($scope.register.password2.$error);
};
it looks like it's working. when the passwords are the same, the message is not displayed and indeed the $error object is empty. But the input is still invalid: ($scope.register.password2.$invalid == true)
you can see what I'm talking about in this plunkr: http://plnkr.co/edit/ETuVqsdSaEBWARvlt4RR?p=preview
try 2 identical passwords. the message will disappear but when you blur from the input, it's still red because internally it's $invalid
The problem probably comes from the fact that you're not typing a password in the first field that matches your regex pattern. The first password is thus undefined, since it doesn't respect the ng-pattern validation rule.
That said, you shouldn't modify the $error array directly. Instead, you should set the validity of the field using $setValidity(). That will not only set and remove the error automatically, but also deal with the $invalid/$valid properties, add and remove the CSS classes, etc.
var valid = !((angular.isUndefined(pass1) || angular.isUndefined(pass2) || pass1.trim() != pass2.trim()));
$scope.register.password2.$setValidity("passwordsDontMatch", valid);
Here's a working example. But remember to enter a valid password in the first place.
Also, instead of implementing this check with ng-keyup, you should make it a directive, which would add a validator to the validators of the form input. This would make sure the check is made whatever the way the second password is entered (i.e. via copy/paste using the mouse only, or simply by prepopulating the form programmatically.
Just curious if this is something that is currently possible in Angular, or if I have to write my own directive.
I'm attempting to dynamically create input tags for each property on any object. To accomplish this, I am inspecting the object in javascript and creating an array that contains the objects property names mapped to the corresponding input "type".
Ex.
var fields = []
for (var name in myObject) {
if (!myObject.hasOwnProperty(name)) continue;
fields.push({ 'name': name, 'inputType': typeToInputType(myObject[name]) });
}
$scope.fields = fields;
Then in my markup I go ahead and do the following:
<label for="{{f.name}}" ng-repeat="f in fields">
{{f.name}}: <input name="{{f.name}}" ng-model="myObject.{{f.name}}" type="{{f.inputType}}" />
</label>
Everything works great except when we get to this attribute:
ng-model="myObject.{{f.name}}"
That is causing serious problems. The idea behind it is that I have a
$scope.myObject
and when people type stuff into the inputs, for example the "Name" input, then it should be equivalent to:
$scope.myObject.Name = "I typed stuff";