Skip to content
This repository was archived by the owner on Nov 22, 2024. It is now read-only.
This repository was archived by the owner on Nov 22, 2024. It is now read-only.

<html> component #533

Closed
Closed

Description

@PatrickJS

document and write about maintaining an html component as the document

html.module.ts

@NgModule({
    bootstrap: [ HtmlComponent ],
    declarations: [ HtmlComponent ],
    imports: [
        UniversalModule,
        FormsModule
    ]
})
export class MainModule {}

html.template.ts

<html>
<head>
  <title></title>
</head>
<body>
  <app></app>
</body>
</html>

index.html

<!DOCTYPE html><html></html>

Activity

  1. FrozenPandaz commented on Sep 18, 2016

    @FrozenPandaz
    Contributor

    Scripts aren't rendering again. That will be a blocker for this.

  2. cipriansorlea commented on Sep 23, 2016

    @cipriansorlea

    This prevents any website built with Universal to be SEO friendly and have custom titles and meta tags. Any clue when this would be available? In RC4 I was able to get Universal running and be SEO friendly, but with the GA version, that seems to no longer work.

  3. jeffwhelpley commented on Oct 3, 2016

    @jeffwhelpley
    Contributor

    @jeffbcross this is a super high priority issue that we need to talk about this week

  4. FrozenPandaz commented on Oct 13, 2016

    @FrozenPandaz
    Contributor

    UNIVERSAL_CACHE doesn't make it onto the page when using a HTML Component after 2.1.0-rc.1

  5. PatrickJS commented on Oct 13, 2016

    @PatrickJS
    ContributorAuthor

    the problem here is that we have a DOM created from index.html and a new one created via the <html> component. When attaching our script to the dom we were previously adding it to the document itself which places it after the <html> tag. Now we're adding it in the body tag but we're referencing the old body tag rather than the one created by the <html> component

  6. FrozenPandaz commented on Oct 14, 2016

    @FrozenPandaz
    Contributor

    I feel like we should generate the document with a HTML Component of sorts.

    The document should start off with the implementer's index.html but then be compiled by hooking into services provided to the implementer.

    It should work off of a variety of services:

    • Title Service (Angular already has one, we just need to make it universal?)
    • Meta Service should add custom metas
    • Assets Service should add script and css tags
      • This should allow for inline tags as well
      • EDIT: This should handle onloads of server created assets for client side code (Universal onload)
    • Doctype Service should edit the kind of html it is. AMP for example requires a <html ⚡> tag instead of the traditional <html>.

    In the browser, these services should do their javascript equivalents.

    In execution, if we could find away to wrap around the user's app it would just be a component containing the implementer's app and this should be rather straightforward.

    Otherwise it might be something like:

    1. These Services are provided to the app. Starts off with what the index.html contains. This should allow the implementer to very naturally transition from a client only Ng2 app.
    2. During the execution of the app, it will just keep a state of what should be on the page.
    3. Then finally onStable the component should pull from these services to generate the final document.

    I have no idea how practical that sounds but hopefully it helps.

  7. u12206050 commented on Oct 20, 2016

    @u12206050

    Any updates on this "high important priority"? We have a production site ready for launch and the only thing we are missing is SEO, (being able to render meta tags server side).

    We have a working meta service that does it correctly on the browser side, (obviously useless though) and I have experimented with all the various renders and getDom methods I could find in angular, but like @gdi2290 mentioned the references I get seem to not be the same one ending up being rendered.

    Any help will be greatly appreciated.

  8. jeffwhelpley commented on Oct 20, 2016

    @jeffwhelpley
    Contributor

    @u12206050 Sorry about the delay in updates on this. We are meeting to discuss status of all high priority items tomorrow and I will provide some info on this and all other issues after that.

  9. zcoon commented on Oct 22, 2016

    @zcoon

    Hi @jeffwhelpley do you have any updated info for us after your meeting yesterday?

  10. jeffwhelpley commented on Oct 23, 2016

    @jeffwhelpley
    Contributor

    The solution for this issue is going to be a new SeoService similar to setTitle that will allow you to make any of the modifications you want to the head section of the document. @gdi2290 will likely get an additional prototype into the universal starter to get everyone started and then we will get something into universal and/or core (TBD) later on. So, watch the starter repo.

  11. kuldeepkeshwar commented on Oct 23, 2016

    @kuldeepkeshwar

    @gdi2290 @jeffwhelpley can you share any gist which we can use as temparory solution as most of us are very close to the production release.

  12. kuldeepkeshwar commented on Oct 23, 2016

    @kuldeepkeshwar

    as of now I'm using below solution

    import { Injectable, Inject } from '@angular/core';
    import {  DOCUMENT } from '@angular/platform-browser';
    import { SeoMetaData } from "./../../model/seo-meta-data";
    @Injectable()
    export class SEOService {
        constructor( @Inject(DOCUMENT) private document: any)  {}
        public setData(metaData: SeoMetaData): void {
            this.setMeta(metaData.title,metaData.description);
        }
        private setMeta(title: string = '', description: string = '') {
            this.setTitle(title);
            this.setMetaDescription(description);  
        }
    
        private setTitle(title: string) {
            this.document.title = title;
        }
    
        private setMetaDescription(description: string) {
            let headChildren = this.document.head.children;
            for (let i = 0; i < headChildren.length; i++) {
                let element = headChildren[i];
    
                if(element.name === 'meta' && element.attribs.name === 'description'){
                    element.attribs.content = description;
                }
            }
        }
    }
    
    
  13. u12206050 commented on Oct 24, 2016

    @u12206050

    For any one else wanting a full service to set meta tags incl social:

    Include the following in the head of index.html

    <meta name="title" content="">
      <meta name="og:title" content="">
      <meta name="description" content="">
      <meta name="og:description" content="">
      <meta name="keywords" content="">
      <meta name="og:url" content="">
      <meta name="og:image" content="">
      <meta name="og:site_name" content="">
      <meta name="article:published_time" content="">
      <meta name="article:modified_time" content="">
      <meta name="article:tag" content="">
      <meta name="twitter:card" content="">
      <meta name="twitter:site" content="">
      <meta name="twitter:creator" content="">
      <meta name="og:type" content="">
      <meta name="fb:app_id" content="">
    

    and here is the entire service, you can call updateMetaTags(meta: object) with an object of key (meta name), value (meta content) pairs:

    import { Injectable, Inject } from '@angular/core';
    import { DOCUMENT } from '@angular/platform-browser';
    
    declare var global: any;
    
    @Injectable()
    export class SEOMetaService {
      metaTags: any[];
    
      constructor(@Inject(DOCUMENT) private document: any) {
        this.metaTags = this.document.head.children;
      }
    
      public updateMetaTags(meta: any = {}) {
        if (!this.document) return null;
        if (meta.disableUpdate) {
          return false;
        }
        try {
          this.setTitle(meta.title, meta.titleSuffix);
    
          Object.keys(meta).forEach(key => {
            if (key === 'title' || key === 'titleSuffix')
              return;
    
            this.setMetaTag(key, meta[key]);
            if (key == 'description')
              this.setMetaTag('og:description', meta[key]);
          });
        } catch(err) {
          console.log(err);
        }
      }
    
      private setTitle(title?: string, titleSuffix?: string) {
        let titleStr = title || '';
        if (titleStr.indexOf(titleSuffix) < 0)
          titleStr += (!!titleSuffix ? ' | '+titleSuffix : '');
        this.setMetaTag('title',titleStr);
        this.setMetaTag('og:title',titleStr);
        this.document.title = titleStr;
      }
    
      private setMetaTag(name: string, value: string) {
          let len = this.metaTags.length;
          for (let i = 0; i < len; i++) {
              let element = this.metaTags[i];
    
              if(element.name === 'meta' && element.attribs.name === name){
                element.attribs.content = value;
                break;
              }
          }
      }
    }
    
  14. jeffbcross commented on Oct 24, 2016

    @jeffbcross
    Contributor

    After some discussion, we decided not to support the HTML Component feature as it was previously implemented. We'll be working on making sure the Title service works in Universal, and will add a similar Meta service to manage meta tags. There's already a good start on the Meta service for platform-browser, which would be ported to Universal: angular/angular#12322 Please add comments there if you have a meta tag use case that wouldn't be covered by that service.

  15. 7 remaining items

  16. pxwise commented on Oct 26, 2016

    @pxwise

    I see, so in order to have feature parity with react-helmet we would need services for <script> <link> and <html> attributes in addition to the <title> and <meta> services.

    I agree it would be cool to declaratively build the document as a component. Easy to visualize, all markup generated through the same template engine. Maybe we can lobby to reopen this feature request for a future major version.

  17. jeffwhelpley commented on Oct 26, 2016

    @jeffwhelpley
    Contributor

    @pxwise so, for the html element, do you foresee needing to change anything other than the lang for i18n sites?

    What would be the use case for the link and script tags? I would think those would be static from one request to another, no?

  18. pxwise commented on Oct 26, 2016

    @pxwise

    @jeffwhelpley for <html> We will only need lang and ⚡

    Yes many link and script tags would be static but some use cases for dynamic would be:

    <link>

    • rel="canonical"
    • rel="dns-prefetch", based on underlying api, 3rd party site dependencies, per page
    • css, needed as stopgap while we migrate legacy design to 100% angular embedded styles

    <script>

    I am looking at this need from the initial server generated page load, not on route change which is more pertinent to title and meta. Ideally we have one html to maintain for the entire site and configure upstream at the CMS. Otherwise we'd require new universal deploys when we add / edit / delete pages with varying link and script tags.

  19. jeffwhelpley commented on Oct 26, 2016

    @jeffwhelpley
    Contributor

    Got it. OK, we are talking about the MetaService with @jeffbcross and I will be sure to include all of these in the discussion.

  20. jeffbcross commented on Oct 28, 2016

    @jeffbcross
    Contributor

    Hey @pxwise thanks for more use cases. I'd like to determine how these cases depend on dynamic data within Angular, to understand better if there should be support within Angular. In other words, why must Angular solve this problem instead of a template engine or third-party library? I'd assume a lot of those use cases just need to know the URL of the page in order to know what values to populate those link and script tags with.

    And just to give background on why I'm pushing back so much on this feature; I do see the convenience of having first-class support for html documents. It requires thoughtful design and security review to implement and maintain such functionality, since Angular was not designed to support this.

  21. jeffbcross commented on Oct 28, 2016

    @jeffbcross
    Contributor

    Sorry, my last comment ignored some of the intermediary comments, I thought we were still talking about the context of the HTML Component.

  22. pxwise commented on Oct 28, 2016

    @pxwise

    Howdy @jeffbcross. Some cases above that could use data from angular components would be:

    • <link> dns-prefetch to automatically prefetch domains used by components making api requests.
    • <script> JSON-LD schema.org definitions for a recipe site on a dynamically fetched route /recipe/node/123
    • <script> include / exclude ad script based on user authorization

    The other tags could be populated with a separate config object, mapping url path -> page state values, driven by a node template engine.

    I still think an HTML Component would be a nice to have allowing for a simple mental model to construct dynamic pages but understand how this introduces significant expansion of scope for angular. I guess it's a matter of determining angular's boundaries as a server side templating language.

    The intent of my initial response was requesting guidance for creating a 3rd party module to serve as an angular HTML component or service as a supplement to angular universal, to avoid the need for a node templating engine if this feature were not available in core.

  23. PatrickJS commented on Oct 31, 2016

    @PatrickJS
    ContributorAuthor

    @pxwise any feedback from trying out UniversalAd?

  24. pxwise commented on Oct 31, 2016

    @pxwise

    @gdi2290 yes I got it working, thanks for the gist. I'll ping you later today to review what I have, made some changes.

  25. u12206050 commented on Nov 1, 2016

    @u12206050

    @zcoon sorry for the late reply, but yes it seems to work great. We have some extra checks happening somewhere else making sure we attach the correct image to use on social websites though.

  26. yazeedb commented on Nov 3, 2016

    @yazeedb
    Contributor

    @u12206050 This solution looks great! Is there a way to dynamically create the meta and inject it into the document head server-side? I've tried the following:

    let meta_tag = getDOM().createElement('meta');
    DOM.setAttribute(meta_tag, 'name', meta.name);
    getDOM().appendChild(this.document.head, meta_tag);

    But it seems to break my client-side render if I tamper with the document. Any ideas why?

  27. jrmcdona commented on Aug 2, 2017

    @jrmcdona

    @gdi2290 is it possible to use the ejs engine with angular? All I need ejs for is for the layout (header) because I do have custom script and link tags because mine are dynamic. This very easily solved with templating but when mixing it in with Angular I don't see how it would work given I only want to use it for the layout.

    @pxwise how did you solve for dynamic link and script tags?

  28. PatrickJS commented on Aug 2, 2017

    @PatrickJS
    ContributorAuthor

    you can mix them, yeah

  29. angular-automatic-lock-bot commented on Sep 4, 2019

    @angular-automatic-lock-bot

    This issue has been automatically locked due to inactivity.
    Please file a new issue if you are encountering a similar or related problem.

    Read more about our automatic conversation locking policy.

    This action has been performed automatically by a bot.

  30. locked and limited conversation to collaborators on Sep 4, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions