28

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
        });
    }
}
sainu
  • 2,686
  • 3
  • 22
  • 41
ditoslav
  • 4,563
  • 10
  • 47
  • 79

4 Answers4

51

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.

peterh
  • 11,875
  • 18
  • 85
  • 108
  • re 'not clear' - it is defined in HTML5, see https://stackoverflow.com/a/53916062/717732 for examples, see https://github.com/angular/angular/issues/5563#issuecomment-161600091 for full list of void elements from HTML5 and other information about the decision – quetzalcoatl Jan 17 '19 at 14:03
  • 1
    @quetzalcoatl The popularity of the Angular recently [started to decrease](https://data.stackexchange.com/stackoverflow/query/1032108/tag-popularity-history?TagName=angular#graph). I don't think it happened only because this decision. I think, it happened because many similar actions of the Angular development. I am really sorry for that, probably the Angular will be in 5 years only a bad dream. – peterh May 24 '19 at 13:28
  • 10% drop isn't that much, and angular already had some drop early in 2018. Also note that 'angular' term is somewhat ambiguous due to old-vs-new schizm, and often people use 'ng' instead (ngrx, ngfoobar, etc instead of angular-foobar like earlier). I think we could feel the decline when it reaches ~25-33% drop. Also, note that even on this page you linked to, angular's history starts in 2016. That is, 2-2.5 years ago. So yeah, in 5 years (2x that time) it may be dead :):):) Btw. I saw the "peterh - PLEASE DELETE THIS ACCOUNT" I dont know when it was, but you should contact the support for this – quetzalcoatl May 24 '19 at 14:38
  • 1
    just to be clear - I dont like angular that much.. there's awful lot of things that could be done at least a bit better. But it certainly is MUCH better than before! – quetzalcoatl May 24 '19 at 14:39
  • @quetzalcoatl I already contacted them, but if you see also the profile, you can also see, that I also asked for a merge to my previous account. Their last answer was that they don't have the required developer resources for that merge (probably the SEDE core has no user merge feature, a developer should do it by hand). But they will do it when then will have, this is what I am waiting for. – peterh May 24 '19 at 15:48
  • @quetzalcoatl It is possible, but I still have some distaste if I need to download 400 javascripts in 6MB of length to create some material gui. I still have some distaste that it is hard to make an error-free js console output in angular. Btw, why the heck they don't support google clojure compiler since ages, from the beginning? It could reduce the crap overhead to some hundred kBytes, it would be already tolerable. I can only hope, it is not because the google closure compiler is developed by *another* Google team... – peterh May 24 '19 at 15:51
  • @quetzalcoatl About the stat: most technologies have 3 phases of their life. 1) they have an initial, exponentially growing phase 2) a following, linear phase 3) an exponential decay. Angular, this young technology, is now between (2) and (3). My interpretation of this stat, including my mainly negative experiences, can be summarized in only a single word: **failure**. – peterh May 24 '19 at 15:55
7

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 ] 
})
ditoslav
  • 4,563
  • 10
  • 47
  • 79
2

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

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
2

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.

crazyCoder
  • 329
  • 1
  • 2
  • 16