Обновление мета-тегов в angular universal с внешним вызовом API
Я провел более 2 месяцев, но не смог найти четкого решения, как поступить с угловым универсалом. Я уже потратил около 6 месяцев на внедрение Angular Universal в проекте, на который у меня не так много времени, и теперь я застрял в этой проблеме. Может ли кто-нибудь помочь мне с этим, так как кажется, что весь мир хочет знать решение для Angular SSR.
Вот мой код (служба мета-тегов):
import {Injectable} from '@angular/core';
import { Meta, Title } from '@angular/platform-browser';
import {commonMetas} from './meta-data.model';
@Injectable()
export class SeoService {
public commonMetas = commonMetas;
constructor(public meta: Meta, public title: Title) {}
setAutomatically (key = 'none') {
const detail = this.commonMetas[key];
/** setting defaults */
this.setTitle(detail.title);
this.setAuthor();
this.setDescription(detail.description);
this.setKeywords(detail.keyword);
}
setFromJson(key: {
title: any,
description: any,
image: any,
keyword: any,
author: any
}) {
key.title = key.title ? key.title : this.commonMetas['none'].title;
key.description = key.description ? key.description : this.commonMetas['none'].description;
}
setTitle(titleToSet = '') {
this.title.setTitle(titleToSet);
}
setAuthor (nameToSet = '') {
this.meta.addTag({ name: 'author', content: 'havemybooks.com'});
}
setKeywords (keyword = '') {
this.meta.addTag({ name: 'keywords', content: keyword});
}
}
}
И мой компонент.
ngOnInit() {
this.sub = this.route.params.subscribe(params => {
this.id = +params['id'];
this.api.getParticular({id: this.id}).subscribe((response) => {
this.content = response.blog_data[0];
this.content.main_image = this.getImage(this.content.main_image);
this.content.metaCreatedAt = moment(this.content.created_at).format('YYYY-MM-DD');
this.content.displayCreatedAt = moment.utc(new Date(this.content.created_at)).fromNow();
this.content.name = this.handleName(this.content.name);
this.seo.setFromJson({
title: this.content.title,
image: this.content.main_image,
description: this.content.blog,
author: this.content.name,
keyword: ''
});
});
});
}
Вот несколько связанных вопросов Stackru и проблем GitHub:
Угловой универсальный серверный рендеринг, метатеги
Обновление мета-тегов для SEO с использованием универсального углового
Угловой универсальный + внешний API
https://github.com/fulls1z3/ngx-meta/issues/101
Angular Universal - метатеги OG не работают с дочерними маршрутами
https://github.com/fulls1z3/ngx-meta/issues/118(I пытался получить помощь от кого-то, кто успешно реализовал, но не получил помощь)
https://github.com/maciejtreder/ng-toolkit/issues/460 (я его открыл)
Список можно продолжить. Я видел ряд обсуждений, которые так и не были завершены. Любой, кто может подсказать, как сделать вызов API перед рендерингом в ng-express.
Я реализовал SSR и использую тег ngx-meta, но fb crawler показывает метатеги по умолчанию, которые я использовал в теге head.
5 ответов
Не удалось найти простой способ сделать это, но вот временное / хакерское решение, предоставленное здесь пользователем GitHub, и я цитирую его ответ напрямую:
//all imports
enableProdMode();
export const app = express();
const mysql = require('mysql');
const httpRequest = require("request");
// all other consts
app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [
provideModuleMap(LAZY_MODULE_MAP)
]
}));
//all other piece of code in server.ts
app.get('/*', (req, res) => {
res.render('index', {req, res}, (err, html) => {
if (html) {
//console.log(html);
// This is where you get hold of HTML which "is about to be rendered"
// after some conditional checks make a HTTP call
let url = 'http://....';
httpRequest.get(url, (error, response, body) => {
if(error) {
return console.log(error);
}
const respBody = JSON.parse(body);
if(respBody){
html = html.replace(/\$TITLE/g, respBody.title);
html = html.replace(/\$DESCRIPTION/g, respBody.description);
html = html.replace(/\$OG_META_KEYWORDS/g, respBody.metaKeywords);
html = html.replace(/\$OG_META_DESCRIPTION/g, respBody.metaDescription);
html = html.replace(/\$OG_DESCRIPTION/g, respBody.ogDescription);
html = html.replace(/\$OG_TITLE/g, respBody.ogTitle);
html = html.replace(/\$OG_IMAGE/g, respBody.img);
html = html.replace(/\$OG_SITE/g, respBody.ogSite);
}
res.send(html);
});
}
}
}
И в index.html создайте значения шаблона, как показано ниже:
<title>$TITLE</title>
<meta name="description" content="$DESCRIPTION"/>
<meta name="keywords" content="$OG_META_KEYWORDS" />
<meta name="metaDescription" content="$OG_META_DESCRIPTION"/>
<meta name="metaKeywords" content="$OG_META_KEYWORDS" />
<meta property="og:title" content="$OG_TITLE" />
<meta property="og:description" content="$OG_DESCRIPTION" />
<meta property="og:site_name" content="$OG_SITE" />
<meta property="og:type" content="website" />
<meta property="og:image" content="$OG_IMAGE" />
Я реализовал настройку title на Angular 2 Universal. Это нужно делать с помощью Renderer, например:
1.Импортируйте рендерер внутри компонента:
import {Renderer } from '@angular/core';
2.Добавьте зависимость внутри конструктора:
constructor
(
...
private renderer: Renderer
) {}
3. Используйте его сейчас для установки заголовка:
renderer.setText(renderer.selectRootElement('title'), value);
Наткнулся на это в поисках такого же решения. У меня сработало использование setTimeout. Обязательно обновите кеш или используйте режим инкогнито, иначе вы пропустите метаданные (я сделал эту ошибку). Мои исследования показывают, что необходимо дождаться подписки на данные API, прежде чем SSR отобразит страницу, поэтому наблюдаемый должен оставаться открытым (но я не эксперт). https://angular.io/guide/observables. Кроме того, единственный способ заставить его работать - поместить ответ от подписки прямо в metaService. Я покажу, что я имею в виду, в своем коде.
setTimeout( () =>
this.seo.setFromJson({
title: this.content.title,
image: this.content.main_image,
description: this.content.blog,
author: this.content.name,
keyword: ''
});
,0);
Мой код с решением:
this.publicFeedService.getPublicProfile(data).subscribe((response:any) => {
this.memberProfile = response[this.userId];
setTimeout( () =>
//Define the meta title using profile data
this.titleService.setTitle('The best investment ideas from ' + response[this.userId].firstName + ' ' + response[this.userId].lastName), 0);
setTimeout( () =>
//Define the meta tags using the user profile data
this.metaService.addTags([
{name: 'keywords', content: 'investing ideas, stock ideas, financial profile'},
{name: 'description', content: response[this.userId].about},
{name: 'robots', content: 'index, follow'}
]), 0);
Решение, которое вы можете назвать это!
Рецепт Мастера Йоды
Как человек, который пытался добиться SEO-дружественного angular с помощью SSR и утилит, которые angular может предложить относительно метатегов, я хотел бы поделиться идеей по этому вопросу.
Я был настолько сосредоточен на этой теме, что не увидел очевидного и того, насколько просто было ее решить!
Как вы, возможно, знаете, сначала будет отображаться HTML, затем CSS и только ПОТОМ JavaScript.
Кроме того, индексные боты в основном заботятся о необработанном HTML!
Там для того, должны ли боты принимать решения после того, как они отрендерили JS, что, кстати, неэффективно и дорого для таких компаний, как Google, и у них есть свои причины для этого, или ... Мы можем встроить одностраничные приложения в конкретный бэкэнд
. программы, совместимые с изменением html перед его отправкой на клиентскую сторону.
Думаю об этом ...
одностраничное приложение было введено для решения конкретной новой проблемы и не может быть проанализировано только с помощью необработанного HTML!
Таким образом, на конкретный ответ на « Обновить метатеги в угловом универсальном с внешним вызовом API » уже был дан ответ.
Однако, если вам нужно настоящее SEO-дружественное угловое приложение, вы должны встроить их вместе.
Мне нравится делать это в Spring boot + Thymeleaf . Если вы используете какой-либо другой бэкэнд, вы знаете, что делать...
Подход
- Просто создайте свои проекты angular/react/vue или в своем кейс-проекте. Вам не нужно создавать его с помощью angular universal, основная идея состоит в том, чтобы распространять одну и ту же страницу index.html для репликации со структурой папок на основе вашей маршрутизации с помощью бегунов узлов, чтобы вы могли изменить метатег каждой страницы. Я уверен, что с достаточным количеством информации вы сможете найти способ встроить приложение даже с этим, но стоит ли оно того? Я оставляю это на ваше усмотрение.
- Создавайте стратегии для своих маршрутов в своем бэкэнде. Заставьте их все возвращать индекс, но не раньше, чем установите правильные метатеги и спецификацию, которую вы хотите применить к нему. Клиентское приложение само сделает всю остальную работу по рендерингу в браузере.
- Настройте серверную часть и/или внешний интерфейс для максимальной производительности, черт возьми, вы также можете многое сделать с помощью devOps. Затем перерыв на кофе.
Совет: при таком подходе вы можете использовать шаблоны Thymeleaf, а также управлять несколькими одностраничными приложениями, используя только маршрутизацию!
Я приведу простой пример, так как это всего лишь идея, а не учебник. Не стесняйтесь оставлять комментарии, я отвечаю вскоре после этого.
Выполнение
- Index.html допустим, я хочу, чтобы этот метатег был динамическим на основе пути, который я рассматриваю.
...
<meta th:if="${post}" property="og:site_name"
th:content="${post} ? ${post.getName()}: 'Your sit name'">
...
- Контроллер шаблонов и пути
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
@RestController
@AllArgsConstructor
public class TemplateController {
@GetMapping("/")
public ModelAndView HomePage() {
// replace the path you aim for the index.html
ModelAndView home = new ModelAndView("app/index");
return home;
}
@GetMapping("/post/{name}")
public ModelAndView Post(@PathVariable String name) {
// replace the path you aim for the index.html
ModelAndView home = new ModelAndView("app/index");
PostResponse postResponse = new PostResponse();
postResponse.setName(name);
home.addObject("post", mock);
return home;
}
}
- PostResponse просто фиктивный класс
import lombok.Data;
import java.util.Date;
import java.util.UUID;
@Data
public class PostResponse {
private UUID id;
private Date created;
private String path;
private String name;
}
Теперь, если вы просто зайдете на страницу маршрутаlocalhost:port
вы можете найти мета с «названием вашего сайта» . Вы переходите на страницуlocalhost:port/post/some
name , вы получите мета с содержимым «какое-то имя» .
Сначала добавьте все необходимые метатеги в файл index.html следующим образом
Затем обновите метатеги в каждом необходимом компоненте, используйте meta.updateTag(), как показано ниже
Этот работал для меня.
Я пытаюсь создать службу для обновления, но это не сработало, она работает только тогда, когда мета-обновление добавлено в тот же компонент.