npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ngx-link-preview

v1.2.0

Published

The Open Graph link preview component for Angular 6+

Downloads

578

Readme

ngx-link-preview

npm npm npm

The Open Graph link preview component for Angular 6+

⇨ DEMO

Installation

npm install --save ngx-link-preview
yarn add ngx-link-preview

Features

  • Configurable preview
  • Render array of links
  • Parse links in string
  • Loading indicator
  • Theming: default and modern theme included

Requirements

  • Angular 6+
  • You will need to create an endpoint at your backend to parse an url for meta tags.
    See info below

Component configuration

1. Import to your module

@NgModule({
   imports: [
      NgxLinkPreviewModule,
      ...
   ]
})
export class AppModule { }

2. Use as regular component

<ngx-link-preview></ngx-link-preview>

3. Parameters: Configure the preview

• Two required parameters:

1. [apiRoute]="myApiRoute"

apiRoute accepts any url, where you want to retrieve the metadata from. The target url will be attached by the component as base64 urlencoded query parameter.

/** API route where to get the meta data from, component will build the full request url
 * Schema: api.example.com/api/get-meta-data?url=d3d3LmV4YW1wbGUuY29t
 */
@Input()
public apiRoute: string;
2. [getApiEndpoint$]="apiCallbackFn"

A generic callback function that returns an observable, that runs the api request on subscription. You can use the default Angular HttpClient method, or your configured backend wrapper observable.

/** Method that does the API request, provide as class member arrow function from parent */
@Input()
public getApiEndpoint$: (requestUrl: string) => Observable<any>;

For example:

public apiCallbackFn = (route: string) => {
   return this.http.get(route);
};

Optional parameters

• Links that should be rendered, default: []
/** Plain links string array */
@Input()
public links: string[] = [];
• Input string that should be parsed for links, can be combined with links[]
/** Input string to parse for links */
@Input()
public parseForLinksStr: string;
• Query parameter name, default: 'url'
/** Target url will be attached as encodeURI(btoa(url)), so it must be decoded on the server */
@Input()
public queryParamName = 'url';
• Show image in preview, default: true
/** boolean: show image in preview */
@Input()
public showImage = true;
• Show site name in preview, default: true
/** boolean: show site name in preview */
@Input()
public showSiteName = true;
• Show title in preview, default: true
/** boolean: show title in preview */
@Input()
public showTitle = true;
• Show description in preview, default: true
/** boolean: show description in preview */
@Input()
public showDescription = true;
• Show url in preview, default: false
/** boolean: show link url in preview */
@Input()
public showLinkUrl = false;
• Use cache, default: true
/** boolean: use cache to display previews faster on next rendering */
@Input()
public useCache = true;
• Max cache age in milliseconds, default: 7 days
/** number: max age the data cache of a link preview should be used */
@Input()
public maxCacheAgeMs = 1000 * 60 * 60 * 24 * 7; // 7 days
• Show loading indicator, default: true
/** boolean: show loading indicator */
@Input()
public showLoadingIndicator = true;
• HTML link <a href="..."></a> should be clickable, default: false
/**
* boolean: whether the <a href="..."></a> link should be clickable.
* This is a question of context security. Otherwise use (previewClick) event.
*/
@Input()
public useHtmlLinkDefaultClickEvent = false;
• HTML link target, default: '_blank' (new tab)
/**
* HtmlLinkTarget: where the HTML link should be opened on click.
* Only has an effect if [useHtmlLinkDefaultClickEvent]="true"
*/
@Input()
public htmlLinkTarget: HtmlLinkTarget = '_blank';
• Event emits the URL on click
/** Event emitter: on click to handle the click event, emits the clicked URL */
@Output()
public previewClicked = new EventEmitter<string>();
<!-- $event: string is URL --> 
(previewClick)="previewClicked($event)"

Theming

The package ships a default and a modern theme. The modern theme crops the preview image, what might conclude in unintended results. To use the modern theme, just pass the css class:

<ngx-link-preview class="modern"></ngx-link-preview>

Feel free to create more themes and submit a pull request or open an issue!

.ngx-link-preview-container {
   .og-link-preview {
      &:hover {
      }
      .row {
         .col {
            &.preview-image {
            }
            &.text-data {
            }
            .image {
               img {
               }
            }
            .title {
            }
            .description {
            }
            .header {
               .site-name {
               }
            }
            .footer {
               .url {
               }
            }
         }
      }
   }
}

Loading spinner

You can customize the loading spinner by passing your spinner as content of the component:

<ngx-link-preview>
    <my-spinner-component></my-spinner-component>
</ngx-link-preview>

<!-- or alternatively (any child element): -->
<ngx-link-preview>
    <div class="spinner"></div>
</ngx-link-preview>

Endpoint configuration

Node.js example

With node.js you can use url-metadata

import urlMetadata from 'url-metadata';

public getMetadata(request: Request, response: Response) {
   let { url } = request.query;
   url = this.decodeSafeUrl(url);
   
   urlMetadata(url).then(
      resp => {
         response.send(resp);
      },
      error => {
         response.send(error).status(500);
      }
   );
}

public decodeSafeUrl(value: string): string {
   const valueBase64 = decodeURI(value);
   return Buffer.from(valueBase64, 'base64').toString('utf8');
}

PHP example

$router->get('/meta-tags', function() {
  $url = $_GET['url'];
  $urlDecoded = base64_decode(urldecode($url));
  ini_set('user_agent', 'Mozilla/4.0 (compatible; MSIE 6.0)');
  $sites_html = file_get_contents($urlDecoded);

  $html = new DOMDocument();
  @$html->loadHTML($sites_html);

  $metaTags = [
      'title' => '',
      'description' => '',
      'image' => '',
      'canonical' => '',
      'url' => '',
      'author' => '',
      'availability' => '',
      'keywords' => '',
      'og:description' => '',
      'og:determiner' => '',
      'og:image' => '',
      'og:image:height' => '',
      'og:image:secure_url' => '',
      'og:image:type' => '',
      'og:image:width' => '',
      'og:locale' => '',
      'og:locale:alternate' => '',
      'og:site_name' => '',
      'og:title' => '',
      'og:type' => '',
      'og:url' => '',
      'price' => '',
      'priceCurrency' => '',
      'source' => '',
  ];

  foreach ($html->getElementsByTagName('meta') as $meta) {
      $property = $meta->getAttribute('property');
      $content = $meta->getAttribute('content');
      if (strpos($property, 'og') === 0) {
          $metaTags[$property] = $content;

          if ($property === 'og:title') $metaTags['title'] = $property;
          if ($property === 'og:description') $metaTags['description'] = $property;
          if ($property === 'og:image') $metaTags['image'] = $property;
      }
  }
  $metaTags['canonical'] = $urlDecoded;
  $metaTags['url'] = $urlDecoded;

  return response()->json($metaTags);
});