How can I select an index from an HTMLCollection in Ionic? - dom

I want to select all .message-wrapper elements that I generate in my page
<div class="message-wrapper" *ngFor="let key of chat.chat_lines_keys" [ngClass]="{
'neutro': !chat.chat_lines[key].user_id,
'me': user.id == chat.chat_lines[key].user_id,
'you': chat.chat_lines[key].user_id && user.id != chat.chat_lines[key].user_id
}">
<div class="message">
<input type="hidden" name="user_id" [value]="chat.chat_lines[key].user_id">
<input type="hidden" name="line_id" [value]="chat.chat_lines[key].id">
<p>{{ chat.chat_lines[key].text }}</p>
</div>
</div>
When I select the .message-wrapper it returns me an HTMLCollection, but I can't access its child
let messages = this.element.nativeElement.getElementsByClassName("message-wrapper");
console.log(messages); // Returns HTMLCollection
console.log(messages[0]); // Returns null

Related

How to validate inputs on each step in a multi part form in vuejs?

I have created step in each tabs which represent the steps in the form. I have used v-if condition to check which step should be displayed. As of now the steps work perfectly fine when i click the next button. Even if the inputs are empty I am able to go to the next step. I want some validation on each step that will check if the input is empty and add a class to that input say "error-class". How do I do that in vuejs?
This is my form in .vue file.
<form id="product_form" enctype="multipart/form-data">
<!-- One "tab" for each step in the form: -->
<button type="button" v-if="step === 2 || step === 3 || step === 4" id="prevBtn1" #click.prevent="prev()"></button>
<div v-if="step === 1" class="tab">
<h3>Post a product</h3>
<div class="preview" id="preview">
</div>
<div class="single-holder">
<input type="text" name="pname" id="pname" placeholder="Title*" required="true" ref="pname">
</div>
</div>
<div v-if="step === 2" class="tab">
<h3>Describe your Product</h3>
<div class="descrip">
<textarea name="description" id="description" placeholder="Description" required="true"></textarea>
</div>
</div>
<div v-if="step === 3" class="tab">
<h3>Set Inventory</h3>
<div class="fixed-width">
<div class="single-holder">
<label>Quantity</label>
<input type="number" name="quantity" id="quantity" required="true">
</div>
</div>
</div>
<div v-if="step === 4" class="tab">
<h3>Share On</h3>
<div class="address-details-holder clearfix">
<div class="single-holder">
<input placeholder="_ _ _ _" id="zipcode" name="zipcode" maxlength="4" type="text" #keypress="onlyNumber">
</div>
</div>
</div>
</form>
This is my method in Vuejs
methods:{
onlyNumber ($event) {
let keyCode = ($event.keyCode ? $event.keyCode : $event.which);
if ((keyCode < 48 || keyCode > 57) && keyCode !== 46) { // 46 is dot
$event.preventDefault();
}
},
prev() {
this.step--;
},
next() {
this.step++;
//if(this.step===1){
// console.log(this.$refs.name.value);
//if(this.$refs.pname.value !== null){
// this.step++;
//}
//}
}
As of now the steps work fine if i remove the if condition in the function next() in methods. But I need input validations on each step so the user has to fill out the missing data in all the form fields.
I think you can use Vee Validate . It will help you check required in each input
<input v-validate="'required'" data-vv-as="field" name="required_field" type="text">
And return error message for that input if it false
<span>{{ errors.first('email') }}</span>

Why validation is not working?

corporate.html
<form #f="ngForm" name="corporateProfileForm" ng-submit="corporateFrmSave(objDS, objDSCurrency)" novalidate="">
<div class="row form-group">
<div class="col-md-12" >
<input type="text" *ngIf="formData" [(ngModel)]="formData.Corporate_Id" id="corpid" title="Corporate ID" tab-index="1" name="corpid" maxlength="20" #corpid="ngModel" required/>
<label for="corp"><b>Corporate ID</b></label>
<div *ngIf="corpid.invalid && (corpid.dirty || corpid.touched)" class="alert alert-danger">
<div *ngIf="corpid.errors.required">
Name is required.
</div>
<div *ngIf="ncorpidame.errors.minlength">
Name must be at least 4 characters long.
</div>
<div *ngIf="corpid.errors.forbiddenName">
Name cannot be Bob.
</div>
</div>
</div>
</div>
</form>
Iam getting error message as
ERROR TypeError: Cannot read property 'invalid' of undefined
Make sure you define your variable with this structure:
TS file
export class ContactComponent implements OnInit {
myform: any;
corpid: FormControl;
constructor() {
}
ngOnInit() {
this.createFormControls();
this.createForm();
}
createFormControls() {
this.corpid= new FormControl('', Validators.required);
}
createForm() {
this.myform = new FormGroup({
corpid: this.corpid,
});
}
HTML file
<div class="md-form form-sm form-group"
[ngClass]="{
'invalid': corpid.invalid && (corpid.dirty || corpid.touched),
'valid': corpid.valid && (corpid.dirty || corpid.touched)
}">
<i class="fa fa-user prefix"></i>
<input
type="text" id="corpid" class="form-control"
required
[formControlName]="'corpid'">
<label for="corpid" class="">Your corpid</label>
</div>
<div class="form-control-feedback"
*ngIf="corpid.errors && (corpid.dirty || corpid.touched)">
<p *ngIf="corpid.errors.required">corpid is required</p>
</div>
It's hard to tell because issue is not in your HTML snipped but in .ts file.
I hope my Code will be helpfull
The issue is with the *ngIf, so when that condition is checked, Angular renders the rest of the template, and while the small fraction of time while *ngIf is being evaluated, your template reference variable corpid does not exist, so it will throw an error when trying to check the validation intially.
Wrap the input field with the validation div's inside the *ngIf instead of applying it only on the input field.
<div class="row form-group" *ngIf="formData">
<input [(ngModel)]="formData.Corporate_Id" id="corpid" ... >
<!-- more code here... -->
</div>
DEMO

angular2 formArray conditional validation

I'm trying to impose validation based on a change of one field onto another field inside a formGroup which is inside a FormArray of multiple instances of this group. I'm using mydatepicker on one of the fields. For example, when the date is changed, I then want the Reason for change field in that group only to be validated to check to make sure the first option (value of 0) is not selected. I have 2 problems with this:
When I change the date, the Reason for change field does not get checked for validity right away. It only happens AFTER I change the value of the field to 1 and then to 0. It's default is set to 0 and it should immediately pick this up when I change the date.
When it does finally realize the form is invalid, it does it for ALL the update buttons rather than just the one in the FormGroup whose Date field I have changed.
ts file code:
subscribeDateChange(formGroup){
(<any>this.rfcActionTasksForm).controls.tasks.controls[0].controls['DueDate'];
const tasks = formGroup;
const changes$ = tasks.controls['DueDate'].valueChanges;
changes$.subscribe(dd => {
var arrayControl = this.rfcActionTasksForm.get('tasks') as FormArray;
var item = arrayControl.at(1);
console.log(item);
if(tasks.value['ReasonForChangeId'] == '0'){
tasks.controls['ReasonForChangeId'].setValidators(Validators.pattern(/([1-9])/));
}
});
}
ngOnInit() {
this.rfcActionTasksForm = this._fb.group({
tasks: this._fb.array([this.buildTask()])
});
}
buildTask(): FormGroup {
return this._fb.group({
Id: '',
Action: ['', Validators.required],
Step: '',
AssignedToId: ['', Validators.required],
AssignedToColour: '',
DueDate: ['', Validators.required],
ReasonForChangeId: '',
OriginalDueDate: '',
Completed: '',
OverDue: ''
},{
validator: (formGroup: FormGroup) => {
//return this.validateDays(formGroup);
//console.log(formGroup.controls['DueDate']);
//this.subscribeDateChange(formGroup.controls['DueDate'], formGroup.controls['ReasonForChangeId']);
return this.subscribeDateChange(formGroup);
}
});
}
html:
<form class="multi-col implementation" *ngIf="rfc.Plan [formGroup]="rfcActionTasksForm">
<div class="task-item" formArrayName="tasks" *ngFor="let task of tasks.controls; let i = index">
<div [formGroupName]="i">
<div class="row header-row">
<div class="col-md-12">
<h5 class="no-margin">Step {{i + 1}}</h5>
<input
[style.display]="'none'"
formControlName="Step">
<input
[style.display]="'none'"
formControlName="AssignedToColour">
<input
[style.display]="'none'"
formControlName="Id">
</div>
</div>
<div class="input-row row">
<div class="col-md-11">
<label for="{{'task' + i}}">Action:</label>
<div
[ngClass]="{'has-error': (tasks.get(i + '.Action').touched || tasks.get(i + '.Action').dirty) && !tasks.get(i + '.Action').valid }">
<textarea
rows="6"
id="{{'task' + i}}"
formControlName="Action"></textarea>
<span class="help-block" *ngIf="(tasks.get(i + '.Action').touched || tasks.get(i + '.Action').dirty) && tasks.get(i + '.Action').errors">
<span *ngIf="tasks.get(i + '.Action').errors.required">
Please enter a title.
</span>
</span>
</div>
</div>
<div class="col-md-1 text-center">
<label>Status</label>
<i *ngIf="tasks.get(i + '.Completed').value" class="glyphicon glyphicon-ok-sign ok" title="Completed"></i>
<i *ngIf="!tasks.get(i + '.Completed').value && !tasks.get(i + '.OverDue').value" class="glyphicon glyphicon-minus-sign pending" title="In progress"></i>
<i *ngIf="!tasks.get(i + '.Completed').value && tasks.get(i + '.OverDue').value" class="glyphicon glyphicon-exclamation-sign text-danger" title="Overdue!"></i>
</div>
</div>
<div class="input-row row">
<div class="col-md-3 assigned-to">
<div
[ngClass]="{'has-error': (tasks.get(i + '.AssignedToId').touched || tasks.get(i + '.AssignedToId').dirty) && !tasks.get(i + '.AssignedToId').valid }">
<label for="{{'assignedTo' + i}}">Assigned to:</label>
<div class="color-block" [style.background]="tasks.get(i + '.AssignedToColour').value"></div>
<label class="fa select">
<select
*ngIf="users"
id="{{'assignedTo' + i}}"
formControlName="AssignedToId">
<option
*ngFor="let user of users | trueValueFilter: 'IsActive'"
[value]="user.Id">{{user.Name}}</option>
</select>
</label>
<span class="help-block" *ngIf="(tasks.get(i + '.AssignedToId').touched || tasks.get(i + '.AssignedToId').dirty) && tasks.get(i + '.AssignedToId').errors">
<span *ngIf="tasks.get(i + '.AssignedToId').errors.required">
Please select a user.
</span>
</span>
</div>
</div>
<div class="col-md-3">
<div
[ngClass]="{'has-error': (tasks.get(i + '.DueDate').touched || tasks.get(i + '.DueDate').dirty) && !tasks.get(i + '.DueDate').valid }">
<label for="{{'dueDate' + i}}">Due date:</label>
<my-date-picker
class="datepicker"
type="text"
id="{{'dueDate' + i}}"
formControlName="DueDate"
[options]="myDatePickerOptions"></my-date-picker>
<span class="help-block" *ngIf="(tasks.get(i + '.DueDate').touched || tasks.get(i + '.DueDate').dirty) && tasks.get(i + '.DueDate').errors">
<span *ngIf="tasks.get(i + '.DueDate').errors.required">
Please set a due date.
</span>
</span>
</div>
</div>
<div class="col-md-2">
<label for="{{'reason' + i}}">Reason for change:</label>
<label class="fa select">
<select
class="reason-select"
*ngIf="reasons"
id="{{'reason' + i}}"
formControlName="ReasonForChangeId">
<option
*ngFor="let reason of reasons"
[value]="reason.Id">{{reason.Reason}}</option>
</select>
</label>
</div>
<div class="col-md-3">
<div class="text-center">
<label for="{{'OriginalDueDate' + i}}">Original due date:</label>
<span>{{tasks.get(i + '.OriginalDueDate').value | dateToStringFilter}}</span>
<input
readonly
type="text"
id="{{'OriginalDueDate' + i}}"
[style.display]="'none'"
formControlName="OriginalDueDate">
</div>
</div>
<div class="col-md-1">
<div class="text-center">
<label for="{{'completed' + i}}">Completed:</label>
<div class="checkbox-group">
<input type="checkbox"
type="checkbox"
id="{{'completed' + i}}"
formControlName="Completed">
<label class="checkbox" for="{{'completed' + i}}"></label>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div>
<button
class="glyphicon glyphicon-plus-sign btn-icon add"
title="Insert task after this one"
(click)="insertTaskField(i)"></button>
<button
class="glyphicon glyphicon-remove-sign btn-icon delete"
title="Delete this task"
(click)="removeTaskField(i, tasks.get(i + '.Id')?.value)"></button>
<button
*ngIf="!tasks.get(i + '.Id').value"
(click)="saveNewTask(rfc.Id, i);"
[disabled]="!rfcActionTasksForm.valid"
class="pull-right">Save new</button>
<button
*ngIf="tasks.get(i + '.Id')?.value"
[disabled]="!rfcActionTasksForm.valid"
(click)="updateTask(i, tasks.get(i + '.Id')?.value)"
class="pull-right">Update</button>
</div>
</div>
</div>
</div>
</div>
<div class="row last">
<div class="col-md-12">
<button
class="pull-right"
[disabled]="enableUpdateAll === false"
(click)="reOrderTasks()">Update All</button>
</div>
</div>
</form>
I see multiple problems here:
A validator should be of the form:
(control: AbstractControl): {[key: string]: any} => {
if(isValid(control))
return null;
else
return {"myValidator":"invalid thing detected"};
}
You are returning undefined everytime so it can't work.
You cannot subscribe because it means everytime you make a change in your form, you resubscribe to the whole group's valuechanges, that's non-sense.
Forget about valueChanges, you need to do your control synchronously. Check if there is an error and use the setError() method of your children controls.
something like :
(group: FromGroup): {[key: string]: any} => {
if(!isValid(group)){
group.get("myChildControl").setErrors({"localError":"error detected !"});
return {"groupError":"error detected !"};
}
return null;
}
I'm not sure if you should use the second parameter of setError(errors,{emitEvent:false}) to avoid propagation or not.

Processing multiple forms from an only template

I have two forms into a template, how can I identify every html form in order
to process it into my handler?
Is possible get the form name in the post handler code?
I'm using nosurf, therefore I must generate and check the
token in the same request, maybe I'm doing wrong..
<form action="/form" method="post" name="form1">
<label class="control-label">Set A</label>
<div class="controls">
<input type="text" id="my" name="my">
</div>
<div style="display:none;">
<input name="_formkey" type="hidden" value="{{.token}}">
</div>
</form>
<form action="/form" method="post" name="form2">
<label class="control-label">Set thing</label>
<div class="controls">
<input type="text" id="thing" name="thing">
</div>
<div style="display:none;">
<input name="_formkey" type="hidden" value="{{.token}}">
</div>
</form>
My Handler
func myHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method{
case "GET":
data:=map[string]interface{}{
"key":nosurf.Token(req),
}
if err := renderTemplate(w, "base", data); err != nil {
log.Error(err)
}
case "POST":
// how?
if r.FormValue("my"){}
...
if r.FormValue("thing"){}
...
}
}
Thanks
Is possible get the form name in the post handler code?
I don't think that's possible, but you can send the form name in another hidden input field.

Yii - Error (and possible solution) validating form with clientValidation active, and using a custom container

I was creating a CActiveForm with the ClientValidation enabled, but stepped in an error when I wrapped a RadioButtonList inside a UL container.
Whenever I submited the form I get a "field cannot be empty" message, even if the radios were checked.
My form:
$form=$this->beginWidget('CActiveForm', array(
'id'=>'enrollment-form',
'enableClientValidation'=>true,
'clientOptions'=>array(
'inputContainer'=>'ul',
'validateOnSubmit'=>true,
),
));
And one of the RadioButtonLists I use:
<div id="drinks">
<?php echo $form->label($model,'drink_id',array('class' => 'title')); ?>
<?php echo $form->radioButtonList($model,'drink_id', CHtml::listData($drinks, 'id', 'name'), array('container'=>'ul', 'template' => '<li class="radio_row">{input}{label}</li>','separator' => '')); ?>
<?php echo $form->error($model,'drink_id'); ?>
</div>
Was I making something wrong?
I studied the code of the jquery.yiiactiveform.js and found that my problem was that in the function that gets the value of the inputs, the value was not taken correctly, because the ID used to identify the inputs was not set in a span, or in the checkboxes/radios themselves (the id was set in the UL container I defined):
var getAFValue = function (o) {
var type,
c = [];
if (!o.length) {
return undefined;
}
if (o[0].tagName.toLowerCase() === 'span') {
o.find(':checked').each(function () {
c.push(this.value);
});
return c.join(',');
}
type = o.attr('type');
if (type === 'checkbox' || type === 'radio') {
return o.filter(':checked').val();
} else {
return o.val();
}
};
So I solved this adding || o[0].tagName.toLowerCase() === 'ul':
var getAFValue = function (o) {
var type,
c = [];
if (!o.length) {
return undefined;
}
if (o[0].tagName.toLowerCase() === 'span' || o[0].tagName.toLowerCase() === 'ul') {
o.find(':checked').each(function () {
c.push(this.value);
});
return c.join(',');
}
type = o.attr('type');
if (type === 'checkbox' || type === 'radio') {
return o.filter(':checked').val();
} else {
return o.val();
}
};
Maybe I was just making some mistake and this solution is just a crappy workaround... but maybe this is just a bug ¿?
The HTML generated:
<form id="enrollment-form" action="/enrollment" method="post">
<div style="display:none"><input type="hidden" value="b06e1d1d796f838f30ba130f8d990034aa9fdad6" name="YII_CSRF_TOKEN" /></div>
<div id="enrollment-form_es_" class="errorSummary" style="display:none"><p>Please fix the following input errors:</p>
<ul><li>dummy</li></ul>
</div>
<div id="drinks">
<label class="title" for="EnrollmentForm_drink_id">Drinks</label>
<input id="ytEnrollmentForm_drink_id" type="hidden" value="" name="EnrollmentForm[drink_id]" />
<ul id="EnrollmentForm_drink_id">
<li class="radio_row">
<input id="EnrollmentForm_drink_id_0" value="2" type="radio" name="EnrollmentForm[drink_id]" />
<label for="EnrollmentForm_drink_id_0">Tea</label>
</li>
<li class="radio_row">
<input id="EnrollmentForm_drink_id_1" value="1" type="radio" name="EnrollmentForm[drink_id]" />
<label for="EnrollmentForm_drink_id_1">Juice</label>
</li>
</ul>
<div class="errorMessage" id="EnrollmentForm_drink_id_em_" style="display:none"></div>
</div>
I noticed that if I didn't use any container when generating the RadioButtonList, Yii aplies a "class=success" to a span. And with my UL container, that class is not generated. When I've changed the Javascript, the class success is generated again.
** The HTML generated without the inputContainer option and without the last array in radioButtonList:
<div id="drinks">
<label class="title" for="EnrollmentForm_drink_id">Bebidas</label>
<input id="ytEnrollmentForm_drink_id" type="hidden" value="" name="EnrollmentForm[drink_id]">
<span id="EnrollmentForm_drink_id">
<input id="EnrollmentForm_drink_id_0" value="2" type="radio" name="EnrollmentForm[drink_id]"> <label for="EnrollmentForm_drink_id_0">Tea</label><br>
<input id="EnrollmentForm_drink_id_1" value="1" type="radio" name="EnrollmentForm[drink_id]"> <label for="EnrollmentForm_drink_id_1">Juice</label>
</span>
<div class="errorMessage" id="EnrollmentForm_drink_id_em_" style="display:none"></div>
</div>
As you can see, Yii generates a strange span that controls if the form is valid or not, by aplying to it the class "success". If I use the inputContainer UL then this class="success" is not generated (with my workaround it is generated and it works).