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

create universal services #309

Closed
1 of 7 tasks
PatrickJS opened this issue Mar 7, 2016 · 30 comments
Closed
1 of 7 tasks

create universal services #309

PatrickJS opened this issue Mar 7, 2016 · 30 comments

Comments

@PatrickJS
Copy link
Contributor

PatrickJS commented Mar 7, 2016

blocked via angular/angular#7455

  • NgStyleHost directive to use NodeDomSharedStylesHost added via e8befd4
    renamed via d804d7a
  • NgTitle directive to use Title service
  • NgMeta directive to use MetaTag Service
  • NgBase directive to use BaseUrl service
  • NgModule directive to precompile inline scripts
  • NgScript directive to precompile inline ts scripts
  • NgPreboot directive for precompiled client preboot
@PatrickJS
Copy link
Contributor Author

we're waiting for the next next release

@PatrickJS
Copy link
Contributor Author

<universal-styles> was added

@PatrickJS
Copy link
Contributor Author

I'm adding NgPreboot

@jeffwhelpley
Copy link
Contributor

@gdi2290 What is the status here? Does it make sense to have so many different things in one issue? As we discussed, we should create a new module under Universal with all the common services people will need. I am modifying the title for this issue to reflect that.

@jeffwhelpley jeffwhelpley changed the title create index.html directives create universal services May 6, 2016
@megalepozy
Copy link

I'm not sure if it's the right place to ask for it but is there something like NgLink for setting canonical links? it's badly needed for SEO

@jeffwhelpley
Copy link
Contributor

@megalepozy I agree with this. We should have one of the universal services that automatically sets the canonical link in the head. Also, related is i18n href lang tags.

@jeffwhelpley
Copy link
Contributor

As posted by @Quixomatic, the workaround for now is:

import { Injectable, Inject, ElementRef, Renderer } from '@angular/core'
import { DOCUMENT } from '@angular/platform-browser'

@Injectable()
export class InterfaceService {    
    //--------------------------------------------
    //! Set the variables to their default values
    //--------------------------------------------
    constructor (@Inject(DOCUMENT) private document) {
    }

    //----------------------------
    //! Set the title of the page
    //----------------------------
    setTitle (title: string) {
        this.document.title = title
    }
}

It will still be good to create these universal services, but this workaround is pretty simple and should cover setting the title.

@threesquared
Copy link

Is there currently a way to create a new DOM element?

@jeffwhelpley
Copy link
Contributor

@threesquared you would do this through normal Angular components. Are you thinking about trying to use the lower level DOM api? If so, that won't work. What is the specific use case where you can't just use a normal component?

@threesquared
Copy link

The use case is the same as the above but to also create any other meta tags which are required like opengraph data so they can be rendered on the server side. At the moment I am rather hackily accessing getDOM() via __platform_browser_private__ in @angular/platform-browser...

@jeffwhelpley
Copy link
Contributor

Yeah, so meta tags will be part of the service yet to be created. We will update this thread when it is available through the universal api.

@Hendrixer
Copy link

Awesome @jeffwhelpley , turns out me and @gdi2290 need this right now 💯 , so we might just hop on it...

@jeffwhelpley
Copy link
Contributor

Nice, that would be awesome. HtmlComponent is part of this as well. We have a Universal mtg tomorrow so can talk about it then on the hangout.

@johnnysainz
Copy link

Using the @Quixomatic workaround provided by @jeffwhelpley, I'm updating my meta descriptions with the following:

public setMetaDescription (desc: 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 = desc;
            }   
        }
    }

Unfortunately this only works for the server, not the browser. Surely there is a better solution out there that works in both?

@jeffwhelpley
Copy link
Contributor

@johnnysainz usually meta tags only matter on the server side. Can you give me some more details about your use case where you think that it is needed on the client side?

@johnnysainz
Copy link

@jeffwhelpley i'm not very experienced with SEO. My understanding is (was) that meta descriptions need to be updated while navigating--same as title. If this is not the case, then thank you for saving me some time! And thanks for Universal!!

@jeffwhelpley
Copy link
Contributor

@johnnysainz typically the meta tags are either used by Google when crawling your site (in which case they make separate server side requests for each URL) or site preview within Twitter/Facebook/LinkedIn/etc. In all of those cases, the only thing that matters is the server side.

Just trying to brainstorm a bit here, I think the only reason I can think the meta tags would be useful on the client side is when you have a browser plugin that utilizes them for whatever reason (organizing your bookmarks?). In any case, for the important business reasons, you most likely don't need to care about meta tags on the client side.

@threesquared
Copy link

As a pretty hacky solution I mentioned above you can access getDOM() as I have done here which works at the moment...

@PatrickJS
Copy link
Contributor Author

closing as <html> component isn't supported by angular anymore

@threesquared
Copy link

@gdi2290 @jeffwhelpley mentioned above that this thread would be updated with details of a meta tag service?

@samvloeberghs
Copy link

@threesquared do you mind if I use your implementation for now? Works like a charm :)

@PatrickJS
Copy link
Contributor Author

@threesquared please see PR angular/angular#12322 for more details on the Meta Service

@threesquared
Copy link

@gdi2290 thanks!
@samvloeberghs of course.

@marklonquist
Copy link

marklonquist commented Nov 30, 2016

The suggestion in #309 (comment) did not work for me for setting the title serverside. I'm getting a 'data' is undefined error.

Works fine if I use it client-side.

@ilDon
Copy link

ilDon commented Jan 7, 2017

Not sure if still of interest, but putting together all suggestions found here and around, I've come up with a solution that is working well for me:

import { Component, AfterViewInit, Renderer, Inject } from '@angular/core';
import { DOCUMENT } from '@angular/platform-browser';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements AfterViewInit {
  constructor(@Inject(DOCUMENT) private document: any, private renderer: Renderer) {}

  ngAfterViewInit(): void {
    let headChildren = this.document.head.children;
    for (let i = 0; i < headChildren.length; i++) {
      let element = headChildren[i];
      if(element.name === 'title') this.renderer.setText(element, 'App title');
      if(element.name === 'meta' && element.attribs.translatestring) this.renderer.setElementAttribute(element, 'content', 'I am a translated string');
    }

    // To add new meta
    const elem = this.renderer.createElement(this.document.head, 'meta');
    this.renderer.setElementProperty(elem, 'name', 'foo');
  }
}

If you need this also client side you can simply transform this into a service, and then call it each time the route changes, then use this.renderer.setElementAttribute to change the attribute content to the right strings (e.g. from a constant with all the values based on the route).

@DBuit
Copy link

DBuit commented Feb 1, 2017

@ghego1 i tried to use your for loop to set meta tags in the InterfaceServer

import { Injectable, Inject, ElementRef, Renderer } from '@angular/core'
import { DOCUMENT } from '@angular/platform-browser'

@Injectable()
export class InterfaceService {    
    //--------------------------------------------
    //! Set the variables to their default values
    //--------------------------------------------
    constructor (@Inject(DOCUMENT) private document) {
    }

    //----------------------------
    //! Set the title of the page
    //----------------------------
    setTitle (title: string) {
        this.document.title = title
    }
}

But im gettings this error: "No provider for ElementRef!", can you help me?

@ilDon
Copy link

ilDon commented Feb 1, 2017

@DBuit, I think the problem is that you are doing this in a Service. I implemented this in a component, and then passed to the service the rendered object.

I have not yet studied how Angular2 works behind the scenes so I'm unable to provide you a full description of why this can't be delegated entirely to a Service, but I can share with you the following working code :-)

Component:

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

import { MetaSetterService } from './wherever/meta-setter.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements AfterViewInit {

  constructor(private metaSetter: MetaSetterService, public renderer: Renderer) { }

  ngAfterViewInit(): void {
      this.metaSetter.setAll(this.renderer);
  }

}

And then the service:

import { Injectable, Inject } from '@angular/core';
import { DOCUMENT } from '@angular/platform-browser';

@Injectable()
export class MetaSetterService {

  constructor(@Inject(DOCUMENT) private document: any) {}

  private renderer: any;

  public setAll(renderer: any): void {
    this.renderer = renderer;
    this.document.head.children.forEach( element => this.recurseAllHeadNodes(element) );
  }

  private recurseAllHeadNodes(element: any): void {
    if(element.name === 'title') this.renderer.setText(element, 'myTitle');
    else if(element.name === 'meta') this.renderer.setElementAttribute(element, 'content', 'myMeta');
  }

}

@jrmcdona
Copy link

jrmcdona commented Aug 1, 2017

Does this work well with dyanmic link and script tags from an external service?

@raviguru123
Copy link

raviguru123 commented Nov 17, 2018

hi @ilDon, your solution does not work when we open view page source angular (SSE), can you please give support with angular SSE as well,
i am using this for adding canonical URl

@angular-automatic-lock-bot
Copy link

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.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 4, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests