0

I have a firebase website hosted in Firebase hosting and built using Ionic Framework (Angular 8.2).

The web app uses a client-side rendering (default Angular 8 behavior) and does not load any web app content to the browser's HTML. This causes significant issues in our SEO, page indexing, and page ranking.

See the HTML source code below of the default landing page for example (Note the <app-root></app-root> tag):

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>GAGA</title>

    <base href="/" />

    <meta
      name="viewport"
      content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
    />
    <meta name="format-detection" content="telephone=no" />
    <meta name="msapplication-tap-highlight" content="no" />
    <!-- <meta http-equiv="Content-Security-Policy"
    content="default-src *; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'; media-src *; img-src * filesystem: data:" /> -->
    <link rel="icon" type="image/png" href="assets/icon/favicon.png" />

    <!-- add to homescreen for ios -->
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black" />
    <meta name="theme-color" content="#1976d2" />
    <meta name="mobile-web-app-capable" content="yes">
    <!-- <link rel="manifest" href="manifest.webmanifest" /> -->
    
    <link rel="apple-touch-startup-image" href="assets/splash/apple-splash-2048-2732.png" media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" />
    <link rel="apple-touch-startup-image" href="assets/splash/apple-splash-2732-2048.png" media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" />
    <link rel="apple-touch-startup-image" href="assets/splash/apple-splash-1668-2388.png" media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" />
    <link rel="apple-touch-startup-image" href="assets/splash/apple-splash-2388-1668.png" media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" />
    <link rel="apple-touch-startup-image" href="assets/splash/apple-splash-1668-2224.png" media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" />
    <link rel="apple-touch-startup-image" href="assets/splash/apple-splash-2224-1668.png" media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" />
    <link rel="apple-touch-startup-image" href="assets/splash/apple-splash-1536-2048.png" media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" />
    <link rel="apple-touch-startup-image" href="assets/splash/apple-splash-2048-1536.png" media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" />
    <link rel="apple-touch-startup-image" href="assets/splash/apple-splash-1242-2688.png" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" />
    <link rel="apple-touch-startup-image" href="assets/splash/apple-splash-2688-1242.png" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" />
    <link rel="apple-touch-startup-image" href="assets/splash/apple-splash-1125-2436.png" media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" />
    <link rel="apple-touch-startup-image" href="assets/splash/apple-splash-2436-1125.png" media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" />
    <link rel="apple-touch-startup-image" href="assets/splash/apple-splash-828-1792.png" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" />
    <link rel="apple-touch-startup-image" href="assets/splash/apple-splash-1792-828.png" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" />
    <link rel="apple-touch-startup-image" href="assets/splash/apple-splash-1242-2208.png" media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)" />
    <link rel="apple-touch-startup-image" href="assets/splash/apple-splash-2208-1242.png" media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)" />
    <link rel="apple-touch-startup-image" href="assets/splash/apple-splash-750-1334.png" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" />
    <link rel="apple-touch-startup-image" href="assets/splash/apple-splash-1334-750.png" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" />
    <link rel="apple-touch-startup-image" href="assets/splash/apple-splash-640-1136.png" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)" />
    <link rel="apple-touch-startup-image" href="assets/splash/apple-splash-1136-640.png" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)" />

    <link rel="apple-touch-icon" sizes="180x180" href="assets/icons/apple-icon-180.png" />
    <link rel="apple-touch-icon" sizes="167x167" href="assets/icons/apple-icon-167.png" />
    <link rel="apple-touch-icon" sizes="152x152" href="assets/icons/apple-icon-152.png" />
    <link rel="apple-touch-icon" sizes="120x120" href="assets/icons/apple-icon-120.png" />
    <!-- Tamara -->
    <script async src="https://cdn.tamara.co/widget/tamara-widget.min.js"></script>
    
                <script async src="https://www.googletagmanager.com/gtag/js?id=UA-159202064-1"></script>
                <script>
                    window.dataLayer = window.dataLayer || [];
                    function gtag() { 
                        dataLayer.push(arguments); 
                    }
                    gtag('js', new Date());
        
                    gtag('config', 'UA-159202064-1');
                </script>
                
    <script async src="https://www.googletagmanager.com/gtag/js?id=AW-604302093"></script>
    <script>
        window.dataLayer = window.dataLayer || [];
        function gtag() { 
            dataLayer.push(arguments); 
        }
        gtag('js', new Date());

        gtag('config', 'AW-604302093');
    </script>
  <link rel="stylesheet" href="styles.eb3052c834c7da8f09b2.css"></head>

  <body>
    <app-root></app-root>
    <noscript>Please enable JavaScript to continue using this application.</noscript>
    <!-- Face Pixel Code -->
    <script>
      !(function (f, b, e, v, n, t, s) {
        if (f.fbq) return;
        n = f.fbq = function () {
          n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments);
        };
        if (!f._fbq) f._fbq = n;
        n.push = n;
        n.loaded = !0;
        n.version = '2.0';
        n.queue = [];
        t = b.createElement(e);
        t.async = !0;
        t.src = v;
        s = b.getElementsByTagName(e)[0];
        s.parentNode.insertBefore(t, s);
      })(window, document, 'script', 'https://connect.facebook.net/en_US/fbevents.js');

      function logEventFb(event, obj) {
        fbq('track', event, obj);
      }

      function initFacebookPixel(pixelId) {
        fbq('init', pixelId);
      }

      function setPageViewFb(pageView) {
        fbq('track', pageView);
        console.log('track view:', pageView);
      }

      function shareViaSocial(text) {
        navigator.share({
          text
        });
      }
    </script>

    <!-- Snap Pixel Code -->
    <script type="text/javascript">
      (function (e, t, n) {
        if (e.snaptr) return;
        var a = (e.snaptr = function () {
          a.handleRequest ? a.handleRequest.apply(a, arguments) : a.queue.push(arguments);
        });
        a.queue = [];
        var s = 'script';
        r = t.createElement(s);
        r.async = !0;
        r.src = n;
        var u = t.getElementsByTagName(s)[0];
        u.parentNode.insertBefore(r, u);
      })(window, document, 'https://sc-static.net/scevent.min.js');

      function initSnapchat(snapId, userEmail) {
        snaptr('init', snapId, {
          user_email: userEmail
        });
      }

      function logEventSnapchat(pageView, obj) {
        snaptr('track', pageView, obj);
      }
    </script>
    <!-- End Snap Pixel Code -->

    <!-- TikTok Pixel Code -->
    <script>
      !(function (w, d, t) {
        w.TiktokAnalyticsObject = t;
        var ttq = (w[t] = w[t] || []);
        (ttq.methods = [
          'page',
          'track',
          'identify',
          'instances',
          'debug',
          'on',
          'off',
          'once',
          'ready',
          'alias',
          'group',
          'enableCookie',
          'disableCookie'
        ]),
          (ttq.setAndDefer = function (t, e) {
            t[e] = function () {
              t.push([e].concat(Array.prototype.slice.call(arguments, 0)));
            };
          });
        for (var i = 0; i < ttq.methods.length; i++) ttq.setAndDefer(ttq, ttq.methods[i]);
        (ttq.instance = function (t) {
          for (var e = ttq._i[t] || [], n = 0; n < ttq.methods.length; n++)
            ttq.setAndDefer(e, ttq.methods[n]);
          return e;
        }),
          (ttq.load = function (e, n) {
            var i = 'https://analytics.tiktok.com/i18n/pixel/events.js';
            (ttq._i = ttq._i || {}),
              (ttq._i[e] = []),
              (ttq._i[e]._u = i),
              (ttq._t = ttq._t || {}),
              (ttq._t[e] = +new Date()),
              (ttq._o = ttq._o || {}),
              (ttq._o[e] = n || {});
            var o = document.createElement('script');
            (o.type = 'text/javascript'), (o.async = !0), (o.src = i + '?sdkid=' + e + '&lib=' + t);
            var a = document.getElementsByTagName('script')[0];
            a.parentNode.insertBefore(o, a);
          });
      })(window, document, 'ttq');

      function initTiktokPixel(pixelId) {
        ttq.load(pixelId);
      }

      function trackEvent(eventName, obj) {
        ttq.track(eventName, obj);
      }
    </script>
    <!-- End TikTok Pixel Code -->

    <script>
      function getURLParameter(name) {
        console.log('location.search = ', location.search);
        return (
          decodeURIComponent(
            (new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) || [
              ,
              ''
            ])[1].replace(new RegExp('\\+', 'g'), '%2B')
          ) || null
        );
      }

      function locationHasChanged() {
        if (location.href.indexOf('/booking/confirmation') != -1) {
          var amount = getURLParameter('amount');
          var transactionDocumentId = getURLParameter('transactionId');
          var objGA = {
            send_to: 'AW-604302093/RfE2CITy_P8BEI3Wk6AC',
            value: amount,
            currency: 'SAR',
            transaction_id: transactionDocumentId
          };
          console.log('objG: ', objGA);
          gtag('event', 'conversion', objGA);
        }
      }

      let currentUrl = location.href;
      const checkPageTransition = () => {
        // requestAnimationFrame(() => {
        if (currentUrl !== location.href) {
          locationHasChanged();
        }
        currentUrl = location.href;
        // }, true);
      };

      document.body.addEventListener('click', checkPageTransition);
      window.addEventListener('load', function () {
        console.log('LOAD ...');
      });
    </script>

    <!-- Deep Link Adjust -->
    <script>
      // Function callback Custom-URL-scheme (Android).
      function handleOpenURL(url) { sessionStorage.setItem('deepLinkAdjust', url) }
    </script>
  <script src="runtime-es2015.c2d98ede7b5e860b1261.js" type="module"></script><script src="runtime-es5.c2d98ede7b5e860b1261.js" nomodule defer></script><script src="polyfills-es5.8d609fa504ef0e3ff287.js" nomodule defer></script><script src="polyfills-es2015.7083b1229f85eaf01d71.js" type="module"></script><script src="scripts.fc3dae47aa26344b0dc0.js" defer></script><script src="main-es2015.81549ae55de7e98047a3.js" type="module"></script><script src="main-es5.81549ae55de7e98047a3.js" nomodule defer></script></body>
</html>

I am thinking about a way to inject HTML (text content) before the page is served to the client side in a meta tag or inside the body. So what is the best way to achieve this?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Eyad
  • 13,440
  • 6
  • 26
  • 43
  • That's called server-side rendering https://angular.io/guide/universal – Chris Hamilton Jul 14 '23 at 01:09
  • Yes, but my current angular version does not support Angular Universal and I don't want to do a cross-app angular version update. I just want a very simple way to inject HTML so the SEO can detect the page content. – Eyad Jul 14 '23 at 10:24
  • Meaning you want html to be injected before it hits the client right? ie. You need to run server side code? – Chris Hamilton Jul 14 '23 at 12:04
  • Yes that's correct, and without having to upgrade my angular version. I thought about a way to maybe develop a function or a piece of code outside of my angular dev framework that would read my DB and inject all the neede meta tags and other html tags (ex hidden p tag) that would make my content readable by the SEO and doesn't effect the final rendered page in the client side. – Eyad Jul 15 '23 at 15:03
  • 1
    You're talking about writing your own SSR framework that works with Angular, why would you not just upgrade to use Angular Universal at that point? It's probably a lot less work and I don't think anyone here is going to walk you through how to write your own framework. – Chris Hamilton Jul 15 '23 at 16:29
  • I thought there should be a way for me to intercept the page load event in Google Firebase and I can execute a simple code to modify the final generated HTML and inject my own content which will be visible by the SEO. Backdays when I was developing in Microsoft Azure, you can intercept any page load within the cloud service itself (not in your dev/coding framework) and effectively do what I envision to do here. – Eyad Jul 16 '23 at 07:53
  • 1
    @Eyad Why does it need to be **before** the page is served to the client? You want to improve performance? (`Angular Universal` is a SSR framework designed to run through the `constructor`, `ngOnInit`, etc, on every possible /route , so the HTML is pre-compiled before arriving at the browser. Instead of having one `index.html`, you'll have a whole server-side thing. ) – Eliezer Berlin Jul 16 '23 at 11:07
  • 1
    @Eyad Perhaps this would be helpful: https://medium.com/@baunov/dynamic-meta-tags-for-angular-app-part-1-dc5957af202c – Eliezer Berlin Jul 16 '23 at 11:13

0 Answers0