ngModel 2-way binding not assigning after following the documentation - javascript

My html file has the following in it:
<form (submit)="register()">
<div class="form-group">
<label for="username">Username</label>
<input type="text" name='username' placeholder="Enter your username" [(ngModel)]="userData.username">
In my ts file for the same component I have:
userData: any = {};
Finally, in my app.module.ts I have:
import { NgModule } from '#angular/core';
#NgModule({
...,
imports: [ ... FormsModule, ...],
...})
According to everything I've read, that is sufficient to have the data in my .ts file update, but it is not currently. I've tried manually updating ngModelChange seperately than the model (and updating the [] and () accordingly), changing names across the html file, removing elements from html and adding them back to find if something isn't working, and several other desperation tricks, but can't seem to figure out why this isn't properly binding.

Form control and ngModel shouldn't be implemented at the same time. If you're going to implement a form group, then use a form control. Otherwise, use ngModel.

Related

How to pass selected file from file upload button to slot props when using vue-csv-import?

So I'm making an app right now that's using a package called vue-csv-import and in the docs it tells me that I can define my custom element inside the vue-csv-input component it provides which exposes two slot props v-slot="{file, change}". I've been clawing my eyes out trying to find a way for my code to work. I tried doing it like this but it doesn't work. It somehow can't find my uploaded file in my custom file input but the component works when I'm not defining a custom input and just use the default input the package provides. The package documentation has no examples on how to do actually use the slot props for a custom file input so I can't rely on that.
MyComponent
<vue-csv-input v-slot="{file, change}">
<label for="finput" class="fancy-upload-btn">Upload CSV File</label>
<input #change="change" type="file" name="file" id="finput">
</vue-csv-input>
VueCsvInput from vue-csv-import
<template>
<slot :file="file" :change="change">
<input ref="csvRef" type="file" change="change" :name="name" v-bind="$attrs">
</slot>
</template>
What's inside the change function in VueCsvInput
const change = function () { let tmpFile = csvRef.value.files ? csvRef.value.files[0] : null;
if (validate(tmpFile)) { VueCsvImportData.file = tmpFile;
}
}
I've tried ddding ref="csvRef" to my custom file input but doesn't work either. It always ends up not getting the uploaded the file and the package documentation has no examples on using it with a custom file input, only that it provides the props to do so.
Your help would be highly appreciated. If you have an alternative package to recommend then that would be nice too.

How to get input from textarea in html to .ts in Angular

I am a newbie in Angular and now I want to get user input from a textarea but I fail to do so.
Updated: ng-model="num1" > [(ngModel)]="num1"
HTML:
<span><input [(ngModel)]="num1" type="text" placeholder="Enter value"></span>
// user enter one number
<span><input type="text" readonly value={{answer}}></span></p>
<button class="result" (click)=onSelectEqual()>=</button>
// click this button to get the value that user entered
.ts:
num1 : number;
answer: number;
onSelectEqual(){
this.answer = this.num1 + 1 ;
}
In angular, for two-way binding you have to enclose ngModel in [()]. See - https://angular.io/api/forms/NgModel.
<span><input [(ngModel)]="num1" type="text" placeholder="Enter value"></span>
Use [(ngModel)] instead of ng-model in your html template. ng-model is the syntax of Angularjs which will not work in Angular2+.
Also make sure to import FormsModule in app.module.ts
import { FormsModule } from '#angular/forms';
Also add it to the imports array in app.module.ts
imports: [
BrowserModule,
AppRoutingModule,
FormsModule
],
This is important as without the FormsModule, angular will not be able to identify the ngModel directive.
On a different note, the input will return the value typed by the user as a string. To read it as a number and add 1 to it, you will have to typecast it:
onSelectEqual(){
this.answer = Number(this.num1) + 1 ;
}
Please also note that // for comments doesn't work in html template, use <!-- --> instead.

How to customize the select element for our requirement or how to convert dropdown became validatable select component

I am attached the custom select component from visual design. I need to reproduce it. up to my level I am not able to customize the select component to mach this mock.
But as a option, I can able to create a drop down using ul to match the same. But In my angular app, I am using Reactiveform, so, where I could not able to add the validation errors.
so what is the correct way to handle this kind of scenario?
Mytry
here is my mock up:
Using ReactiveForms you can set Validators to any of your Form Controls
public readonly form: FormGroup = this.fb.group({
SelectedValue: new FormControl("",Validators.required)
});
Then in your template you can use any options you want defined in your component or otherwise, and set some flags and helpful messages when the form control is not valid.
Validators.required will make sure that the field is required for the form to be valid
<div [formGroup]="form">
<select formControlName="SelectedValue">
<option *ngFor="let opt of options" [value]="opt.key">{{opt.value}}</option>
</select>
<h3 *ngIf="!form.controls.SelectedValue.valid">Selected Value field is required</h3>
<button disabled="!form.controls.SelectedValue.valid" (click)="doSomething()"> Click me</button>
</div>
Your component (Do not forget the imports!):
****
import { FormBuilder, FormGroup, FormControl, Validators } from '#angular/forms';
****
constructor(private fb: FormBuilder){}
options = [{key:1,value:"A"},
{key:2,value:"B"},
{key:3,value:"C"}];
public readonly form: FormGroup = this.fb.group({
SelectedValue: new FormControl("",Validators.required)
});
doSomething(){
//ur logic goes here
alert(this.form.value.SelectedValue);
}
Another option is to use ng-select if you are using angular bootstrap in your project which has some extra functionality like clear and styling.

Angular 2 - ngModel does not work inside <form> after updating to rc4

I've just done an update to RC4; however, many things stopped working, the below was working fine.
Now, ngModel does not work inside unless you remove one of them.
page.js
import {Component} from '#angular/core';
page.html
<form (ngSubmit)="submitForm()">
<ion-list radio-group [(ngModel)]="content" name="ionListGroup">
</ion-list>
</form>
When I click to open page.html, nothing happens, but I see:
*It looks like you're using the old forms module. This will be opt-in in the next RC, and
will eventually be removed in favor of the new forms module. For more information, see:
https://docs.google.com/document/u/1/d/1RIezQqE4aEhBRmArIAS1mRIZtWFf6JxN_7B4meyWK0Y/pub
I've done some debugging and I've seen that if I remove [(ngModel)]="content" or <form> tag everything work fine again, but I can't remove any of them because I need them both.
1. Include form module to NgModule in the app.module.ts file
import { FormsModule } from '#angular/forms';
#MgModule({
imports: [
...
FormsModule,
...
])}
2. Don't forget to give a name to the input element
<input mdInput type="text" [(ngModel)]="username" placeholder="User Name" name="first" required>
From the docs mentioned in the error message. You need the following step
import {disableDeprecatedForms, provideForms} from '#angular/forms';
bootstrap(AppComponent, [
disableDeprecatedForms()
provideForms()
])

Angular 2 doesn't update input value modified by third-party libraries

I have a simple form with its input field associated to a directive:
<form id="scrollable-dropdown-menu">
<input class="input-field" name="value" [someDirective] type="text" [(ngModel)]="value" #query="ngModel" />
<button (click)="onPost(query.value)" type="submit">Search</button>
</form>
The directive changes the input field through the use of a third party library. In my particular case, this is an autocomplete/typeahead Jquery plugin. This plugin provides options to the user and after selecting an option, it changes the value of the input field.
However, Angular doesn't update its property query.value and therefore, passes the old value to the onPost method.
The directive looks something like this:
#Directive({
selector: '[someDirective]',
})
export class MyDirective {
constructor(private elRef: ElementRef, private renderer: Renderer) {
// this changes the value of the field if the user selects an option
$('.input-field').plugin();
}
}
I was suggested to use UpdateValue, but I can't see how to use it inside a directive. That led me to look at #ViewChild, but it doesn't seem to work in directives, although I could be mistaken.
I also tried to force an update by injecting ChangeDetectorRef, but I didn't see any difference. This is what I did:
my.directive.ts
import {ChangeDetectorRef} from '#angular/core';
#Directive({
selector: '[someDirective]',
})
export class MyDirective {
constructor(private elRef: ElementRef, private renderer: Renderer, private detectorRef: ChangeDetectorRef) {
$('.input-field').plugin();
$('.input-field').my-plugin(':selected', ()=>{
// do something...
this.detectorRef.detectChanges();
})
}
}
AfterSelection is triggered when the user selects an option from the autocomplete plugin. For the plugin, it looks a bit different because it binds to some event, but I think this illustrates the idea.
I would appreciate any suggestions.
UPDATE:
This is a plnkr to show the main issue using the typeahead.js library. If you write a letter in the input field, it will show you some options. Select one of them and the value of the input field will change accordingly. However, when you submit the form (clicking on the input field or pressing enter), an alert will show that the value that was passed to the onPost function was the old value of the field and not the autocompleted value you selected.
Provisional solution: Demo. Passing the form to the custom directive and using updateValue to manually change the value:
my.component.ts
<form *ngIf="active" #frm="ngForm">
<input name="value" [myDirective]="frm" type="text" [(ngModel)]="value" #query="ngModel" />
<button (click)="onPost(query.value)" type="submit">Search</button>
</form>
my.directive.ts
#Directive({
selector: '[myDirective]',
})
export class MyDirective {
#Input('myDirectivev') query;
...
$(this.elRef.nativeElement).my-plugin(':selected', (ev, suggestion) => {
this.query.controls['value'].updateValue(suggestion);
});
This works, but let me know if there is a standard approach to solve this kind of issue.
I was told about another solution on Gitter. Unfortunately, I can't remember who told me about it and Gitter's search functionality doesn't help me. Since this person hasn't posted his solution, I will share it for the benefit of people who might be interested.
The idea is basically to pass the whole field as an argument to the onPost(input) method and access the value property of the input field (input.value).
#Component({
selector: 'my-app',
template: `
<form id="scrollable-dropdown-menu" class="search-form">
<input name="value" class="form-control typeahead" typeahead type="text" data-provide="typeahead" [(ngModel)]="thing" #query />
<button (click)="onPost(query)" class="btn btn-default search-button" type="submit">Search</button>
</form>
`,
directives: [TypeaheadDirective],
providers: []
})
export class AppComponent {
onPost(input) {
alert(`Your current value is ${input.value}`);
}
}
Notice the [(ngModel)]="thing" and #query in the input field. I'm not entirely sure why this updates its value correctly and the native attempt doesn't, but this is far simpler than other alternatives. Here you can find a demo.
Instead of
this.detectorRef.detectChanges();
use
$('.input-field').trigger('input');
ControlValueAccessor used by ngModel listens to input events for normal input fields.
Günter Zöchbauer is right. You should dispatch 'input' event to update angular's model. But JQuery trigger didn't help. Only native event fixed this
#Directive({
selector: '[myDirective]',
})
export class MyDirective {
...
$(this.elRef.nativeElement).my-plugin(':selected', (ev, suggestion) => {
const inputEvent: any = document.createEvent('CustomEvent');
inputEvent.initEvent('input', true, true);
el.dispatchEvent(inputEvent);
});
}
I think Zone is not aware of third-party variable changes, hence third-party variable changes are not reflected in Angular
You could try to place your jQuery code inside an angular zone, by implementing something like this:
import Zone: import {NgZone} from 'angular2/core' or import {NgZone} from '#angular/core' , depending on the Angular 2 version you are using;
add zone: NgZone to the constructor arguments of MyDirective;
replace $('.input-field').plugin(); by
zone.run( () => {$('.input-field').plugin(); });
Here is a post about Change detection and Zone
Hope this will help you...

Categories

Resources