0

I have a component binding issue. I'm pretty sure that I've got a syntax issue, but I'm not sure what I'm doing wrong.

The full source code is available at https://github.com/TheMagnificent11/LunchVoting in the feature/6_ExtendedInputBug branch (https://github.com/TheMagnificent11/LunchVoting/tree/feature/6_ExtendedInputBug). The master and develop branches have working code that doesn't use the extended-input component.

input-error.component.ts

This is the component that displays a form field error.

import { Component, Input } from '@angular/core';

@Component({
    selector: 'input-error',
    template: './input-error.component.html'
})
export class InputErrorComponent {

    @Input()
    errorMessage: string;

    @Input()
    isError: boolean = true;

}

input-error.component.html

<span class="text-danger" *ngIf="isError">
    {{errorMessage}}
</span>

extended-input.component.ts

This is the component combines label, input and validation errors (collection of child components input-error) for a single form field.

import {
    Component,
    Input,
    ContentChildren,
    QueryList
} from '@angular/core';

import { InputErrorComponent } from '../input-error/input-error.component';

@Component({
    selector: 'extended-input',
    template: './extended-input.component.html'
})
export class ExtendedInputComponent {

    @Input()
    inputName: string = '';

    @Input()
    labelText: string = '';

    @Input()
    isError: boolean = false;

    @ContentChildren(InputErrorComponent)
    errors: QueryList<InputErrorComponent>;

}

extended-input.component.html

<div class="form-group" [ngClass]="{'has-error':border border-danger}">
    <label for="{{inputName}}">{{labelText}}</label>
    <ng-content select="input-control"></ng-content>
    <div *ngIf="isError">
        <ng-content select="input-errors"></ng-content>
    </div>
</div>

controls.module.ts

Module that has extended-input component

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';

import { ExtendedInputComponent } from './extended-input.component.ts';
import { InputErrorComponent } from './input-error.component.ts';

let components = [
    ExtendedInputComponent,
    InputErrorComponent
];

@NgModule({
    declarations: components,
    imports: [
        CommonModule,
        ReactiveFormsModule
    ]
})
export class ControlsModule {
}

register.component.ts

This the form component that uses the other extended-input component.

import { Component } from '@angular/core';
import {
    FormBuilder,
    FormGroup,
    FormControl,
    Validators
} from '@angular/forms';

@Component({
    selector: 'register',
    templateUrl: './register.component.html'
})
export class RegisterComponent {

    registrationForm: FormGroup;

    givenName: FormControl;

    surname: FormControl;

    constructor(private formBuilder: FormBuilder) {
        this.givenName = new FormControl('', [Validators.required, Validators.maxLength(100)]);
        this.surname = new FormControl('', [Validators.required, Validators.maxLength(100)]);

        this.registrationForm = this.formBuilder.group({
            givenName: this.givenName,
            surname: this.surname,
        });
    }

}

register.component.html

<form [formGroup]="registrationForm" (submit)="onSubmit()">

    <extended-input [inputName]="'givenName'" 
                    [labelText]="'Given Name'"
                    [isError]="givenName.touched && givenName.invalid">
        <input-control>
            <input type="text" formControlName="givenName" />
        </input-control>
        <input-errors>
            <input-error [isError]="givenName.hasError('required')">
                Given Name is required
            </input-error>
        </input-errors>
    </extended-input>

    <!-- The above components should produce HTML similar to this -->
    <div class="form-group">
        <label for="surname">Surname</label>
        <input type="text" formControlName="surname" />
        <div [hidden]="surname.valid || surname.untouched">
            <span class="text-danger" [hidden]="!surname.hasError('required')">
                Surname is required
            </span>
        </div>
    </div>

    <button type="submit" [disabled]="!registrationForm.valid">
        Register
    </button>

</form>

Entry Module

The module that contains register component.

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';

import { ControlsModule } from '../controls/controls.module';

import { RegisterComponent } from './register-component';

@NgModule({
    declarations: components,
    imports: [
        CommonModule,
        ReactiveFormsModule,
        ControlsModule
    ]
})
export class EntryModule {
}

Error

Uncaught Error: Template parse errors:
Can't bind to 'inputName' since it isn't a known property of 'extended-input'.
1. If 'extended-input' is an Angular component and it has 'inputName' input, then verify that it is part of this module.
2. If 'extended-input' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.
3. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component. ("
        <form [formGroup]="registrationForm" (submit)="onSubmit()">

            <extended-input [ERROR ->][inputName]="'givenName'" 
                            [labelText]="'Given Name'"
                 "): ng:///EntryModule/RegisterComponent.html@9:28
Can't bind to 'labelText' since it isn't a known property of 'extended-input'.
1. If 'extended-input' is an Angular component and it has 'labelText' input, then verify that it is part of this module.
2. If 'extended-input' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.
3. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component. ("

            <extended-input [inputName]="'givenName'" 
                            [ERROR ->][labelText]="'Given Name'"
                            [isError]="givenName.touched && givenName.inv"): ng:///EntryModule/RegisterComponent.html@10:28
Can't bind to 'isError' since it isn't a known property of 'extended-input'.
1. If 'extended-input' is an Angular component and it has 'isError' input, then verify that it is part of this module.
2. If 'extended-input' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.
3. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component. (""'givenName'" 
                            [labelText]="'Given Name'"
                            [ERROR ->][isError]="givenName.touched && givenName.invalid">
                <input-control>
               "): ng:///EntryModule/RegisterComponent.html@11:28
'input-control' is not a known element:
1. If 'input-control' is an Angular component, then verify that it is part of this module.
2. If 'input-control' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. (""
                            [isError]="givenName.touched && givenName.invalid">
                [ERROR ->]<input-control>
                    <input type="text" formControlName="givenName" />
             "): ng:///EntryModule/RegisterComponent.html@12:16
Can't bind to 'isError' since it isn't a known property of 'input-error'.
1. If 'input-error' is an Angular component and it has 'isError' input, then verify that it is part of this module.
2. If 'input-error' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.
3. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component. ("
                </input-control>
                <input-errors>
                    <input-error [ERROR ->][isError]="givenName.hasError('required')">
                        Given Name is required
        "): ng:///EntryModule/RegisterComponent.html@16:33
'input-error' is not a known element:
1. If 'input-error' is an Angular component, then verify that it is part of this module.
2. If 'input-error' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. ("
                </input-control>
                <input-errors>
                    [ERROR ->]<input-error [isError]="givenName.hasError('required')">
                        Given Name is requi"): ng:///EntryModule/RegisterComponent.html@16:20
'input-errors' is not a known element:
1. If 'input-errors' is an Angular component, then verify that it is part of this module.
2. If 'input-errors' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. ("input type="text" formControlName="givenName" />
                </input-control>
                [ERROR ->]<input-errors>
                    <input-error [isError]="givenName.hasError('required')">
       "): ng:///EntryModule/RegisterComponent.html@15:16
'extended-input' is not a known element:
1. If 'extended-input' is an Angular component, then verify that it is part of this module.
2. If 'extended-input' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. ("
        <form [formGroup]="registrationForm" (submit)="onSubmit()">

            [ERROR ->]<extended-input [inputName]="'givenName'" 
                            [labelText]="'Given Name'"
 "): ng:///EntryModule/RegisterComponent.html@9:12
    at syntaxError (vendor.js:sourcemap:38524)
    at TemplateParser.parse (vendor.js:sourcemap:49621)
    at JitCompiler._compileTemplate (vendor.js:sourcemap:63824)
    at vendor.js:sourcemap:63743
    at Set.forEach (<anonymous>)
    at JitCompiler._compileComponents (vendor.js:sourcemap:63743)
    at vendor.js:sourcemap:63630
    at Object.then (vendor.js:sourcemap:38513)
    at JitCompiler._compileModuleAndComponents (vendor.js:sourcemap:63629)
    at JitCompiler.compileModuleAsync (vendor.js:sourcemap:63558)

Does anyone know what I've done wrong?

11
  • 3
    try with single quotes surrounding the string [labelText]=" 'Given Name' " Commented Dec 9, 2017 at 8:01
  • 2
    What happens when you use the suggestion above? Commented Dec 9, 2017 at 15:16
  • I still get "template parse errors" Commented Dec 9, 2017 at 15:54
  • It is worth noting that import {FormBuilder} from '@angular/core'; @NgModule({providers: [FormBuilder]}) export ... is an error. Providers are made available implicitly and transiently by the presence of their providing NgModule in an imports array. Commented Dec 31, 2017 at 21:57
  • @AluanHaddad, thanks for that. However, I still get the same parse error. Commented Dec 31, 2017 at 23:30

2 Answers 2

1
+50

If you are declaring extendedInputComponent and InputErrorComponent in some other module(ControlsModule) than your RegisterComponent is declared(EntryModule) and ControlsModule is only imported in EntryModule, you have to export the Components also you declared in the same ControlsModule.

ie, your ControlsModule should be like

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';

import { ExtendedInputComponent } from './extended-input.component.ts';
import { InputErrorComponent } from './input-error.component.ts';

let components = [
    ExtendedInputComponent,
    InputErrorComponent
];

@NgModule({
    declarations: components,
    imports: [
        CommonModule,
        ReactiveFormsModule
    ],
    exports: components
})
export class ControlsModule {
}
Sign up to request clarification or add additional context in comments.

4 Comments

There are other errors in my code but the missing export appears to be the major one
The first ng-content in extended-input.component.html should select an input, not an input-control (as there is no input-control component). Similarly, there is no input-errors component (only input-error). I need to either create one or change the way extended-input displays the validation messages.
why cant you select the input and input-error component the same way you do now
You can select input, but not inpit-control. I don't think you can select input-errors because such a component doesn't exist (only input-error... singular, no plural)
0

1. If 'extended-input' is an Angular component and it has 'inputName' input, then verify that it is part of this module.

Make sure your components are imported properly. I noticed that the RegisterComponent has the 'Component' suffix at the end of the class name but the other ones, InputError and ExtendedInput, don't. Did you create one with the CLI but not the other ones? Maybe you forgot to import them into your module manually if you didn't use the CLI.

After that, there is an issue with property binding that I saw. It expects an expression inside quotes, so if you want to pass in literal text you need to have quotes inside quotes. It's weird. [labelText]="Given Name" is going to look for variables Given and Name, so it needs to be [labelText]="'Given Name'". That's this error from above:

[ERROR ->][labelText]="Given Name"

Your app seems to have some other components input-control and input-errors but I don't see the code for those here, so couldn't get everything working on my end but that's a start. I can't completely follow the strategy with the ng-content/transclusion but that part of angular is not my forte.

4 Comments

I've made your suggested code change regarding component naming and added details of where to find the full source code (github.com/TheMagnificent11/LunchVoting)
Also, haven't use Angular CLI to create any of the code. I used the ASP.Net Core Angular template and then changed the module structure.
I'm not an ASP.Net guy, so I can't debug it on my end. But I suspect when you changed the module structure is what caused the issue. Sorry I can't be of more help.
I think the module structure is mostly fine. If I replace the extended-input component with regular HTML (and standard Angular Forms features), then it works. extended-input is in its own module, so I'll add a bit more of the module code, which may help.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.