My error is
EXCEPTION: Error: Uncaught (in promise): Template parse errors:
Can't bind to 'ngFormModel' since it isn't a known native property ("
<h3 class = "head">MY PROFILE</h3>
<form [ERROR ->][ngFormModel]="form" (ngSubmit)="onSubmit(form.value)">
<div class="row">
"): a#3:7
There is no directive with "exportAs" set to "ngForm" ("stname</label>
<input type="text" id="facebook" class="form-control" ngControl="firstname" [ERROR ->]#firstname="ngForm" >
</div>
"): a#9:85
There is no directive with "exportAs" set to "ngForm" ("/label>
<input type="text" id="facebook" class="form-control col-xs-3" ngControl="lastname" [ERROR ->]#lastname="ngForm" >
</div>
My template,
<h3 class="head">MY PROFILE</h3>
<form [ngFormModel]="form" (ngSubmit)="onSubmit(form.value)">
<div class="row">
<div class="form-group">
<label class="formHeading">firstname</label>
<input type="text" id="facebook" class="form-control" ngControl="firstname" #firstname="ngForm">
</div>
<div *ngIf="firstname.touched">
<div *ngIf="!firstname.valid" class="alert alert-danger">
<strong>First name is required</strong>
</div>
</div>
<div class="form-group">
<label class="formHeading">lastname</label>
<input type="text" id="facebook" class="form-control col-xs-3" ngControl="lastname" #lastname="ngForm">
</div>
<div *ngIf="lastname.touched">
<div *ngIf="!lastname.valid" class="alert alert-danger">
<strong>Last name is required</strong>
</div>
</div>
<div class="form-group">
<label class="formHeading">Profilename</label>
<input type="text" id="facebook" class="form-control col-xs-3" ngControl="profilename" #profilename="ngForm">
</div>
<div class="form-group">
<label class="formHeading">Phone</label>
<input type="text" id="facebook" class="form-control col-xs-3" ngControl="phone" #phone="ngForm">
</div>
<div *ngIf="phone.touched">
<div *ngIf="!phone.valid" class="alert alert-danger">
<strong>Phone number is required</strong>
</div>
</div>
<label class="formHeading">Image</label>
<input type="file" name="fileupload" ngControl="phone">
<div class="form-row btn">
<button type="submit" class="btn btn-primary " [disabled]="!form.valid">Save</button>
</div>
</div>
</form>
My Component
import {Component} from '#angular/core';
import { Http, Response, Headers} from '#angular/http';
import {Observable} from 'rxjs/Observable';
import {Subject} from 'rxjs/Subject';
import {contentHeaders} from '../headers/headers';
import {FORM_DIRECTIVES} from '#angular/forms';
import {Router, ROUTER_DIRECTIVES} from '#angular/router';
import {Control, FormBuilder, ControlGroup, Validators} from '#angular/common';
#Component({
templateUrl: './components/profile/profile.html',
directives: [ROUTER_DIRECTIVES, FORM_DIRECTIVES],
})
export class Profile {
http: Http;
form: ControlGroup;
constructor(fbld: FormBuilder, http: Http, public router: Router) {
this.http = http;
this.form = fbld.group({
firstname: ['', Validators.required],
lastname: ['', Validators.required],
profilename: ['', Validators.required],
image: [''],
phone: [''],
});
}
onSubmit(form: any) {
console.log(form);
let body = JSON.stringify(form);
var headers = new Headers();
this.http.post('http://localhost/angular/index.php/profile/addprofile', body, {
headers: headers
})
.subscribe(
response => {
if (response.json().error_code == 0) {
alert('added successfully');
this.router.navigate(['/demo/professional']);
} else {
alert('fail');
}
});
}
}
The problem is that you're still importing from common and especially using the instructions of the old forms.
Import correctly the components for new forms:
import {FORM_DIRECTIVES, REACTIVE_FORM_DIRECTIVES} from '#angular/forms';
import {FormBuilder, FormGroup, Validators} from '#angular/forms';
And correct the component:
#Component({
...
directives: [FORM_DIRECTIVES, REACTIVE_FORM_DIRECTIVES]
})
export class AppComponent {
form: FormGroup;
constructor(fbld: FormBuilder) {
this.form = fbld.group({
...
});
}
...
}
Then correct the view: ngFormModel has been replaced by formGroup, and use formControl for your fields:
<form [formGroup]="form" (ngSubmit)="onSubmit(form.value)">
<div class="row">
<div class="form-group">
<label class="formHeading">firstname</label>
<input type="text" id="facebook" class="form-control" [formControl]="form.controls['firstname']" >
</div>
<div *ngIf ="form.controls['firstname'].touched">
<div *ngIf ="!form.controls['firstname'].valid" class = "alert alert-danger">
<strong>First name is required</strong>
</div>
</div>
...
<div class="form-row btn">
<button type="submit" class="btn btn-primary" [disabled]="!form.valid">Save</button>
</div>
</div>
</form>
Edit. From Angular 2.0.0-rc.5, is necessary to remove the directives from the component and import the form modules in AppModule:
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
#NgModule({
imports: [
...
FormsModule,
ReactiveFormsModule
],
...
bootstrap: [AppComponent]
})
export class AppModule { }
If you use a shared module, do not forget to export them:
#NgModule({
imports: [
...
FormsModule,
ReactiveFormsModule
],
exports: [
...
FormsModule,
ReactiveFormsModule
]
})
export class SharedModule { }
I had the same issue. What I did to solve it:
remove the tag, and add (click)-function to the button
checked my backend: there was an error in some special event... fixed it
Now it doesn't fire twice anymore.
So double check this! You never know...
Just import the following statement in ts,
import {FORM_DIRECTIVES, FormBuilder, Validators, REACTIVE_FORM_DIRECTIVES} from '#angular/forms';
directives: [CORE_DIRECTIVES, ROUTER_DIRECTIVES, FORM_DIRECTIVES, REACTIVE_FORM_DIRECTIVES],
Make the following changes in templates,
<h3 class = "head">MY PROFILE</h3>
<form [ngFormModel]="form" (ngSubmit)="onSubmit(form.value)">
<div class="row">
<div class="form-group">
<label class="formHeading">firstname</label>
<input type="text" id="facebook" class="form-control" [formControl]="form.controls['firstname']">
</div>
<div *ngIf ="firstname.touched">
<div *ngIf ="!firstname.valid" class = "alert alert-danger">
<strong>First name is required</strong>
</div>
</div>
<div class="form-group">
<label class="formHeading">lastname</label>
<input type="text" id="facebook" class="form-control col-xs-3" [formControl]="form.controls['lastname']">
</div>
<div *ngIf ="lastname.touched" >
<div *ngIf = "!lastname.valid" class = "alert alert-danger">
<strong>Last name is required</strong>
</div>
</div>
<div class="form-group">
<label class="formHeading">Profilename</label>
<input type="text" id="facebook" class="form-control col-xs-3" [formControl]="form.controls['profilename']" >
</div>
<div class="form-group">
<label class="formHeading">Phone</label>
<input type="text" id="facebook" class="form-control col-xs-3" [formControl]="form.controls['phone']">
</div>
<div *ngIf ="phone.touched" >
<div *ngIf = "!phone.valid" class = "alert alert-danger">
<strong>Phone number is required</strong>
</div>
</div>
<div class="form-row btn">
<button type="submit" class="btn btn-primary " [disabled]="!form.valid">Save</button>
</div>
Finally do this in your bootstrapping,
import {provideForms, disableDeprecatedForms} from '#angular/forms';
bootstrap(MyDemoApp, [
provideForms(),
disableDeprecatedForms()]);
Related
I have a small contact form that I have built using Angular. I want to validate the form and change the form data to JSON object.
Here's my Form:
<form [formGroup]="addContactForm" (ngSubmit)="onSubmit()" novalidate >
<div [hidden]="addcontactForm.submitted">
<div class="modal-body" style="overflow: auto">
<!-- create contact -->
<div style="padding: 0 0px 0px 25px;margin-top:30px;">
<div class="form-horizontal">
<span *ngIf="ACname.invalid && (ACname.dirty || ACname.touched)" class="has-error">
<span *ngIf="ACname.errors.required">
Last Name is required.
</span>
</span>
<!-- name -->
<div FormGroupName="ACname">
<div class="form-group" style="text-align:right" [ngClass]="{ 'has-error': ACname.addContactFirstName.invalid && (ACname.addContactFirstName.dirty || ACname.addContactFirstName.touched) }">
<label class="col-sm-3" for="addContactFirstName">First Name</label>
<div class="col-sm-7">
<input id="addFirstName"
formControlName="addContactFirstName"
class="form-control"
placeholder="Enter First Name" />
</div>
</div>
<div class="form-group" style="text-align:right" [ngClass]="{ 'has-error': ACname.addContactLastName.invalid && (ACname.addContactLastName.dirty || ACname.addContactLastName.touched) }">
<label class="col-sm-3" for="addContactLastName">Last Name</label>
<div class="col-sm-7">
<input id="addLastName"
class="form-control"
formControlName="addContactLastName"
placeholder="Enter Last Name" />
</div>
</div>
</div>
<div FormGroupName="ACcontactMethod">
<!-- office phone -->
<div class="form-group" style="text-align:right" [ngClass]="{ 'has-error': ACcontactMethod.addcontactForm.submitted && !ACcontactMethod.addContactOfficePhone.valid }">
<label class="col-sm-3" for="addContactOfficePhone">Office Phone</label>
<div class="col-sm-7">
<input id="addofcPhone"
type="text"
class="form-control"
formControlName="addContactOfficePhone"
placeholder="Enter Office Number" />
<span *ngIf="addContactOfficePhone.invalid && (addContactOfficePhone.dirty || addContactOfficePhone.touched)" class="has-error">
<span *ngIf="addContactLastName.errors.required">
Name is required.
</span>
</span>
</div>
</div>
<!-- mobile phone -->
<div class="form-group" style="text-align:right" [ngClass]="{ 'has-error': addcontactForm.submitted && !addContactMobilePhone.valid }">
<label class="col-sm-3" for="addContactMobilePhone">Mobile Phone</label>
<div class="col-sm-7">
<input id="addmobPhone"
type="text"
class="form-control"
formControlName="addContactMobilePhone"
placeholder="Enter Mobile Number" />
<span *ngIf="addContactMobilePhone.invalid && (addContactMobilePhone.dirty || addContactMobilePhone.touched)" class="has-error">
<span *ngIf="addContactMobilePhone.errors.required">
Name is required.
</span>
</span>
</div>
</div>
<!-- home phone -->
<div class="form-group" style="text-align:right" [ngClass]="{ 'has-error': addcontactForm.submitted && !addContactHomePhone.valid }">
<label class="col-sm-3" for="addContactHomePhone">Home Phone</label>
<div class="col-sm-7">
<input id="addhomPhone"
type="text"
class="form-control"
formControlName="addContactHomePhone"
placeholder="Enter Home Number" />
<span *ngIf="addContactHomePhone.invalid && (addContactHomePhone.dirty || addContactHomePhone.touched)" class="has-error">
<span *ngIf="addContactHomePhone.errors.required">
Name is required.
</span>
</span>
</div>
</div>
<!-- email -->
<div class="form-group" style="text-align:right" [ngClass]="{ 'has-error': addcontactForm.submitted && !addContactEmail.valid }">
<label class="col-sm-3" for="addContactEmail">Email</label>
<div class="col-sm-7">
<input id="addEmail"
type="email"
class="form-control"
formControlName="addContactEmail"
placeholder="Enter Email" />
<span *ngIf="addContactEmail.invalid && (addContactEmail.dirty || addContactEmail.touched)" class="has-error">
<span *ngIf="addContactEmail.errors.required">
Name is required.
</span>
</span>
</div>
</div>
<!-- chat id -->
<div class="form-group" style="text-align:right" [ngClass]="{ 'has-error': addcontactForm.submitted && !addContactChatId.valid }">
<label class="col-sm-3" for="addContactChatId">Chat ID</label>
<div class="col-sm-7">
<input id="addChatID"
type="text"
class="form-control"
formControlName="addContactChatId"
placeholder="Enter Chat ID" />
<span *ngIf="addContactChatId.invalid && (addContactChatId.dirty || addContactChatId.touched)" class="has-error">
<span *ngIf="addContactChatId.errors.required">
Name is required.
</span>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default pull-left" (click)="addcontactForm.reset()" data-dismiss="modal">Close</button>
<button type="submit"
class="btn btn-primary"
[disabled]="!addcontactForm.valid"
(click)="addContact(model);">
Add Contact
</button>
</div>
</div>
<div class="submitted-message" *ngIf="addcontactForm.submitted">
<p>You've submitted your contact, {{ addcontactForm.value.addContactFirstName }} {{ addcontactForm.value.addContactLastName }}!</p>
<button type="button" class="btn btn-default pull-left" (click)="addcontactForm.reset()" data-dismiss="modal">Close</button>
<button (click)="addcontactForm.resetForm({})">Add new Contact </button>
</div>
</form>
Here's my ts:
import { Component} from '#angular/core';
import { AppComponent } from '../app.component';
import { FormBuilder, Validators, FormGroup, FormControl } from '#angular/forms';
#Component({
selector: 'addcontactmodal',
templateUrl: 'addcontact.component.html'
})
export class AddContactModalComponent {
id: any;
addContactForm: FormGroup;
constructor(private _appComponent: AppComponent, private fb: FormBuilder) {
this.id = localStorage.getItem('Id');
this.addContactForm = this.fb.group({
ACname: new FormGroup({
addContactFirstName: new FormControl('', Validators.minLength(40)),
addContactLastName: new FormControl('', Validators.minLength(40)),
}),
ACcontactMethod: new FormGroup({
addContactOfficePhone: new FormControl('', Validators.minLength(20)),
addContactMobilePhone: new FormControl('', Validators.minLength(20)),
addContactHomePhone: new FormControl('', Validators.minLength(20)),
addContactEmail: new FormControl('', Validators.minLength(127)),
addContactChatId: new FormControl('', Validators.minLength(127))
})
});
}
// private method(s)
private addContact() {
let data = {
ChatId: this.fb.group('addContactChatId').value,
Email: addContactEmail,
FirstName: addContactFirstName,
HomePhone: addContactHomePhone,
MobilePhone: addContactMobilePhone,
LastName: addContactLastName,
OfficePhone: this.model.addContactOfficePhone
}
this._appComponent.signalRService.setAgentContact(this.id, data);
}
}
I want to:
Validate the form
Have the data output to JSON
I do not get any of the validation the form promises. It doesn't submit.
Errors:
nhandled Promise rejection: Cannot read property 'invalid' of undefined ; Zone: <root> ; Task: Promise.then ; Value: TypeError: Cannot read property 'invalid' of undefined
at Object.View_AddContactModalComponent_0.co [as updateDirectives]
You are not using complete property paths for your validation messages. Here's a simplified template of yours:
<form [formGroup]="addContactForm" (ngSubmit)="onSubmit()" novalidate >
<!-- formGroupName - mark all form controls belonging to this group inside tag -->
<div formGroupName="ACname">
<input formControlName="addContactFirstName" />
<!-- use complete property path or do like follows! -->
<span *ngIf="addContactForm.hasError('minlength', 'ACname.addContactFirstName')">
Minlength 40
</span>
</div>
</form>
StackBlitz
How can I make submit button disabled, when my form is invalid in Angular2?
Now I use code:
<input type="submit" [disabled]="!registrationForm.valid" value="Zarejestruj">
and this is not working and make form always disabled, also when this is valid.
Any suggestion?
UPDATE:
I found error, in form I have file input and this causes form button failed. Here is my whole code (without validation tips)
<div class="form">
<form [formGroup]="registrationForm" (ngSubmit)="RegisterUser(registrationForm.value)" novalidate>
<div class="form-row">
<input type="text" name="login" placeholder="Nazwa użytkownika" [formControl]="registrationForm.controls['login']">
</div>
<div class="form-row">
<input type="password" name="password" placeholder="Hasło" [formControl]="registrationForm.controls['password']" validateEqual="repeatPassword" reverse="true">
</div>
<div class="form-row">
<input type="password" name="repeatPassword" placeholder="Powtórz hasło" [formControl]="registrationForm.controls['repeatPassword']" validateEqual="password">
</div>
<div class="form-row">
<input type="file" name="avatar" file-model="avatar" [formControl]="registrationForm.controls['avatar']" >
</div>
<div class="form-row submit">
<input type="submit" [disabled]="!registrationForm.valid" value="Zarejestruj">
</div>
</form>
</div>
And registration.component.ts:
/ Imports
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormBuilder, Validators } from '#angular/forms';
#Component({
templateUrl: './app/registration/registration.component.html'
})
// Component class implementing OnInit
export class RegistrationComponent {
registrationForm : FormGroup;
constructor(fb: FormBuilder){
this.registrationForm = fb.group({
'login' : [null, Validators.compose([Validators.required, Validators.minLength(4), Validators.maxLength(12)])],
'password' : [null, Validators.compose([Validators.required, Validators.minLength(6), Validators.maxLength(20)])],
'repeatPassword': [null, Validators.required],
'avatar': [null, Validators.required],
})
}
RegisterUser(value: any){
console.log(value);
}
}
Now the question is: why this does not working?
I am building an app using Angular 4.0.2. How can I add a button to a form to add a new row of input and a delete button for a particular row to delete? I mean that I want a form something like this. I want my form to look something like this:
.
Here is my code:
add-invoice.component.html
<h3 class="page-header">Add Invoice</h3>
<form [formGroup]="invoiceForm">
<div formArrayName="itemRows">
<div *ngFor="let itemrow of itemRows.controls; let i=index" [formGroupName]="i">
<h4>Invoice Row #{{ i + 1 }}</h4>
<div class="form-group">
<label>Item Name</label>
<input formControlName="itemname" class="form-control">
</div>
<div class="form-group">
<label>Quantity</label>
<input formControlName="itemqty" class="form-control">
</div>
<div class="form-group">
<label>Unit Cost</label>
<input formControlName="itemrate" class="form-control">
</div>
<div class="form-group">
<label>Tax</label>
<input formControlName="itemtax" class="form-control">
</div>
<div class="form-group">
<label>Amount</label>
<input formControlName="amount" class="form-control">
</div>
<button *ngIf="i > 1" (click)="deleteRow(i)" class="btn btn-danger">Delete Button</button>
</div>
</div>
<button type="button" (click)="addNewRow()" class="btn btn-primary">Add new Row</button>
</form>
<p>{{invoiceForm.value | json}}</p>
Here is my code for add-invoice.component.ts
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormControl, FormArray, FormGroup } from '#angular/forms';
#Component({
selector: 'app-add-invoice',
templateUrl: './add-invoice.component.html',
styleUrls: ['./add-invoice.component.css']
})
export class AddInvoiceComponent implements OnInit {
invoiceForm: FormGroup;
constructor(
private _fb: FormBuilder
) {
this.createForm();
}
createForm(){
this.invoiceForm = this._fb.group({
itemRows: this._fb.array([])
});
this.invoiceForm.setControl('itemRows', this._fb.array([]));
}
get itemRows(): FormArray {
return this.invoiceForm.get('itemRows') as FormArray;
}
addNewRow(){
this.itemRows.push(this._fb.group(itemrow));
}
ngOnInit(){
}
}
Here's a shortened version of your code:
When you init your form, you can add one empty formgroup inside your formArray:
ngOnInit() {
this.invoiceForm = this._fb.group({
itemRows: this._fb.array([this.initItemRows()])
});
}
get formArr() {
return this.invoiceForm.get('itemRows') as FormArray;
}
Then the function:
initItemRows() {
return this._fb.group({
// list all your form controls here, which belongs to your form array
itemname: ['']
});
}
Here is the addNewRow and deleteRow functions:
addNewRow() {
this.formArr.push(this.initItemRows());
}
deleteRow(index: number) {
this.formArr.removeAt(index);
}
Your form should look like this:
<form [formGroup]="invoiceForm">
<div formArrayName="itemRows">
<div *ngFor="let itemrow of formArr.controls; let i=index" [formGroupName]="i">
<h4>Invoice Row #{{ i + 1 }}</h4>
<div>
<label>Item Name</label>
<input formControlName="itemname">
</div>
<button type="button" (click)="deleteRow(i)" *ngIf="formArr.controls.length > 1" >
Delete
</button>
</div>
</div>
<button type="button" (click)="addNewRow()">Add new Row</button>
</form>
Here's a
DEMO
I am trying to do a login form component but I cannot read the form data.
When I try to write username on the console, 'undefined' writes.
Everything seems usual but form data does not come to component.
Below is the html code:
<form (ngSubmit)="onSubmit(myForm)"
#myForm="ngForm"
class="form-signin">
<div class="form-group">
<h2 class="form-signin-heading">Please sign in</h2>
<input type="text"
id="inputUsername"
name="inputUsername"
class="form-control"
placeholder="User Name"
required>
<input type="password"
id="inputPassword"
name="inputPassword"
class="form-control"
placeholder="Password" >
</div>
<button class="btn btn-lg btn-primary btn-block"
type="submit">Sign in</button>
</form>
Component ts:
#Component({
selector: 'signin',
templateUrl: './signin.component.html',
encapsulation: ViewEncapsulation.None
})
export class SigninComponent implements OnInit{
constructor(){}
ngOnInit(){ }
onSubmit(form: NgForm){
console.log(form.value.inputUsername);
}
}
Thanks in advance.
You need to add the ngModel directive to each of the form fields. This registers the form fields to your form.
<input type="text"
id="inputUsername"
name="inputUsername"
class="form-control"
placeholder="User Name"
ngModel
required>
add FormsModule in appmodule.ts
import { FormsModule } from '#angular/forms';
imports: [
FormsModule,
]
While following the tutorial for forms at https://angular.io/docs/ts/latest/guide/forms.html
I keep getting undefined errors whenever referring to any of the properties that are defined in the .ts file as long as I use templateUrl and a seperate .html
When I take the same html and place in the .ts file inside `` in template everything works fine.
So my question is: Does the templateURl file need to define scope somehow? The tutorial doesn't seem to cover anything about it and I assumed that it would get access to the component variables that called it.
import { Component } from '#angular/core';
import { NgForm } from '#angular/forms';
import { Guitar } from './guitar';
#Component({
selector: 'guitar-form',
// template: `
// <div class="container">
// <h1>Guitar Form</h1>
// <form>
// <div class="form-group">
// <label for="brand">Brand</label>
// <select class="form-control" required
// [(ngModel)]="testmodel.brand" name="brand">
// <option *ngFor="let b of brands" [value]="b">{{b}}</option>
// </select>
// TODO: remove this: {{testmodel.brand}}
// </div>
// <div class="form-group">
// <label for="model">Model</label>
// <input type="text" class="form-control" required
// [(ngModel)]="testmodel.model" name="model">
// TODO: remove this: {{testmodel.model}}
// </div>
// <div class="form-group">
// <label for="color">Color</label>
// <input type="text" class="form-control"
// [(ngModel)]="testmodel.color" name="color">
// TODO: remove this: {{testmodel.color}}
// </div>
// <button type="submit" class="btn btn-default">Submit</button>
// </form>
// </div>
// `
templateUrl: 'app/guitar-form.component.html'
})
export class GuitarFormComponent {
brands = ['Fender', 'Gibson', 'Guild', 'Jackson', 'Epiphone', 'Charvel'];
testmodel = new Guitar(69, this.brands[0], 'Stratocaster', 'Black');
submitted = false;
onSubmit() { this.submitted = true; }
// TODO: remove this when we're done
get diagnostic() { return JSON.stringify(this.model); }
}
Funny, the undefined disappeared when I commented it out so I could demonstrate but ironically the diagnostic is no longer working when placed in the html and this was the one aspect that was working earlier. I have verified connectivity on an individual basis...
<div class="container">
<h1>Guitar Form</h1>
<form>
{{ diagnostic }}
<div class="form-group">
<label for="brand">Brand</label>
<select class="form-control" required
[(ngModel)]="testmodel.brand" name="brand">
<option *ngFor="let b of brands" [value]="b">{{b}}</option>
</select>
{{ testmodel.brand }}
</div>
<div class="form-group">
<label for="model">Model</label>
<input type="text" class="form-control" required
[(ngModel)]="testmodel.model" name="model">
TODO: remove this: {{testmodel.model}} -->
</div>
<div class="form-group">
<label for="color">Color</label>
<input type="text" class="form-control"
[(ngModel)]="testmodel.color" name="color">
{{ testmodel.color }}
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
I think you need to add
moduleId: module.id,
to the #Component({...}) decorator.
See also Angular2 - What is the meanings of module.id in component?