angular / angular

Deliver web apps with confidence 🚀
https://angular.dev
MIT License
94.7k stars 24.67k forks source link

Angular SSR not updating dynamic meta tags and content received from API #53711

Closed leandrotassi closed 1 month ago

leandrotassi commented 4 months ago

Which @angular/* package(s) are the source of the bug?

platform-server

Is this a regression?

Yes

Description

I'm working on an Angular SSR application and I'm facing a problem when updating Open Graph meta tags after receiving content from the API, as in another component that updates directly (without consuming the API) there is no problem. Although when viewing elements in the page source code, all meta tags are updated. The problem is that when sharing, they don't show the updated meta tags.

After I updated to Angular 17 it stopped working, but I'm not sure, because it was in some version that it stopped working and I had made several Angular updates.

Please provide a link to a minimal reproduction of the bug

Unfortunately, I have no way of reproducing the error for you to analyze, as the error only occurs in production and cannot be reproduced in stackblitz.

Please provide the exception or error you saw

ngOnInit(): void {
  this.metadataService.removeTags(); // Removing existing tags

  // ... (other initialization code)

  if (this.reNumber.test(sumarioId) && Number(sumarioId) > 0) {
    this.artigoService.GeArticle(sumarioId).subscribe({
      next: (res: Artigo) => {
        if (res != null) {
          this.artigo = res;
          // ... (other processing)
          this.updateMetaDataArticle();
        }
      },
      // ... (error and complete handlers)
    });
  }
}

private updateMetaDataArticle(): void {
  let meta: PageMetaData = new PageMetaData();
  // ... (other meta data setup)

  meta.title = this.artigo?.manuscriptTitle as string;
  meta.description = this.artigo?.abstract as string;

  if (this.metadataService) {
    this.metadataService.updateMetadata(meta);
    // ... (other actions)
  }
}

public updateMetadata(metadata: Partial<PageMetaData>): void {
  // ... (other metadata updates)
  this.metaTagService.addTags([
    { property: 'og:title', content: metadata.title },
    { property: 'og:description', content: metadata.description }
    // ... (other meta tags)
  ], false);
}

public removeTags(): void {
  if (!this._platform.isBrowser) return; // Avoid processing during server-side rendering

  this.metaTagService.removeTag('property="og:title"');
  this.metaTagService.removeTag('property="og:description"');
  // ... (other tag removals if relevant)
}

Please provide the environment you discovered this bug in (run ng version)

Angular CLI: 17.0.8
Node: 20.10.0
Package Manager: npm 10.2.5
OS: win32 x64

Angular: 17.0.8
... animations, cli, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, platform-server
... router, ssr

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1700.8
@angular-devkit/build-angular   17.0.8
@angular-devkit/core            17.0.8
@angular-devkit/schematics      17.0.8
@angular/cdk                    17.0.4
@angular/material               17.0.4
@schematics/angular             17.0.8
rxjs                            7.8.1
typescript                      5.2.2
zone.js                         0.14.2

Anything else?

No response

AndrewKushnir commented 4 months ago

@leandrotassi unfortunately we can't perform any investigation with the information that you've provided and we'd need a repro to see what's happening and whether there is anything on the framework side that should be fixed. You may not be able to reproduce the problem via Stackblitz (as you've mentioned), but you may try creating a new Angular app (via ng new) and try to reproduce a problem there. Note: please do not share the code of your app, create a minimal possible example code to illustrate a pattern that causes the problem.

hieutranagi47 commented 3 months ago

@AndrewKushnir: This is a link you can test: https://ssr-api-ivory.vercel.app/post/3 And this is the repo that I used to deploy into the above host: https://bitbucket.org/My_Profiles/ssr-api/src/main/ I think that we will have a solution to apply SSR in this use-case.

kodetratech commented 3 months ago

@hieutranagi47: did you find any solution for this? Is there any update on this? After upgrading to latest angular 17 version, it stopped working

hieutranagi47 commented 2 months ago

@kodetratech I don't know the root cause of the issue in this case, Angular renders the home page first, then switch to the post detail page although I access the post detail page. I guess that it's Angular issue, so I don't know what the best solution for this should be. If your page calls only one API => There is no problem.

AndrewKushnir commented 2 months ago

@hieutranagi47 thanks for creating a test application! I've tried running it locally and I can see updated meta tags in the page source once the page is SSR'ed and HTML is delivered to a browser. Could you please provide a bit more info on how to reproduce the problem locally?

hieutranagi47 commented 2 months ago

@kodetratech Thank you so much for your time. We don't face the issue when we serve the project locally. The problem occurs when we build it as production and deploy it into a server. You can check it here: https://ssr-api-ivory.vercel.app/post/3

yogeshbansal commented 2 months ago

@hieutranagi47 I am also facing the same problem. one thing I can tell something has happened after Angular version 17.0.3. I have an application that uses Angular 17.0.3, when I share the URL of my application to social media like Twitter or Facebook, it renders the dynamic meta tags correctly. But my other application which is running on angular 17.2.1, URLs are not working on social media sites. it renders the default value of the meta tags. Example of app running on 17.03 ( dynamic meta tags working) : https://contentbuffer.com/issues/news/technical/telegram-now-lets-users-convert-personal-accounts-business-accounts Example of app running on 17.2.1 ( dynamic meta tags not working): https://codebrainery.com/article/navigating-ai-revolution-software-development-chatgpt

roosikem commented 2 months ago

@hieutranagi47 I am also facing the same problem. project is working locally but in production it is not working. meta tags are not updating and not able to working in social media (not displaying correct meta tags and images, showing only default tags)

hieutranagi47 commented 2 months ago

If you notice, you will see that Angular renders the root route ("/"), before render the actual route.

roosikem commented 2 months ago

I have notice that when updating the meta tags after receiving response from the api call it is not updating but without API call it is working.

Any solution for this

monacodelisa commented 2 months ago

I am also looking for a solution, please share updates whenever possible

hieutranagi47 commented 2 months ago

@roosikem @monacodelisa @yogeshbansal : While we are waiting for the fix from the Angular core team, we could try the pre-render feature of Angular CLI. I tested it and it works. https://angular.io/guide/prerendering#prerendering-parameterized-routes

monacodelisa commented 2 months ago

@roosikem cool thank you, I am actively trying different options, I will give it a try

yogeshbansal commented 1 month ago

@hieutranagi47 @roosikem @AndrewKushnir Just wondering...Does anyone know the cause of this issue?

leandrotassi commented 1 month ago

After careful analysis of the situation, I identified that when sharing a website on my own platform, the meta tag information was not being displayed correctly. Instead, default information from the index.html file was being loaded. This issue arises due to the behavior of certain social media crawlers, which do not wait for the website to be fully rendered before collecting information. For example, Twitter is more tolerant in this regard, while Facebook or LinkedIn are not.

To address this issue, I adopted a series of strategic measures:

  1. I used resolvers to consume the API and prepare the update of meta tags in the component, as the resolver is called before the route is activated. This way, when the route is activated, the meta tags are updated immediately.
  2. In the component, I obtained the data consumed in the resolver to update the meta tags.
  3. I am using version 17.3 of Angular. I started the project from scratch with default settings and later migrated the project from the previous version to this one, using the builder "@angular-devkit/build-angular:application" which comes pre-configured. When updating the Angular version with ng update, the builder was not updated.
  4. On the Ubuntu server hosting the site, I use NGINX and switched from Forever to PM2 to run the site. Forever has not been updated for a long time, and PM2 is always up-to-date and faster.

With these measures, I was able to completely resolve the sharing problem.

I hope these tips can help those facing the same issue.

yogeshbansal commented 1 month ago

@leandrotassi thanks for the response...I am also exactly doing the same. But it's not working for me. If I share the URL on Twitter or facebook, it's still picking the default meta tags.

JeanMeche commented 1 month ago

Can anyone provide a minimal repo of this issue? I wasn't able to reproduce with the bitbucket link provided by @hieutranagi47.

hieutranagi47 commented 1 month ago

@JeanMeche We don't face the issue in the local environment. Please deploy it on a server (Production development) then you can see the issue. I have deployed it on Vercel here: https://ssr-api-ivory.vercel.app/post/3

JeanMeche commented 1 month ago

I did build the app and run the dist locally without being able to reproduce your issue.

JeanMeche commented 1 month ago

@hieutranagi47 Your issue is that you're not using SSR but prerendering (SSG). (ng-server-context="ssg" attribute in the DOM) Check the output build and particularly prerendered-routes.json, it contains only

{
  "routes": [
    "/",
    "/post"
  ]
}

You app isn't prerendering each post entry which explains why the meta still contains the placeholder.

monacodelisa commented 1 month ago

@JeanMeche thank you for your comments, I wanted to ask something that I am not sure about - as far as I understand, right now both Netlify and Vercel only support SSG and do not fully support SSR - since for SSR you need to be running a Node server on a Node hosting environment like Render, Cyclic, AWS Lambda and so on ? Or can SSR be achieved fully on Netlify / Vercel ?

JeanMeche commented 1 month ago

@monacodelisa You can deploy on Vercel serverless and Netlify Edge.

monacodelisa commented 1 month ago

@JeanMeche cool, thank you

hieutranagi47 commented 1 month ago

@JeanMeche: Can you give me more info about this issue. From the angular.json configure, when we set pretender: true, it means that Angular CLI will pre-render all available routes inside the project, and the others will run as client-side rendering if we deploy the project on a static server (ie: Vercel - I am not sure about it, AWS S3, ...). To make sure that the project will be run as SSR, we have to deploy it into a node server (ie: Vercel Serverless)?

JeanMeche commented 1 month ago

Yes this is what is hapeing in your case, CSR is what currently renders your posts . You need a node server to run SSR.

hieutranagi47 commented 1 month ago

Thank you so much for the great support!

leandrotassi commented 1 month ago

@yogeshbansal Hmm, that's strange, but there's one thing I forgot to mention: I believe implementing the bond strategy is also important. In the "app route" there is a title attribute to update the page title. If the title is dynamic, you can also use the resolver.

Could it be that the server that hosts your website is not influencing sharing behavior?

Did you make sure that the builder is "@angular-devkit/build-angular:application"?

Because it was these sets of measures, which I mentioned earlier, that made sharing work.

yogeshbansal commented 1 month ago

@leandrotassi: I have these 2 applications both are running on the same setup and server. One is working and other one is not. Example of the app running on 17.03 ( dynamic meta tags working) : https://contentbuffer.com/issues/news/technical/telegram-now-lets-users-convert-personal-accounts-business-accounts Example of app running on 17.2.1 ( dynamic meta tags not working): https://codebrainery.com/article/navigating-ai-revolution-software-development-chatgpt

codewithbisky commented 1 month ago

This is how I solved this. I created a ItemResolver and added it into my routes

`import { Injectable } from '@angular/core'; import { Resolve, ActivatedRouteSnapshot } from '@angular/router'; import {ItemService} from "../service/item.service";

@Injectable({ providedIn: 'root' }) export class ItemResolver implements Resolve { constructor(private dataService: ItemService) {}

async resolve(route: ActivatedRouteSnapshot) {
    const id = route.paramMap.get('id') ?? '';
    return await this.dataService.getItemPublicPromise(id);
}

}`

This is route configuration

{path: 'view-video/:id', component: ViewVideoPageComponent, resolve: { resolvedData: ItemResolver }},

I did this in onNit of ViewVideoPageComponent

`imageUrl: string = ''; item: any ; ngOnInit(): Promise { this.item = this.resolvedData = this.route.snapshot.data['resolvedData']; this.imageUrl = your_domain+this.item.imagePath;

    this.updateMeta();

}

private updateMeta() {

    this.ngxSeoService.setSeo({
        title: this.item.title,
        meta: {
            description: this.item.metaDescription,
            image: this.imageUrl,
            type:"website"
        },
    });
}
`
I am using this dependency
"@avivharuzi/ngx-seo": "^16.0.0",

Everything is working. It is not showing the thumbnail on whatsapp. If you solve whatsapp let me know

Example of this is this one https://www.codewithbisky.com/view-video/2b3fae4d-8e75-4d50-b9b0-e50857e2e83c

yogeshbansal commented 1 month ago

@codewithbisky This is how your link is looking on twitter when you try to post it. see the attached image Screenshot 2024-03-30 at 10 16 00 AM

codewithbisky commented 1 month ago

Twitter cached that image when I was testing it. You can try other urls on my platform.

On Sat, 30 Mar 2024, 18:18 Yogesh Bansal, @.***> wrote:

@codewithbisky https://github.com/codewithbisky This is how your link is looking on twitter when you try to post it. see the attached image Screenshot.2024-03-30.at.10.16.00.AM.png (view on web) https://github.com/angular/angular/assets/4295712/e8deb56e-815c-4757-8ec2-89df066686bf

— Reply to this email directly, view it on GitHub https://github.com/angular/angular/issues/53711#issuecomment-2028317743, or unsubscribe https://github.com/notifications/unsubscribe-auth/BCC6WIWBSRFCDQBVTSDLALDY23XU3AVCNFSM6AAAAABBEZVDFWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAMRYGMYTONZUGM . You are receiving this because you were mentioned.Message ID: @.***>

codewithbisky commented 1 month ago

This is an example.

On Sat, 30 Mar 2024, 18:25 Marshall Chabaga, @.***> wrote:

Twitter cached that image when I was testing it. You can try other urls on my platform.

On Sat, 30 Mar 2024, 18:18 Yogesh Bansal, @.***> wrote:

@codewithbisky https://github.com/codewithbisky This is how your link is looking on twitter when you try to post it. see the attached image Screenshot.2024-03-30.at.10.16.00.AM.png (view on web) https://github.com/angular/angular/assets/4295712/e8deb56e-815c-4757-8ec2-89df066686bf

— Reply to this email directly, view it on GitHub https://github.com/angular/angular/issues/53711#issuecomment-2028317743, or unsubscribe https://github.com/notifications/unsubscribe-auth/BCC6WIWBSRFCDQBVTSDLALDY23XU3AVCNFSM6AAAAABBEZVDFWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAMRYGMYTONZUGM . You are receiving this because you were mentioned.Message ID: @.***>

quedicesebas commented 1 month ago

@codewithbisky works with Angular 17?

codewithbisky commented 1 month ago

Yes, it works with angular 17

On Wed, 3 Apr 2024, 01:37 Sebastián Rojas Ricaurte, < @.***> wrote:

@codewithbisky https://github.com/codewithbisky works with Angular 17?

— Reply to this email directly, view it on GitHub https://github.com/angular/angular/issues/53711#issuecomment-2033284528, or unsubscribe https://github.com/notifications/unsubscribe-auth/BCC6WIWBIFKUVIQOS2DI3QTY3M6NNAVCNFSM6AAAAABBEZVDFWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAMZTGI4DINJSHA . You are receiving this because you were mentioned.Message ID: @.***>

quedicesebas commented 1 month ago

Alerady using it :)

liamong0422 commented 1 month ago

@codewithbisky I noticed that the content on the page you've shared wasn't loaded from API. I've tried your solution but it doesn't work either, I'm on Angular 17.3.3

quedicesebas commented 1 month ago

@liamong0422 if you need to load meta-data from an API, you should use resolvers:

const title = 'My Pizza place';
const seoResolver: ResolveFn<NgxSeo> = (route, state) => {
  return inject(MenuService)
    .getItemByUUID(route.params['itemUUID'])
    .pipe(
      map((item: MenuItem) => {
        return {
          title: route.params['itemUUID'] ? item.name : 'Menú | ' + title,
          meta: {
            description: route.params['itemUUID']
              ? item.description
              : 'Best pizza in town',
            image: item.imageUrl,
            type: 'website',
            url: 'https://mypizzaplace.com' + state.url,
          },
        };
      }),
    );
};

export const MENU_ROUTES: Routes = [
  {
    path: ':itemUUID',
    component: MenuComponent,
    resolve: { seo: seoResolver },
  }
  {
    path: '',
    component: MenuComponent,
    resolve: { seo: seoResolver },
  },
];
liamong0422 commented 2 weeks ago

@liamong0422 if you need to load meta-data from an API, you should use resolvers:

const title = 'My Pizza place';
const seoResolver: ResolveFn<NgxSeo> = (route, state) => {
  return inject(MenuService)
    .getItemByUUID(route.params['itemUUID'])
    .pipe(
      map((item: MenuItem) => {
        return {
          title: route.params['itemUUID'] ? item.name : 'Menú | ' + title,
          meta: {
            description: route.params['itemUUID']
              ? item.description
              : 'Best pizza in town',
            image: item.imageUrl,
            type: 'website',
            url: 'https://mypizzaplace.com' + state.url,
          },
        };
      }),
    );
};

export const MENU_ROUTES: Routes = [
  {
    path: ':itemUUID',
    component: MenuComponent,
    resolve: { seo: seoResolver },
  }
  {
    path: '',
    component: MenuComponent,
    resolve: { seo: seoResolver },
  },
];

I'm already using the resolver but I am still experiencing the same issue. The website will load the home page first, then to the details page. But for pages that doesn't load API, it will go straight to the page and the metatags were successfully loaded in the page source.

quedicesebas commented 2 weeks ago

Yes, is not working :(