Angular2: how do display character count on reactive form input - javascript

I'm using Angular2 reactive forms, and I want to display a character count of a textarea as the user types.
I was hoping to just be able to include the form control's name.length in my html like so:
<div class="form-group">
<label for="incidentDescription">Brief Description of Incident</label>
<textarea id="incidentDescription" formControlName="incidentDescription" required [attr.maxLength]="maxIncidentDescriptionLength"></textarea>
<small><code>{{alaynaPage.incidentDescription.length}}</code> of <code>{{maxIncidentDescriptionLength}}</code> characters</small>
</div>
This "works" however the length of the form control lags one keystroke behind. So for example if I type a into the textarea {{alaynaPage.incidentDescription.length}} is 0. If i then type b (so string is ab) {{alaynaPage.incidentDescription.length}} is now 1.
How do I get this to work as expected?
I got it to work via a hack but there has to be an easier way:
//in component:
theLength: number = 0;
ngOnInit(): void {
this.buildForm();
(this.alaynaPageForm.controls['incidentDescription'] as FormControl).valueChanges.subscribe(value => {
// do something with value here
this.theLength = value.length;
});
}
//in my html:
<small class="form-text text-muted"><code>{{theLength}}</code> of <code>{{maxIncidentDescriptionLength}}</code> characters</small>

You just need to use a template reference variable
<textarea id="incidentDescription" formControlName="incidentDescription" #incidentDescription></textarea>
<small class="form-text text-muted"><code>{{incidentDescription.value.length}}</code> of <code>{{maxIncidentDescriptionLength}}</code> characters</small>

You want a hack, here's the hack. Use this:
{{alaynaPageForm.value?.incidentDescription?.length}}

Related

How to set up a specific reject message for a HTML number input element?

I have a loop of html forms <input type="number">, which are basically simple algebra calculations for certain people to fill in. I set the correct answer by limiting both the max and min accepted number to the same number. However, in this way, if the participant gives a wrong answer, the reject message would be something like this: "values must be greater than or equal to ...". It is technically correct but I would like it to only say "incorrect answer, please try again".
Is there any way to do this?
Tried to use something like alert =, but it doesn't meet my requirements.
There's ${parameters.numbers} and ${parameters.answers} in the code because I am using lab.js for the looping. They just mean every time the number in the equation and the answer would change. For example, for the first loop ${parameters.numbers} is 200, and the corresponding answer ${parameters.answers} is 194. lab.js would take care of converting these two parameters to actual numbers for each loop of the form.
<form>
<label for="algebra">${parameters.numbers} - 6 = ?</label><br>
<input name="algebra" type="number" id="algebra" required="" max="${parameters.answers}" min="${parameters.answers}"><br>
<button type="submit">OK</button>
</form>
I try to avoid a dramatic alert dialogue for this, just a non-intrusive message like the default style would be good. If you want to recreate the default "values must be greater than or equal to ..." message, just replace the parameters like this would be good:
<form>
<label for="algebra">200 - 6 = ?</label><br>
<input name="algebra" type="number" id="algebra" required="" max="194" min="194"><br>
<button type="submit">OK</button>
</form>
I agree with #ElroyJetson that putting the answer inside the tag is not a good idea, but I focused this answer on the way you can set and unset the error message.
I used jQuery, but this can also be done with plain javascript.
The idea is to group the input tag with a span tag (here inside the div with class input-field).
When the value changes or when the form is submitted (in this case when the value changes), you remove any previous error message from the span tag, and then perform the validation. If there is an error you set it in the span tag.
In this way the error message will show below the input element.
To try it fill in an answer and click outside of the input box.
$(document).ready(function(){
$(".input-field").change(function(){
let $inputField = $(this);
let $input = $inputField.find("input");
let $errorMsg = $inputField.find("span.err-msg");
let max = Number($input.data("max"));
let min = Number($input.data("min"));
$errorMsg.text("");
let v = Number($input.val());
if(v < min || v > max){
$errorMsg.text("Invalid answer");
}
});
});
.err-msg{
color:red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form>
<div class="input-field">
<label for="algebra">200 - 6 = ?</label><br>
<input name="algebra" type="number" id="algebra" required="" data-max="194" data-min="194"><br>
<span class="err-msg"></span>
</div>
</form>
Don't set the correct answer with min & max. Instead, just call a javascript function by giving your button tag an onClick to evaluate if the user's answer is correct.
<button onclick="evaluateAnswer('.algebra');" class="submitBtn" >OK</button>
Then your javascript should look something like this:
function evaluateAnswer(cssClass){
var usersAnswer = $(cssClass).val();
var actualCorrectAnswer = 100;
if(usersAnswer == actualCorrectAnswer){
//Do something to proceed
}else{
alert('Sorry, your answer is incorrect');
}
}
Also, I just noticed that you did not want to alert as-in a javascript alert. What you could do is style your message and give it a css class that has the property display:none. Then when you want to show the message when user enters the wrong answer, you can use javascript to remove the class, and also use javascript to add the class back when user enters correct answer.
Edit
You should maybe store your correct answers in a database, evaluate it's correctness serverside, and use Ajax to display the message to prevent users from being able to right-click -> view source and look at the answers in your client-side code
My current solution is like this. There is invisible html elements which stores the correct answer, and the js script validates if the input is correct. Again, the ${} parts represents variables that change in each loop.
html part
<main class="content-horizontal-center content-vertical-center">
<form name="mathEvaluation">
<label for="algebra">${parameters.numbers} - 6 = ?</label><br>
<input name="answer" type="number" id="answer" required="" size="3"><br>
<button type="submit">OK</button>
<input type="hidden" id="hidenAnswer" value=${parameters.answers} />
</form>
</main>
js part
this.options.validator = function(data) {
if(mathEvaluation.answer.value == mathEvaluation.hidenAnswer.value){
return true
} else {
alert("Please enter the correct number.")
}
}

How to submit data from input fields generated using ng-repeat and get the value of all the input fields?

I am new to Angular js and I need to create a form where the input fields will be dynamically generated based on a loop and I need to send all the field's data to an API.
This is the string that I get from the backend
"Earth:planet,life,solar,global$##data_col.signal:gateway ox,gw ox,gateway all ox,all sig,,gw signal gain,gateway"
This is my part of the html below where I process the string
<div class="container-fluid synBox">
<span><b>Enter synonyms:</b></span>
<form enctype='application/json'>
<div class="form-group" name="syn" ng-repeat="n in message.expert_advice.split('$##')">
<span class="fonts" style="color:#487baa;"><b>{{n.split(":")[0]}}</b></span>
<input class="form-control" id="expert_advice_input" type="text" ng-model={{n.split(":")[1]}} placeholder="" name={{n.split(":")[0]}} value={{n.split(":")[1]}}>
</div>
<button type="submit" class="btn btn-primary center-block" ng-click="submit_synonyms()">Submit</button>
</form>
</div>
Here is my js for the function in onclick submit_synonyms()
$scope.submit_synonyms = function() {
var variable = document.getElementById('expert_advice_input').value;
console.log(variable)
}
Here is what it looks like in the UI
I was hoping that I would get the value for all input fields but when I click the button I only get the value of the first input field (as seen in the console).
planet,life,solar,global
I also followed other similar questions in stackoverflow like Ng-repeat submit form with generated fields but couldn't figure out how to apply it in my situtation. What am I doing wrong?
Do note that the number of input fields can be dynamic based on the string supplied to me.
ADDITIONAL INFO
Just for the sake of clarity, the reason I am doing the splits on the string is to get the heading and the remaining comma separated strings in the input fields to which a user can add more string and hit the submit button.
write the following in your controller
const param = <input> //"Earth:planet,life,solar,global$##data_col.signal:gateway ox,gw ox,gateway all ox,all sig,,gw signal gain,gateway";
$scope.param = param.split('$##').reduce((acc,val)=> {
const spl = val.split(':');
acc[spl[0]] = spl[1];
return acc;
},{});
and following in your template or html
<ul >
<li data-ng-repeat="(key,value) in param"><label for={{key}}>{{key}}</label><input type='text' value='{{value}}'</li>
</ul>
add styles and optimize code style.

Angular4 ngModel changes type of data from 'number' to 'string'

I have an Angular4 app that captures data from a form and stores in DynamoDB. It uses ngModel to support two-way data binding and on the display all looks good. Issue comes into play because of an input field typed as 'text' bound to Typescript 'number' field. Seems to be changing the type of the object value to 'string'. I would simply change the HTML Input type to 'number' except for the unnecessary and undesirable increment/decrement decorators on the form field (and hiding them seems to have limited support). So I was curious if there is another way to keep the data structure typed as desired... if this is a bug in ngModel... or if the input type just simply needs to be 'number'.
The structure in my sample.component.ts file looks like this:
export class Course {
Id: number;
Name: string;
}
...
courseInstance: Course;
saveCourse() {
JSON.stringify(this.courseInstance);
}
My sample.component.html file looks like this:
<div>
<label for="courseid">Course ID: </label>
<input type="text" class="form-control"[(ngModel)]="courseInstance.Id" name="courseid">
</div>
<div>
<label for="courseName">Course Name: </label>
<input type="text" class="form-control"[(ngModel)]="courseInstance.Name" name="courseName">
</div>
<div>
<button type="button" class="btn btn-info btn-lg" (click)="saveCourse()">Save</button>
</div>
Output of JSON.stringify(this.courseInstance) results in something like
{"Id":"100","Name":"Test course"}
Notice the value 100 represented as a string.
If I don't use the form, but simply create an instance such as
courseInstance: Course = {
Id: 100,
Name: 'Test course'
};
Then when outputing result of JSON.stringify(courseInstance); I get
{"Id":100,"Name":"Test course"}
If I try to use PutItem to store object in DynamoDB, the Id value fails type check when data comes from HTML form.
I would have thought that the typing in Typescript would have held precedence over the HTML 'text' input type.
Change the type text to the number to make the type number and don't let to write letters.
<input type="number" class="form-control"[(ngModel)]="courseInstance.Id" name="courseid">
The [(ngModel)] syntax can only set a data-bound property. If you need to do something more or something different, you can write the expanded form.
<div>
<label for="courseid">Course ID: </label>
<input type="text" #ref class="form-control" [ngModel]="courseInstance.Id" (ngModelChange)="onDataChange(ref.value)"name="courseid">
</div>
The ngModel data property sets the element's value property and the ngModelChange event property listens for changes to the element's value.use parseInt to get the desired result.
ngModelChange will be fired on every keystroke you need to debounce value else event will be emitted for every keystroke and To debounce values you can use a Subject with debounceTime() operator.A subject is both an observable and an observer. This means you can treat it as an observable and pass values to it as well. Apart from this use Template reference variable
In your component
import { Subject } from 'rxjs/Subject';
import {debounceTime } from 'rxjs/operators
debouncer= new Subject();
constructor() {
this.debouncer
.debounceTime(1000)
.subscribe((val) =>{
console.log(val);
this.courseInstance.id=ParseInt(val,10);
});
}
onDataChange(value) {
this.debouncer.next(value);
}
Angular currently doesn't support type checking in the property binding and event binding. As you can see here, there is an open issue in their github:
https://github.com/angular/angular/issues/16952
What I suggest you to do is to change your input to type number and add this css in your component:
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
EDIT: adding an application in the Stackblitz:
https://stackblitz.com/edit/angular4-ngmodel-changes-type-of-data-from-number-to-string

Using AngularJS to style label when ng-model changes in a text field

A few days back, I asked the following question:
I've searched for how to do this, and I've not had any luck. I'm fairly inexperienced with web stuff, so perhaps it's so trivial that no one needs to ask how to do it :(
Suppose I have an HTML text input field with a label, like this:
<label for = "stuff">Stuff</label>
<input type = "text" name = "stuffz" id="stuff" value = "hello!">
Now suppose the text input field value is changed. Is there a way to use AngularJS to restyle the label (Like, turn it green, for example) when this change occurs? I've looked into using ng-change and ng-class, but I'm not knowledgeable enough about how these work to use them in this manner.
When I tested the solution provided, which was:
CSS
.marvellous {
color: green;
}
HTML
<div ng-app="demo">
<label for="stuff" ng-class="{ 'marvellous' : !!hasChanged }">Stuff</label>
<input type="text" id="stuff" ng-model="myModel" ng-change="hasChanged = true"></div>
It worked, but only when I manually changed the text field (i.e. I typed stuff in the text field directly). However, in the particular application I'm working on, I need for the labels to be restyled when the value stored in ng-model changes. Unfortunately, I falsely assumed that if this method worked when I manually changed the text field, it must work if ng-model were to change as well. I've come to find out that it doesn't.
What's the reason for this? And how can I make the label re-style when ng-model changes?
Thanks!
EDIT: When I say "ng-model changes," what I mean is..in my controller, there is a variable that is used to populate the text fields of the app that I'm working on. However, when the user clicks an "import changes" button, this variable is changed according to the changes that they are importing, which consequently changes the corresponding text fields linked to that variable. Ultimately, I want all of the labels attached to these changed text fields to be highlighted for the user to see. I'm sorry for my vagueness.
Each input in an angular js form has meta data properties to help you. For example
<form id="form">
<label for="stuff" ng-class="{ 'marvellous' : form.stuff.$dirty}">Stuff</label>
<input type="text" id="stuff" ng-model="myModel" ng-change="hasChanged = true">
</form>
https://docs.angularjs.org/api/ng/type/form.FormController
You can achieve that using $scope.$watch :
function demoCtrl ($scope) {
$scope.$watch('myModel', function (newValue, oldValue) {
if (newValue) {
$scope.hasChanged = true;
}
});
$scope.changeMyModel = function () {
$scope.myModel = 'wonderful';
};
}
.marvellous {
color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app ng-controller="demoCtrl">
<label for="stuff" ng-class="{ 'marvellous' : !!hasChanged }">Stuff</label>
<input type="text" id="stuff" ng-model="myModel" ng-change="hasChanged = true">
<button ng-click="changeMyModel()">change model</button>
</div>
<!--Use ng-style !!! replace your lable with this--->
<label for="stuff" ng-style="hasChanged()">Stuff</label>
and define your function like this below---
$scope.hasChanged = function(){
if($scope.myModel !== initValue){
return { color: "green" }
}
}

Validate Form Inputs on a good and reliable way

On my page im working on, are a lot of input-forms.
I want to check the user inputs before submit.
Example
HTML/PHP
<input type="text" name="adress" id="adress">
<input type="text" name="amount" id="amount">
and actually im doing the following in Javascript
Javascript
function dataValidation() {
error=false;
var adress = document.getElementById('adress').value;
var amount = document.getElementById('adress').value;
if (adress == ""){
error=true;
}
if (amount >0 && amount < 999){
}
else {
error=true;
}
if (error == false){
document.forms["myForm"].submit();
}
}
So, basically this works fine for my, but the problem is, i have to create that function for every form, and add a IF for every field
So, im looking for a better solution
Idea 1 : Add a list, wich provides the types of input, like
adress = not empty,
amount = >0 && <999,
and then create a function, which checks all fields with that list
Idea 2: Add a tag or something directly to the input field, what it should contain. and then create a function which checks all the fields
Does somebody have a Idea how this can be done, and would like to help/advise me?
you could try this by jquery as:
your html:
<input type="text" name="adress" id="adress">
<input type="text" name="amount" id="amount">
<input type="button" id="check_input">
and apply jquery for all input field
$("#check_input").click(function() {
$("input").each(function() {
var element = $(this);
if(element.val() == "") {
element.css("border","1px solid red");
element.attr("placeholder","Field is empty");
return false;
}
});
});
if you have multiple forms with same fields try to call validation externally it will reduce some of your work apart from that i dont know any other method so lets wait for others reply
You might wanna consider using HTML5.
HTML5 can save time writing JS validations. For example to validate an email address you could use the following:
<input type="email" name="email" required />
Notice the type="email" and required. Instead of using js for validating, you could use HTML5 form attributes with Regular Expression Patterns.
For example If I want to create an input field which accepts only one numbers and 3 uppercase letters, I can easily do with it using a RegEx pattern:
<input type="text" pattern="[0-9][A-Z]{3}">
Read a little bit more on HTML5 and RegEx. It could help you.

Categories

Resources