How to get reactive validation with array group? - forms

I have set code this way
errorGroup: any = FormGroup;
this.errorGroup = this.formBuilder.group({
errors: this.formBuilder.array([])
});
For repeat/add new data in group I have add this function which works fine.
addErrorGroup() {
return this.formBuilder.group({
error_code: ['',[Validators.required ]]
})
}
Get controls by this way. I think hear I'm missing something.
get f() { return this.errorGroup.controls.errors; }
In HTML
<select formControlName="error_code" name="error_code" (change)="errorCodeChange($event.target.value , i)">
<option *ngFor="..." value={{...}}>{{...}}</option>
</select>
<span *ngIf="f.error_code.errors.required" class="error-msg">This is required field.</span>
I got this error.
ERROR TypeError: Cannot read property 'errors' of undefined

If that error is coming from HTML, it's because your *ngIf condition is trying to read a value from an undefined object.
At the point where the view is rendered, and checked, it's entirely possible that f (incidentally, you should change that variable name to something more descriptive, but 🤷🏻‍♂️) doesn't have any errors populated yet, so will be undefined.
You can do one of two things here, either, you can wrap the whole thing in another *ngIf to ensure the error_code part of f is populate before accessing it:
<span *ngIf="f && f.error_code">
<span *ngIf="f.error_code.errors.required" class="error-msg">This is required field.</span>
</span>
Or, you can use the safe navigation operator:
<span *ngIf="f?.error_code?.errors?.required" class="error-msg">This is required field.</span>
Note the ? after each object key. This bails out when it hits the first null value, but, the app continues to work as it fails gracefully.
You can read more about it here: https://angular.io/guide/template-syntax#the-safe-navigation-operator----and-null-property-paths

How about if you just do below?
<span *ngIf="errorGroup.get('error_code').errors.required" class="error-msg">
This is required field.
</span>
so by doing this way, you don't need the f() getter in your component file.

Related

How do I get the value from a GWT SelectElement?

I am trying to get the value of a SelectElement using the getValue() method of that class. However, when I debug and watch what's happening, the value is always null. I am able to confirm that the SelectElement contains the expected HTML node when debugging and that one of the options contained within has the selected attribute.
Here is the code that finds the select element in the DOM and tries reading the value:
SelectElement e = (SelectElement) DOM.getElementById( "sel-" + transaction.getId().toString() ).cast();
Boolean isAcknowledged = Enums.TransactionType.ACKNOWLEDGED.equals( e.getValue() );
As I said above, calling the e.getValue() method does not return a value but when I watch what is contained in e, I see the expected HTML node with one of the options set as selected.
<select class="form-control" id="sel-88024">
<option value="CONSUMED" selected="">Used</option>
<option value="ACKNOWLEDGED">Received</option>
</select>
But there is never a value in getValue(). Any ideas would be appreciated.
I think the problem is not related to GWT. Could it be that Enums.TransactionType is a real Java-Enum and you have to use Enums.TransactionType.ACKNOWLEDGED.name().equals(e.getValue())?

Dynamic Vue Form from Input-Elements

I'm building a reusable vue-form-component. For more flexibility the basic idea is to NOT have to specify the form information in the vue-data-object beforehand, but to get the data-structure from the dom-input-elements itself.
On instance-creation the "v-model" attributes are read from the input-tags and applied to the instance via vue.set()
This works fairly well: https://jsfiddle.net/seltsam23/hrL3ec3z/9/
One detail is missing though: I need to only query the children-input-fields, and not the site-wide fields in case I'm using more than one form-component at the same time:
created() {
var inputs = document.querySelectorAll('input'); // works, but this returns ALL elements
var inputs = this.$el.querySelectorAll('input'); // Doesn't work because $el is only available after mounted().
...
}
mounted() {
var inputs = this.$el.querySelectorAll('input'); // works, but attribute "v-model" is removed from inputs at this point
...
}
I've tried to create data-path attribute in the created() phase to store the v-model value on the element itself, but after mount all those created attributes disappear.
Any ideas how to achieve this in an elegant way?
You should be aware that the only reason you see v-model attributes at all is that you're using inline-template. They are in the DOM during the created phase because the template has not yet been processed. What I'm saying is that you're trying to do something pretty hacky, and you probably shouldn't.
It's backward to the normal Vue approach of having the data model drive the DOM, but I know that in some cases it is useful to initialize things from the HTML.
How about this approach?
Vue.component('my-form', {
props: ['init'],
data() {
return {
form: {}
}
},
created() {
this.form = this.init;
}
})
new Vue({
el: '#vue',
})
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="vue">
<my-form inline-template :init="{foo: {bar: 'one', baz: 'two'}}">
<form>
<input type="text" v-model="form.foo.bar">
<span v-text="form.foo.bar"></span>
<hr>
<input type="text" v-model="form.foo.baz">
<span v-text="form.foo.baz"></span>
</form>
</my-form>
</div>

Protractor: element is not getting cleared with clear function

I am expecting that the previous value on firstname is removed and then I can write the new value. But it is not removing the name.
Clear() function is not helping here.
var firstname= element(By.model('subject.firstName'));
firstname.clear().then(function() {
firstname.sendKeys('bob');
})
HTML:
<input type="text" ng-model="subject.firstName"
placeholder="First Name" name="firstName" validator="required"
valid-method="submit" message-id="requireFirstName"
ng-maxlength="50" class="ng-pristine ng-pending ng-empty
ng-valid-maxlength ng-touched">
Protractor version: 4.0.11
I generally add click() event before performing clear() or sendKeys(), just to make sure focus is on element. For example:
element(by.model('anyvalue')).click().clear().sendKeys(value);
Make sure you have an element with model anyvalue on your website.
Change
element(By.model...
to
element(by.model...
I believe that you don't have to use then() on clear, even though it returns a promise. So you can check if this will work:
firstname.clear();
firstname.sendKeys('bob');

Play Framework Form Error Handling

This is my view file containing the form that has to filled in by the user:
#helper.form(call) {
#helper.input(resumeForm("surname"), '_label -> "Surname") { (id, name, value, args) =>
<input name="#name" type="text" value="#value" placeholder="Enter your surname">
}
}
This is my custom field constructor:
#(elements: helper.FieldElements)
#if(!elements.args.isDefinedAt('showLabel) || elements.args('showLabel) == true) {
<div class="input-with-label text-left">
<span>#elements.label</span>
#elements.input
</div>
} else {
#elements.input
}
Now I have a dilemma. When the entered value doesn't clear validation, I need to add the class field-error to the input and I need to add data-toggle, data-placement and title. However, I don't know of any way to check if there are errors for the specific field. What is the best way to implement this? I already looked at using inputText or something but that is basically the same as the base input thus also does not have access to any errors. I'm also unable to alter the HTML of the elements.input inside the field constructor.
Have a look at play documentation: Writing your own field constructor.
You can check on errors with #if(elements.hasErrors) within the template of your custom field constructor.
<div class="input-with-label text-left #if(elements.hasErrors){field-error}">
...
Edit:
You can pass the error state of your field via the args parameter to your input. From the play docs:
Note: All extra parameters will be added to the generated HTML, except for ones whose name starts with the _ character. Arguments starting with an underscore are reserved for field constructor argument (which we will see later).
You need to cast to the matching type though.
#input(resumeForm("surname"), '_label -> "Surname", 'hasErrors -> resumeForm("surname").hasErrors) { (id, name, value, args) =>
<input name="#name" type="text" value="#value" placeholder="Enter your surname"
class="#if(args.get('hasErrors).map(_ match { case x:Boolean => x}).get){field-error}">
}

Filtering a scope with multiple select in AngularJS

Here is my fiddle: http://jsfiddle.net/mwrLc/12/
<div ng-controller="MyCtrl">
<select ng-model="searchCountries" ng-options="cc.country for cc in countriesList | orderBy:'country'">
<option value="">Country...</option>
</select>
<select ng-model="searchCities" ng-options="ci.city for ci in citiesList | filter:searchCountries | orderBy:'city'">
<option value="">City...</option>
</select>
<ul>
<li ng-repeat="item in list | filter:searchCountries | filter:searchCities">
<p>{{item.country}}</p>
<p>{{item.city}}</p>
<p>{{item.pop}}</p>
<br>
</li>
</ul>
The first select filters the second one but also the result list.
The second select filters only the result list.
It works great until a country and a city are chosen. Then, when you choose another country, second select got reseted but the scope seems to be stuck with the old value.
i cant find a way to have it works properly... Please help!
The best solution is to reset the city model when a change to country model is detected via $scope.$watch():
function MyCtrl($scope) {
// ...
$scope.$watch('searchCountry', function() {
$scope.searchCity = null;
});
}
Note that I changed your model names to singular form ("searchCountry" and "searchCity") which is more appropriate considering the value of those models is set to a single country or city.
Here is the full working example: http://jsfiddle.net/evictor/mwrLc/13/
Ezekiel Victors solution is excellent. However, I ran into a bit of a dependency injection issue while trying to get this to work when referencing the controller from an ng-include. Thanks to the help from Ben Tesser I was able to resolve the issue. Ive attached a jsfiddle that details the fix:
http://jsfiddle.net/uxtx/AXvaM/
the main difference is that you need to wrap the searchCountry/searchCity as an object and set the default value to null. Ditto for the watch.
$scope.test = {
searchCountry: null,
searchCity: null
};
/// And your watch statement ///
$scope.$watch('test.searchCountry', function () {
$scope.test.searchCity = null;
});
In your template you'll change all references from searchCountry/searchCity to test.searchCountry.
I hope this saves someone some time. It's trixy.