How about this: "Looking to Share on Social Media with ME

After developing an app using MEAN.js, I made enhancements to the Articles (blog) section to improve SEO, readability, and design. However, one issue I'm struggling with is how to properly share these Articles on social media platforms like Facebook, Google+, and Twitter while ensuring that the correct data appears using og meta tags.

MY GOAL

All I want is to seamlessly share Articles (blog posts) from my MEAN.js application so that when I post links on social sites such as Facebook, the article content displays correctly.

MY ATTEMPTS

I experimented with creating a separate server layout specifically for blog posts, but this caused multiple issues and didn't seem feasible - surely there's a more efficient approach.

Additionally, I tried updating og meta tag data using Angular on the client side, but Social sites fetched those tags before the values were updated, rendering my efforts futile.

Another approach involved retrieving the Angular route URL during index rendering to update the og meta values prior to rendering the index; however, I couldn't locate these values in the req object.

IDENTIFIED ISSUE

The root of the problem, as I see it:

  1. The initial request hits the server, which serves as a single page application utilizing Angular routing where the req.url value remains '/.'

  2. The index file loads with the standard server template layout.

  3. Angular then initiates an AJAX call to fetch Article data and binds it to the page variables.

Hence, the layout gets rendered (including the og meta values) before Angular finalizes the article information retrieval.

PROPOSED SOLUTION

In my express.js file, the app's local variables are initialized as follows:

// Setting application local variables
app.locals.siteName = config.app.siteName;
app.locals.title = config.app.title;
app.locals.description = config.app.description;
app.locals.keywords = config.app.keywords;
app.locals.imageUrl = config.app.imageUrl;
app.locals.facebookAppId = config.facebook.clientID;
app.locals.jsFiles = config.getJavaScriptAssets();
app.locals.cssFiles = config.getCSSAssets();

These local variables are then called by Swig in the layout.server.view.html file as shown below:

// Note the {{keywords}}, {{description}}, etc. placeholders. 
<!-- Semantic META -->
<meta id="keywords" name="keywords" content="{{keywords}}">
<meta id="desc" name="description" content="{{description}}">

<!-- Facebook META -->
<meta id="fb-app-id" property="fb:app_id" content="{{facebookAppId}}">
<meta id="fb-site-name" property="og:site_name" content="{{siteName}}">
<meta id="fb-title" property="og:title" content="{{title}}">
<meta id="fb-description" property="og:description" content="{{description}}">
<meta id="fb-url" property="og:url" content="{{url}}">
<meta id="fb-image" property="og:image" content="{{imageUrl}}">
<meta id="fb-type" property="og:type" content="website">

<!-- Twitter META -->
<meta id="twitter-title" name="twitter:title" content="{{title}}">
<meta id="twitter-description" name="twitter:description" content="{{description}}">
<meta id="twitter-url" name="twitter:url" content="{{url}}">
<meta id="twitter-image" name="twitter:image" content="{{imageUrl}}">

Ultimately, the aim is to update these values with Article-specific information before rendering the page. The challenge lies in getting this done if the layout renders before Angular determines the article data. Consequently, since the Angular route isn't accessible within the req object, I'm puzzled about how to proceed.

So, the core question remains - how can I effectively share my articles on social media platforms in an aesthetically pleasing manner through MEAN.js? Am I heading in the right direction? Is it achievable with the current Article structure, or should I consider constructing a dedicated blogging module that excludes Angular altogether?

Answer №1

After much trial and error, I managed to make this function correctly for my specific application within the MEANJS framework without relying on Nginx or any external tools. While it may not be a one-size-fits-all solution, I wanted to share my experience and results. Keep in mind that what worked for me might not work for everyone.

The initial setup involved redirecting non-hashed URLs to hashed ones. For instance, if a user shared their profile link as example.com/myprofile, it would automatically navigate to example.com/#!/profile/myprofile.

I then implemented a distinct layout specifically designed for social bots (in hindsight, this step may have been unnecessary) and served this layout when the site was being scraped. Here's how I achieved this:

social-layout.server.view.html

...insert relevant content here...
// Take note of the variable names such as {{siteName}}
<meta id="fb-app-id" property="fb:app_id" content="{{facebookAppId}}">
<meta id="fb-site-name" property="og:site_name" content="{{siteName}}">
<meta id="fb-title" property="og:title" content="{{socialTitle}}">
<meta id="fb-description" property="og:description" content="{{socialDescription}}">
<meta id="fb-url" property="og:url" content="{{socialUrl}}">
<meta id="fb-image" property="og:image" content="{{socialImageUrl}}">
<meta id="fb-type" property="og:type" content="website">

...more content goes here...

Subsequently, in my Express file, I performed explicit checks on user-agents to determine whether a new layout should be presented. If a bot was detected, I retrieved crucial data related to the URL from my database and filled in the variables accordingly:

express.js

// This code snippet executes right after setting app.locals variables.
    // Passing the request url to environment locals
    app.use(function(req, res, next) {
        // Examining user-agents to identify social bots. If detected, serve a different layout to populate the og data for better sharing appearance.
        if(req.headers['user-agent'] === 'facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)' ||
            req.headers['user-agent'] === 'facebookexternalhit/1.0 (+http://www.facebook.com/externalhit_uatext.php)' ||
            // Add more agent checks as needed...

            var urlAttempt = req.url;
            urlAttempt = urlAttempt.substr(1);

            Users.findOne({ link: urlAttempt }, function(err, results) {
                if(err) {
                    res.locals.url = req.protocol + '://' + req.headers.host;
                    next();
                } else if (results !== null) {
                    // Link found. Populate data.
                    res.status(200).render('social-index', {

                        // Updating layout variables with DB details.
                        socialUrl: req.protocol + '://' + req.headers.host + req.url,
                        socialTitle: results.orgName,
                        socialDescription: results.shortDesc,
                        socialImageUrl: req.protocol + '://' + req.headers.host + '/profile/img/' + results.imgName
                    });
                } else {
                    res.locals.url = req.protocol + '://' + req.headers.host;
                    next();
                }
            });
        } else {
            res.locals.url = req.protocol + '://' + req.headers.host;
            next();
        }
    });

Your experience with this approach may differ, but it partially resolved my issues. I am still refining the process of sharing the complete URL (including the hash) on social platforms. Hopefully, this information proves beneficial in some capacity.

Answer №2

I encountered a similar issue and found a solution by installing the Mean-Seo module from the mean.js official GitHub repository. This module works by intercepting requests from crawlers such as Google, which add an "_escaped_fragment_" part to SPA URLs they encounter. Mean-Seo then uses Phantom.Js to render a static HTML version of the dynamic content, serving this to the crawlers.

For Facebook and Twitter, I made modifications to the "mean-seo.js" file in Mean-Seo to adjust meta tags before caching and saving the static file. Since Phantom.Js already rendered the entire article page, there was no need for additional API calls. To parse the HTML conveniently, I also utilized "cheerio.js".

Although this approach partially resolved my problem, I still faced challenges with hashbangs and HTML5 mode. Notably, when the URL did not include "_escaped_fragment_", like "https://example.com/post/1", Facebook and Twitter did not generate proper requests.

Update: Subsequently, I decided to abandon this method due to concerns about the reliability of Phantomjs and resource consumption. Instead, I implemented a new strategy involving a separate express route for Twitter and Facebook. Within the server controller, I introduced a function specifically for crawlers and constructed a basic server template devoid of Angular and Bootstrap components. By utilizing Swig (integrated into Meanjs) to render this simplified view template, I managed the meta tags effectively. Additionally, leveraging Nginx as a proxy, I established a rewrite rule based on user agent for crawler requests:

if ($http_user_agent ~* "baiduspider|twitterbot|facebookexternalhit|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator") {
    rewrite ^/posts/(.*)$ /api/posts/crawl/$1 last;
    }

When a crawler seeks a post URL, Nginx directs the request to my simplified crawler route, generating a concise page for the crawler.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for adjusting the height of MUI Date Picker to fit your preferences

<Box sx={{ m: 1 }}> <LocalizationProvider dateAdapter={AdapterDayjs}> <DatePicker label="Enter Date" slotProps={{ textField: { height: "10px" } }} ...

Execute a function every 10 seconds after it finishes running (JavaScript)

I am currently facing a challenge with a function that utilizes the "post" method to retrieve data from the server for processing. The amount of data retrieved varies, leading to longer completion times (around 30 seconds). My goal is to have this functi ...

Guide on accessing a local image while setting the background image using JavaScript

I am trying to set a background image from a local source on my computer. Below are two lines of code, one that works and one that does not: (the local one fails) _html.style.backgroundImage = 'url("urlsourceblahblahblah")'; _html.style.backgro ...

How can I link to a different field in a mongoDB Schema without using ObjectID?

I have created two schemas for books and authors: const bookSchema = new mongoose.Schema({ title: String, pages: Number, description: String, author: { type: mongoose.Schema.Types.ObjectId, ref: 'Author' } }); const Book = mongoose.model ...

JavaScript is utilized to update HTML content through an Ajax request, but unfortunately, the styling is

Apologies for the unconventional title, I am currently in the process of creating a website. When the page is initially loaded, I call upon two scripts within the 'head' tag to create an attractive dynamic button effect. <script src="https:// ...

Ways to retrieve the posts list along with their corresponding tags using the fewest queries possible

Here is how my database tables are structured: TAGS (more of a category): id, tag name, description, slug POSTS: id, title, url ... POSTSTAGS: id, idPost, idTag USERS: id, username, userSlug... VOTES: id, idPost, idUser Each post can have up to five ...

JavaScript failing to accurately measure the length

Currently experiencing a Javascript issue where the length of an element is not displayed correctly when using .length, even though it shows up in Chrome console. Here is what it looks like in Chrome console <html xmlns="http://www.w3.o ...

Error: Cannot access the length property of an undefined value in the JEST test

I'm currently working on incorporating jest tests into my project, but I encountered an error when running the test. The issue seems to be related to a missing length method in the code that I am attempting to test. It appears to be originating from s ...

Await the reply from Angular while using Selenium WebDriver

I am currently automating an Angular-based application using Selenium WebDriver (Java). After selecting an option from a dropdown in the Application Under Test (AUT), data is loaded onto the page through an AJAX call to a web service. However, there is no ...

Having trouble locating the search bar element on Walmart's website?

I'm currently working on a bot that needs Selenium to interact with the search bar on Walmart's website, where it will input the name of a specific product and perform a search. However, I've encountered an issue where no matter what method ...

I'm having trouble displaying the X's and O's in my tic tac toe JavaScript game. Any suggestions on how to resolve this issue?

After following a couple of online tutorials for the tic tac toe project, I encountered an error in both attempts. The X's and O's were not displaying even though all other features were working fine. Below are my codes for HTML, CSS, and JavaScr ...

Displaying a JQuery loading image on a webpage for a designated amount of time

I am trying to display an image in a div once a price calculation function is called. The image should cover the whole page. Can someone assist me with this? <div class="Progress_Layout" style="display:none"> <div class="Progress_Content"> ...

Exploring the boundaries of HTML data manipulation using JavaScript or jQuery

In my HTML (within a Twig template), there is the following code snippet: <li id="{{folder.id}}" data-jstree='{"icon":"glyphicon glyphicon-tags", "type":"folder"}' ><a href="#">{{folder.name}}</a> I'm attempting to extrac ...

A guide to exporting a class in ReactJS

I am currently working on exporting some classes from my music player file - specifically playlist, setMusicIndex, and currentMusicIndex. const playlist = [ {name: 'September', src: september, duration: '3:47'}, {name: 'hello ...

I can't seem to get anything to show up on my React

After starting to work with routing on react.JS, I encountered a challenge where the simple products.jsx file is supposed to display a simple message upon clicking, but it's not showing anything. Here is the code from Index.JS import React from &apos ...

Generate a text string by choosing options from a form

Greetings! I have a form for creating a file, where the file name is determined by user selections. It's similar to "display text as you type" mentioned here, but my form includes radio buttons in addition to a textbox. For example: Textbox: "Name gi ...

Run C# script with the assistance of .load jquery

I have searched extensively for similar posts, but none seem to address the specific question I have regarding my project. What I am attempting to do is load different pages (.aspx) in an iframe dynamically. However, instead of using an iframe, I want to r ...

Waiting for state changes in React by using the UseState Hook

I am currently working on a function that manages video playback when clicked, and I need to set consecutive states using the useState hook. However, I want to ensure that the first state is completed before moving on to the next setState without relying ...

Angular 8: How to Retrieve Query Parameters from Request URL

Can I retrieve the GET URL Query String Parameters from a specific URL using my Angular Service? For example, let's say I have a URL = "http:localhost/?id=123&name=abc"; or URL = ""; // in my service.ts public myFunction(): Observale<any> ...

Is there a method to automatically create .stories.ts files in Angular Storybook?

I am seeking a way to automatically create a .stories.ts file within a component folder after executing the command ng g component my-component. The desired outcome should resemble this structure: my-component -my-component.component.html . ...