Passing interpolation value through ngModel - html

<input [(ngModel)]="newVariablesAttribute.Steps"
class="form-control"
type="text" readonly
name="{{newVariablesAttribute.Steps}}">
{{stepname}}
I want to pass stepname (which I am able to show in the frontend using interpolation) using ngModel.
But It is not working.

Didn't find any issue with your code. Steps might be undefined
Component
export class SomeComponent {
newVariablesAttribute = {
Steps: 'step'
};
stepname = 'abc';
}
html:
<input [(ngModel)]="newVariablesAttribute.Steps" class="form-control" type="text" readonly name="{{newVariablesAttribute.Steps}}">{{stepname}}
O/P

Since stepname is getting rendered in the UI, you can pass it to the ngModel directly like this right -
<input [(ngModel)]="stepname"
class="form-control"
type="text" readonly
name="{{newVariablesAttribute.Steps}}">
{{stepname}}
(I guess newVariablesAttribute.Steps are some array of objects. )

Related

Is there a way to enable the autocomplete for angular reactive form?

I want to set on the autocomplete attribute for an angular form but it doesn't work as expected. It remembers only the values that I submitted only the first time and I would like to remember and suggest all the values no matter how many times I click on submit button.
Here is the stackblitz with the code that I tried.
<form
autocomplete="on"
(ngSubmit)="onSubmit()"
name="filtersForm"
[formGroup]="formGroup1"
>
<div>
<label>First Name</label>
<input
id="firstName"
name="firstName"
autocomplete="on"
formControlName="firstName"
/>
</div>
<div>
<label>Last Name</label>
<input
id="firstName"
name="lastName"
autocomplete="on"
formControlName="lastName"
/>
</div>
<button type="submit">Submit</button>
</form>
Here are the details about the autocomplete attribute that I used.
In Firefox, the autocomplete is working after several clicks on Submit button, the problem is in Chrome and Edge.
Is there a way to make the autocomplete to work for inputs inside the angular form?
I think, I have found a workaround, that only works with Template Driven Form.
TLDR;
What I have discovered while looking after this issue.
On first form submit autofill remember only first time submission values
form submit POST method can remember all values.
Yes, by looking at above, it clearly seems like 2nd way is suitable for us. But why would anybody do form POST for submitting form to BE. There should be better way to tackle this. Otherwise we would have to think of handling PostBack 😃😃 (FW like .Net does it by keeping hidden input's).
Don't worry we can do some workaround here to avoid form POST. I found an answer for handling POST call without page refresh.
Working JSBin with plain HTML and JS
AutoCompleteSaveForm = function(form){
var iframe = document.createElement('iframe');
iframe.name = 'uniqu_asdfaf';
iframe.style.cssText = 'position:absolute; height:1px; top:-100px; left:-100px';
document.body.appendChild(iframe);
var oldTarget = form.target;
var oldAction = form.action;
form.target = 'uniqu_asdfaf';
form.action = '/favicon.ico';
form.submit();
setTimeout(function(){
form.target = oldTarget;
form.action = oldAction;
document.body.removeChild(iframe);
});
}
Basically we change set few things on form attribute.
target="iframe_name" - Connects to iFrame to avoid page refresh.
method="POST" - POST call
url="/favicon" - API url to favicon (lightweight call)
In angular you can create an directive for the same.
import {
Directive, ElementRef, EventEmitter,
HostBinding, HostListener, Input, Output,
} from '#angular/core';
#Directive({
selector: '[postForm]',
})
export class PostFormDirective {
#HostBinding('method') method = 'POST';
#HostListener('submit', ['$event'])
submit($event) {
$event.preventDefault();
this.autoCompleteSaveForm(this.el.nativeElement);
}
constructor(private el: ElementRef) {}
autoCompleteSaveForm(form) {
let iframe = document.querySelector('iframe');
if (!iframe) {
iframe = document.createElement('iframe');
iframe.style.display = 'none';
}
iframe.name = 'uniqu_asdfaf';
document.body.appendChild(iframe);
var oldTarget = form.target;
var oldAction = form.action;
form.target = 'uniqu_asdfaf';
form.action = '/favicon.ico'; // dummy action
form.submit();
setTimeout(() => {
// set back the oldTarget and oldAction
form.target = oldTarget;
form.action = oldAction;
// after form submit
this.onSubmit.emit();
});
}
#Output() onSubmit = new EventEmitter();
ngOnDestroy() {
let iframe = document.querySelector('iframe');
if (iframe) {
document.body.removeChild(iframe);
}
}
}
Okay, so far everything went well. Then I started integrating this in formGroup(Model Driven Form), somehow it didn't worked. It does not store value next time these fields.
<form (ngSubmit)="onSubmit()" [formGroup]="formGroup1" autocomplete="on">
<div>
<label>First Name</label>
<input id="firstName" name="firstName" formControlName="firstName" />
</div>
<div>
<label>Last Name</label>
<input id="lastName" name="lastName" formControlName="lastName" />
</div>
<button>Submit</button>
</form>
Later I tried the same with Template Driven Form. It just worked like a charm! I did not went into the depth why it didn't work for Model Driven Form (perhaps that investigation could eat more time).
<form #form1="ngForm" ngForm postForm (onSubmit)="onSubmit(form1)">
<ng-container [ngModelGroup]="userForm">
<div>
<label>First Name</label>
<input name="firstName" [(ngModel)]="userForm.firstName" />
</div>
<div>
<label>Last Name</label>
<input name="lastName" [(ngModel)]="userForm.lastName" />
</div>
</ng-container>
<button>Submit</button>
</form>
Yeah, I just said in the begining it works only with Template Driven Form. So you would have to switch to Template. And one more important thing to note, you may think of creating dummy POST api call, that can be lightweight rather than hitting favicon.
Stackblitz
autocomplete attribute works only with submitted values. It has nothing to do with Angular.
https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete
If you need some custom behavior then you are better off creating your own component to autocomplete user's input, this way you can use some default values, add more of them on blur etc.
You just need to remove autocomplete="on" in input tag. With chrome, we only add attribute autocomplete="on" in form element and it will be cached all value that user input into input text. Result will be like this:
<form
autocomplete="on"
(ngSubmit)="onSubmit()"
name="filtersForm"
[formGroup]="formGroup1"
>
<div>
<label>First Name</label>
<input
id="firstName"
name="firstName"
formControlName="firstName"
/>
</div>
<div>
<label>Last Name</label>
<input
id="firstName"
name="lastName"
formControlName="lastName"
/>
</div>
<button type="submit">Submit</button>
</form>
You have to create an array with your desired options which should be displayed as autocomplete. You can have a look here https://material.angular.io/components/autocomplete/examples, there are multiple examples which should help you. Even if you're not using Angular Material, the logic would be the same

<input oninput="this.value = this.value.toUpperCase()" /> should not text-transform on the "UI" but send upperCase value to server

<input oninput="this.value = this.value.toUpperCase()" type="text" id="roleName" name="roleName" class="form-control width200px" [(ngModel)]="role.roleName">
The UI is affected and upper casing the input, I just want to send the role.roleName to server in upper case without transforming it on the UI.
NgModel is two-way binding, therefore if the value transformed, then the UI will be reflected as well. If you would like to have different value, you may try to split the value into two parameters, one to view and another use to send to the server.
Sample:
<input oninput="role.roleName = this.value.toUpperCase()" type="text" id="roleName" name="roleName" class="form-control width200px" [(ngModel)]="role.somethingElse">
Then you have to make it uppercase before you put the value into the request object.
In Typescript simply use .toUpperCase()
Or, you can also use the extended format of ngmodel with a setter on your model.
.ts
class Role {
roleName: string;
getName(): string {
return this.roleName;
}
setName(value: string): void {
this.roleName = value.toUpperCase();
}
}
.html
<input
type="text"
id="roleName"
name="roleName"
class="form-control width200px"
[ngModel]="role.getName()"
(ngModelChange)="role.setName($event)"
/>
Please check on Stackblitz: https://stackblitz.com/edit/angular-3u48kn

what is the proper way to dynamically mark a field as required using Angular 2 Forms?

Using Angular 2 (2.0.0), what is the recommended way to dynamically mark a field as required, using Angular Forms?
In all of their examples, the required attribute is just added like:
<input type="text" class="form-control" id="name" required>
What if the model I'm binding to has an IsRequired property, that will be true/false?
If I use something like:
<input [(ngModel)]="field.Value" type="text" value="{{field.Value}}" [attr.required]="field.IsRequired"/>
That renders on the page like (note the ="true"):
<input type="text" required="true" />
For some reason, Angular doesn't appear to recognize this attribute when it has an actual value (the ="true") so when this field is blank, my form itself still is valid:
<form class="ng-untouched ng-pristine ng-valid">
So it would appear that I must use required and not required="true", but how can I add that attribute in dynamically?
What also doesn't work:
<input type="text" {{ getRequiredAttr(field) }} />
Thought I might be able to have a function that returns my string "required" based on the field, that just gives templating errors.
Is there a way to accomplish this and render only required for my attribute? Or a way to make Angular recognize this attribute when it has a value of true/false?
FWIW - I've verified that I can use *ngIf to write two near-identical <input type='text' /> controls based on my IsRequired property and hardcode one with the required attribute but that seems pretty hacky. Hoping there's a better way!
Why do you have to make it so complicated when you can simply do this,
[required]="isFieldRequired() ? 'required' : null"
The basic forms stuff is great for simple forms, but when you need more control like what you have here, that is when you need to start using the more advanced form stuff. What that would look like in your case would be something like this.
#Component({
selector: 'something',
template: `
<form #myForm="ngForm">
<input [(ngModel)]="field.Value" [formContol]="myFieldControl" type="text" [value]="field.Value">
</form>
`
})
export class MyComponent{
public field: any = {Value: 'hello', isRequired: false};
public myFieldControl: FormControl = new FormControl('', [this.dynamicRequiredValidator.bind(this)]);
public dynamicRequiredValidator(control: FormControl):{[key: string]: boolean}{
if(field.IsRequired && !control.value){
return {required: true};
}
return {};
}
}
Note: You will probably need to import the ReactiveFormsModule into your #NgModule. This comes from #angular/forms as well.
There is also another way you can do this with a directive shown here.

Translate ng-model value in input

I'm trying to translate a value in an input. The input is disabled when I need to translate it so the user can't type text in the textbox. The data is inputted using ng-model and currently looks like this:
<input ng-model="reason" ng-disabled="true" type="text" class="form-control" name="reason">
I've also tried the following:
<input ng-model="reason|translate" ng-disabled="true" type="text" class="form-control" name="reason">
<input ng-model="{{ reason | translate}}" ng-disabled="true" type="text" class="form-control" name="reason">
but none of them worked.
I could translate the value in the controller, but I'd like to do this in the html tags so the actual value on the scope doesn't get changed.
How can I achieve this?
You can use ng-value for inputs instead of the HTML input>value. Then you can translate them easily:
<input ng-value="ctrl.reason | translate" ng-model="ctrl.reason">
I got it working with this example: think about an input with a Yes or No value that you want to be translated. Try one way data-binding in your template:
<input value="{{ answer | translate }}"
type="text" class="form-control" name="answer"
(change)="answerChanged($event)">
If you also want to update the value, listen for changes in the component:
answerChanged(Event event) {
this.answer = event.target.value.toUpperCase();
}
Regarding the i18n files.
en.json:
{
"YES": "Yes",
"NO": "No"
}
fr.json:
{
"YES": "Oui",
"NO": "Non"
}
Plunker:
http://plnkr.co/edit/BeYBAWG57eIZOU3KdBCD

output all members of a form (form.FormController)

Please see the code here
it is an angular form, and the variable form should be of FormController . As you see with the usage of form.$pristine, the variable $pristine exists in form, but why it doesn't output when I just try <pre>form = {{form}}</pre>. I wanted to see all the members (if possible function names, else at least all primitives like $dirty, $valid , etc.) Why it is not outputting in the pre tag and how can I show them? It is mainly for debugging, but curious as well.
<form novalidate name="form" class="simple-form">
Name: <input type="text" ng-model="user.name" /><br />
E-mail: <input type="email" ng-model="user.email" /><br />
Gender: <input type="radio" ng-model="user.gender" value="male" />male
<input type="radio" ng-model="user.gender" value="female" />female<br />
<button ng-click="reset()">RESET</button>
<button ng-click="update(user)">SAVE</button>
</form>
<pre>pristine = {{form.$pristine}}</pre>
<pre>form = {{form}}</pre>
output
pristine = true
form = {}
An expression like this: {{form}} will convert the form object to a string. Angular uses the build in angular.toJson function to serialize the object. Please have a look at the documentation: angular.toJson. As you can see there all properties with a leading $ are stripped.
How to solve your problem:
You may use the JSON.stringify function to get all properties. Because you can't call this function in an expression you need to provide a helper function in your controller:
$scope.stringify = function(obj){
return JSON.stringify(obj);
}
Now you are able to output your complete form object in the view:
{{stringify(form)}}
The following should also work...
<pre>form = {{form | json}}</pre>