Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/btxtiger/ngx-link-preview

The Open Graph link preview component for Angular 6+
https://github.com/btxtiger/ngx-link-preview

angular angular-component html-link library link-preview meta-tags metadata opengraph preview ui weblinks

Last synced: 3 months ago
JSON representation

The Open Graph link preview component for Angular 6+

Awesome Lists containing this project

README

        

# ngx-link-preview
[![npm](https://img.shields.io/npm/v/ngx-link-preview.svg)](https://www.npmjs.com/package/ngx-link-preview)
[![npm](https://img.shields.io/npm/dm/ngx-link-preview.svg)](https://www.npmjs.com/package/ngx-link-preview)
[![npm](https://img.shields.io/librariesio/release/npm/ngx-link-preview)](https://www.npmjs.com/package/ngx-link-preview)

### The Open Graph link preview component for Angular 6+
![](https://raw.githubusercontent.com/btxtiger/ngx-link-preview/master/src/assets/link-preview-2.png)

### ⇨ DEMO

### Installation
```sh
npm install --save ngx-link-preview

# For Angular <= 13
npm install --save [email protected]
```
```sh
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](#endpoint)

### Component configuration
#### 1. Import to your module
```ts
@NgModule({
imports: [
NgxLinkPreviewModule,
...
]
})
export class AppModule { }
```

#### 2. Use as regular component
```html

```

#### 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.
```ts
/** 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.
```ts
/** Method that does the API request, provide as class member arrow function from parent */
@Input()
public getApiEndpoint$: (requestUrl: string) => Observable;
```

For example:
```ts
public apiCallbackFn = (route: string) => {
return this.http.get(route);
};
```
#### • **Optional** parameters
##### • Links that should be rendered, default: `[]`
```ts
/** Plain links string array */
@Input()
public links: string[] = [];
````

##### • Input string that should be parsed for links, can be combined with `links[]`
```ts
/** Input string to parse for links */
@Input()
public parseForLinksStr: string;
```

##### • Query parameter name, default: `'url'`
```ts
/** 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`
```ts
/** boolean: show image in preview */
@Input()
public showImage = true;
```

##### • Show site name in preview, default: `true`
```ts
/** boolean: show site name in preview */
@Input()
public showSiteName = true;
```

##### • Show title in preview, default: `true`
```ts
/** boolean: show title in preview */
@Input()
public showTitle = true;
```

##### • Show description in preview, default: `true`
```ts
/** boolean: show description in preview */
@Input()
public showDescription = true;
```

##### • Show url in preview, default: `false`
```ts
/** boolean: show link url in preview */
@Input()
public showLinkUrl = false;
```

##### • Use cache, default: `true`
```ts
/** boolean: use cache to display previews faster on next rendering */
@Input()
public useCache = true;
```

##### • Max cache age in milliseconds, default: `7 days`
```ts
/** 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`
```ts
/** boolean: show loading indicator */
@Input()
public showLoadingIndicator = true;
```

##### • HTML link `` should be clickable, default: `false`
```ts
/**
* boolean: whether the 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)
```ts
/**
* 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
```ts
/** Event emitter: on click to handle the click event, emits the clicked URL */
@Output()
public previewClicked = new EventEmitter();
```
```html

(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:
```html

```
**Feel free to create more themes and submit a pull request or open an issue!**
You can use this skeleton: (click to show)

```scss
.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:
```html

```


### Endpoint configuration
#### Node.js example
With node.js you can use [url-metadata](https://github.com/LevelNewsOrg/url-metadata)
```ts
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
```php
$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);
});
```

![](https://raw.githubusercontent.com/btxtiger/ngx-link-preview/master/src/assets/link-preview-1.png)