EXCEPTION: Template parse errors: Only void and fo

2020-02-26 14:20发布

问题:

After adding a component into directives array of root component, Angular2 starts complaining in the browser about self enclosing "meta" tag which should be ok, and which also works without complaints if the directive is not there.

I'm trying to add a my-header component into my application.

app.html

<div>
  <my-header></my-header>
</div>
<nav class="nav-bar">
  <div class="nav-bar-top-spacer"></div>
  <ul>
    <li *ngFor="#group of navigation" class="nav-option-group">
      <div class="nav-option-group"></div>
      <div (click)="hideGroup(group)" class="nav-option-group-name">{{ group.Croatian }}</div>
      <ul [ngClass]="{displayNone: !group.visible}">
        <li [routerLink]="[comp.componentName]" *ngFor="#comp of group.components" class="option-group-item">{{ comp.Croatian }}</li>
      </ul>
    </li>
    <li class="nav-option-group">
      <div class="nav-option-group-name">Odjavi se</div>
    </li>
  </ul>
</nav>
<router-outlet></router-outlet>

app.ts

    import { Component, View } from "angular2/core";
    import { COMMON_DIRECTIVES } from "angular2/common";
    import { Http } from "angular2/http";
    import { RouteConfig, RouterLink, RouterOutlet, Route, ROUTER_DIRECTIVES, Router} from 'angular2/router';

    import { Header } from '../app/header';
    import { AccountData } from '../settings/AccountData/AccountData';
    import { AccountDelete } from '../settings/AccountDelete/AccountDelete';
    import { AccountLogin } from '../settings/AccountLogin/AccountLogin';
    import { AccountPassword } from '../settings/AccountPassword/AccountPassword';
    import { AddTrack } from '../settings/AddTrack/AddTrack';
    import { EditUser } from '../settings/EditUser/EditUser';
    import { MakePlaylist } from '../settings/MakePlaylist/MakePlaylist';
    import { MakeWishlist } from '../settings/MakeWishlist/MakeWishlist';
    import { ManageAdmins } from '../settings/ManageAdmins/ManageAdmins';
    import { ManageEditors } from '../settings/ManageEditors/ManageEditors';
    import { ManageRadiostation } from '../settings/ManageRadiostation/ManageRadiostation';
    import { ManageTracks } from '../settings/ManageTracks/ManageTracks';
    import { ManageUsers } from '../settings/ManageUsers/ManageUsers';

    var components = ['AccountData', 'AccountDelete', 'AccountLogin'
        , 'AccountPassword', 'AddTrack', 'EditUser', 'MakePlaylist'
        , 'MakeWishlist', 'ManageAdmins', 'ManageEditors', 'ManageRadiostation'
        , 'ManageTracks', 'ManageUsers'];

    //var routes = components.map((componentName) => { return new Route(componentName, componentName, componentName) });

    @Component({
        selector: 'App',
        templateUrl: './dest/App/App.html',
        styles: [],
        directives: [ ROUTER_DIRECTIVES, COMMON_DIRECTIVES, Header ]
    })
    @RouteConfig([
        { path: '/', redirectTo: ['AccountData'] },
        { path: 'AccountData', name: 'AccountData', component: AccountData },
        { path: 'AccountDelete', name: 'AccountDelete', component: AccountDelete },
        { path: 'AccountLogin', name: 'AccountLogin', component: AccountLogin },
        { path: 'AccountPassword', name: 'AccountPassword', component: AccountPassword },
        { path: 'AddTrack', name: 'AddTrack', component: AddTrack },

        { path: 'EditUser', name: 'EditUser', component: EditUser },
        { path: 'MakePlaylist', name: 'MakePlaylist', component: MakePlaylist },
        { path: 'MakeWishlist', name: 'MakeWishlist', component: MakeWishlist },
        { path: 'ManageAdmins', name: 'ManageAdmins', component: ManageAdmins },
        { path: 'ManageEditors', name: 'ManageEditors', component: ManageEditors },
        { path: 'ManageRadiostation', name: 'ManageRadiostation', component: ManageRadiostation },
        { path: 'ManageTracks', name: 'ManageTracks', component: ManageTracks },
        { path: 'ManageUsers', name: 'ManageUsers', component: ManageUsers }
    ])
    export class App {
        router: Router;
        location: Location;
        navigation: any[];

        hideGroup(group): void {
            group.visible = !group.visible;
        }

        constructor(router: Router) {
            this.router = router;

            this.navigation = [
                {
                    'Croatian': 'Slusaj radio',
                    'groupName': 'Listen',
                    'components': []
                },
                {
                    'Croatian': 'Vlasničke mogućnosti',
                    'groupName': 'OwnerOptions',
                    'components': [
                        { 'Croatian': 'Upravljaj administratorima', 'componentName': 'ManageAdmins', 'componentObject': ManageAdmins },
                        { 'Croatian': 'Pregledaj podatke o postaji', 'componentName': 'ManageRadiostation', 'componentObject': ManageRadiostation }
                    ]
                },
                {
                    'Croatian': 'Administratorske modućnosti',
                    'groupName': 'AdminOptions',
                    'components': [
                        { 'Croatian': 'Uredi zvučne zapise', 'componentName': 'ManageTracks', 'componentObject': ManageTracks },
                        { 'Croatian': 'Upravljaj urednicima', 'componentName': 'ManageEditors', 'componentObject': ManageEditors },
                        { 'Croatian': 'Dodaj pjesmu', 'componentName': 'AddTrack', 'componentObject': AddTrack },
                        { 'Croatian': 'Upravljaj korisnicima', 'componentName': 'EditUser', 'componentObject': EditUser },
                    ]
                },
                {
                    'Croatian': 'Uredničke mogućnosti',
                    'groupName': 'EditorOptions',
                    'components': [
                        { 'Croatian': 'Pregledaj termine', 'componentName': 'MakePlaylist', 'componentObject': MakePlaylist }
                    ]
                },
                {
                    'Croatian': 'Korisničke mogućnosti',
                    'groupName': 'UserOptions',
                    'components': [
                        { 'Croatian': 'Pregledaj listu želja', 'componentName': 'MakeWishlist', 'componentObject': MakeWishlist }
                    ]
                },
                {
                    'Croatian': 'Postavke računa',
                    'groupName': 'AccountSettings',
                    'components': [
                        { 'Croatian': 'Uredi osobne podatke', 'componentName': 'AccountData', 'componentObject': AccountData },
                        { 'Croatian': 'Promijeni lozinku', 'componentName': 'AccountPassword', 'componentObject': AccountPassword },
                        { 'Croatian': 'Obriši račun', 'componentName': 'AccountDelete', 'componentObject': AccountDelete }
                        //  { 'Croatian': 'Login', 'componentName': 'Login', 'componentObject': Logi}
                    ]
                }
            ];

            for (var i = 0; i < this.navigation.length; ++i) {
                this.navigation[i].visible = true;
            }
        };
    }

header.html

<div class="header-bar">
  <div class="app-box"><a href="">
      <div class="app-name">FM Radio</div></a>
    <div class="app-descr">99.4 MHz</div>
  </div>
  <div class="user-box row">
    <div class="user-form-box">
      <form [ngFormModel]="loginForm" (onSubmit)="loginForm.value" method="post" action="/user/auth/login" class="row">
        <input type="email" id="email" placeholder="E-mail" [ngFormControl]="loginForm.controls['email']" [class.error]="!email.valid &amp;&amp; email.touched" [(ngModel)]="emailModel"/>
        <input type="password" id="password" placeholder="Lozinka" [ngFormControl]="loginForm.controls['password']" [class.error]="!password.valid &amp;&amp; password.touched" [(ngModel)]="passwordModel"/>
        <button type="submit">Prijavi se</button><a href="#register">
          <button type="button" class="dim">Registriraj se</button></a>
      </form>
    </div>
    <div class="user-name-box"><i class="material-icons user-icon">account_circle</i>
      <div class="user-name">Mirko Horvat<span class="user-type">administrator</span></div>
    </div>
  </div>
</div>

header.ts

import { Component } from 'angular2/core';
import { FORM_DIRECTIVES, COMMON_DIRECTIVES, FormBuilder, ControlGroup, Validators, Control } from 'angular2/common';

@Component({ 
    selector: 'my-header',
    templateUrl: '.dest/app/header',
    styles: [],
    directives: [ FORM_DIRECTIVES, COMMON_DIRECTIVES ] 
})

export class Header {
    // @Input() modelName
    // @Output() eventEmitterName

    loginForm: ControlGroup;

    email: Control;
    password: Control;

    emailModel: string;
    passwordModel: string;

    constructor(fb, FormBuilder) {
        this.email = new Control('', Validators.required);
        this.password = new Control('', Validators.required);

        this.loginForm = fb.group({
            'email': this.email,
            'password': this.password
        });
    }
}

回答1:

In the reality, despite the other answer, the real cause of the problems can be found in the BREAKING CHANGES section of the angular2 changelog, at version 2.0.0-alpha48:

End tags used to be tolerated for void elements with no content. They are no more allowed so that we more closely follow the HTML5 spec.

Thus, if you had a code like <example a="b" />, for example you read it in an example in the internet to an angular2 version earlier as 2.0.0-alpha48, it won't work.

But, <example a="b"></example> will work!

The angular2 developers think, they want to follow HTML5 "more closely". My opinion about what should they do, is quite different.

It is not clear, what the doc or the error message understands on "void or foreign elements". I suspect, maybe using a different html namespace for our own tags, (i.e. having a <myapp:example a="b" />) will maybe also work.



回答2:

The issue was that, of course, I had a typo. The .html at templateUrl: was missing.

I had:

@Component({ 
    selector: 'my-header',
    templateUrl: './dest/App/MyHeader',
    styles: [],
    directives: [ FORM_DIRECTIVES, COMMON_DIRECTIVES ] 
})

and I was supposed to have

@Component({ 
    selector: 'my-header',
    templateUrl: './dest/App/MyHeader.html',
    styles: [],
    directives: [ FORM_DIRECTIVES, COMMON_DIRECTIVES ] 
})


回答3:

I assume this is a bug. With one of the last alpha or beta the Angular2 parser became less forgiving. The <meta> tag is not in the list of tags that are allowed to be self-closing.

See also

  • https://github.com/angular/angular/issues/5563
  • https://github.com/angular/angular/blob/080469f8e6692c7865fbd574aadd9be06ba35c55/modules/angular2/src/compiler/html_tags.ts#L330


回答4:

Seems angular doesnt support self enclosed tags. It thows error that "Only void and foreign elements can be self closed"

As per w3 spec (https://www.w3.org/TR/html5/syntax.html)

Void elements include area, base, img, include. Foreign elements are MathML and SVG elements.



标签: angular