Generate iframes dynamically in Angular Fire by retrieving data from a database query, dealing with the unsafe value using DOM Sanitization

Currently, I am using Ionic and Angular with Firebase to develop a daily readings application that dynamically displays an iframe for embedded YouTube videos based on the date. Everything works fine until I try to use data bindings in the source URL for the iframe instead of a static URL. This results in an error: Error: NG0904: unsafe value used in a resource URL context.

After researching, I discovered DomSanitizer and SafeResourceUrl as possible solutions. However, I am facing difficulties implementing this strategy due to fetching the URL data through observables. Most examples I found were either using static URLs or different approaches like vanilla JavaScript versus Angular/TypeScript methods. Coming from a PHP background, transitioning to the FAng stack has been challenging for me.

I attempted to integrate DomSanitizer script following Angular specifications and suggestions on forums, but because of the observable, nothing seems to work, and I encounter errors and IDE complaints.

Here is my attempt at implementing a dynamic URL:

    import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
    import { Injectable } from '@angular/core';
    // other imports
    
export interface Readings {
  id?: string;
  orderNo?: number;
  parshah?: string;
  date?: string;
  videoTorah?: string;
  torahPortion?: string;
  url?: string;
  safeUrl?: string;
}

@Injectable({
  providedIn: 'root'
})
export class DataService {
  url: string;
  urlSafe: SafeResourceUrl;

  constructor(
    private firestore: Firestore,
    private domSanitizer: DomSanitizer
    ) { }
  
  getReadings(): Observable<Readings[]> {
    const readingsRef = collection(this.firestore, 'readings');
    const q = query(readingsRef, orderBy('orderNo'), limit(3));
    return collectionData(q, { idField: 'id' }) as Observable<Readings[]>;
  }

  getUrl(readings: Readings): SafeResourceUrl {
    this.getReadings();
    this.url = 'https://www.youtube.com/embed/' + readings.videoTorah;
    this.urlSafe = this.domSanitizer.bypassSecurityTrustResourceUrl(this.url);
    return this.urlSafe;
  }
}

In my home.page.ts, I added this line to my constructor:

 this.dataService.getReadings().subscribe(res => {
        this.readings = res;
        this.cd.detectChanges();
      });
        this.dataService.getUrl();

Originally, my home.page.html called the URL like this:

<iframe width="560" height="315" [src]="reading.videoTorah" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

Now it looks like this:

<ion-list>
  <ion-item *ngFor="let reading of readings">
    <iframe width="560" height="315" src="getUrl()" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
    <ion-label>
      {{ reading.orderNo }}  {{ reading.parshah }} {{ reading.torahPortion }}
    </ion-label>
  </ion-item>
</ion-list>

Despite trying to call the function after src="getUrl()", it didn't work as expected. I may be completely off track now, but as a hobbyist developer seeking guidance, I feel there's a simple solution I'm overlooking due to my limited formal coding education. My online courses did not cover these advanced topics, so I'm actively studying TypeScript and RxJS to overcome such obstacles.

Answer №1

Angular has strict rules when it comes to accepting SafeResources. The following methods will not work:

<iframe src={{url}}>
<iframe [src]="method()">

I found that the only way to make it work was by binding to a property containing the SafeResource. However, this approach felt too limiting. I wanted to utilize Angular effectively with Observables, async pipe, and OnPush change detection.

To achieve this, I developed a safe pipe. It is utilized in the following manner:

  <iframe
    *ngIf="iFrameSrc$ | async as url"
    [src]="url | safe: scRESOURCE_URL"
  ></iframe>
  • The *ngIf is necessary because the async pipe returns T | null even if the source observable only returns T.
  • scRESOURCE_URL is simply
    public readonly scRESOURCE_URL = SecurityContext.RESOURCE_URL
    in the component class since Angular does not allow accessing external definitions outside of the component.

Pipe implementation:

@Pipe({
  name: 'safe',
  pure: true,
})
export class SafePipe implements PipeTransform {
  constructor(private readonly sanitizer: DomSanitizer) {}

  public transform(value: string, type: SecurityContext.HTML): SafeHtml;
  public transform(value: string, type: SecurityContext.STYLE): SafeStyle;
  public transform(value: string, type: SecurityContext.SCRIPT): SafeScript;
  public transform(value: string, type: SecurityContext.URL): SafeUrl;
  public transform(
    value: string,
    type: SecurityContext.RESOURCE_URL
  ): SafeResourceUrl;
  public transform(
    input: string,
    type: SecurityContext
  ): SafeHtml | SafeStyle | SafeScript | SafeUrl | SafeResourceUrl {
    switch (type) {
      case SecurityContext.HTML:
        return this.sanitizer.bypassSecurityTrustHtml(input);
      case SecurityContext.STYLE:
        return this.sanitizer.bypassSecurityTrustStyle(input);
      case SecurityContext.SCRIPT:
        return this.sanitizer.bypassSecurityTrustScript(input);
      case SecurityContext.URL:
        return this.sanitizer.bypassSecurityTrustUrl(input);
      case SecurityContext.RESOURCE_URL:
        return this.sanitizer.bypassSecurityTrustResourceUrl(input);
    }
    throw 'Invalid SecurityContext';
  }
}

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

Utilizing TypeScript Variables within a Jquery Each Iteration

I have a variable named tableIndexNumber that I need to use in different methods. When trying to access this variable, I use "this.tableIndexNumber" and it works fine. However, I face an issue when using it inside a jQuery each loop because the HTML elemen ...

Is the two-way binding to a property with a set and get method not synchronizing properly with a range slider?

When attempting to display a slider in seconds while maintaining it internally in milliseconds, I encountered an issue where the bound value was not updating. It seems to work when binding to a simple field but not a property. Is Angular unable to watch fo ...

What is the best way to bundle a TypeScript package along with its dependencies for seamless integration with various Next.js projects on a local environment

Currently, I am immersed in a project with the following arrangement: / # repository root /core # A local, unpublished npm package used by both projectA and projectB /projectA # A Next.js app /projectB # Another Next.js app In my setup, I gene ...

Issue with rendering React Toastify

I'm running into an issue while trying to integrate react toastify into my react vite application. Specifically, I keep getting an error related to useSyncExternalStore even after attempting to switch to version 9 of react toastify. My React version i ...

Converting TypeScript query string values into GUIDs

I'm currently working on a project that requires me to retrieve a string value (GUID) and convert it into a GUID. The query string format is already in GUID, but when getting the value from the query string using AngularJS and TypeScript, it returns ...

What is the best way to filter out specific data fields from console.log in JavaScript?

When working with Java, I often use lombok to exclude certain fields from being printed. For instance, the @ToString.Exclude annotation can be used to prevent printing the user token. import lombok.ToString; public class TokenResponse { @ToString.Excl ...

I am currently experiencing a problem with deleting documents in Firebase Firestore using React, Typescript, and Redux. Despite the code running, the document continues to exist in

I seem to be encountering an issue that I can't quite figure out. Despite my code running smoothly, I am unable to delete a document from Firestore. The document ID definitely exists within the database, and there are no subcollections attached to it ...

Issue: Unable to locate a differ that supports the object '[object Object]' of type 'object'. NgFor is only compatible with binding to Iterables like Arrays. Solution needed to resolve this

Code snippet for a service that returns JSON data export class EmployeeServiceService { constructor(private _http:Http) { } GetEmployees(): Observable<IEmployee[]>{ debugger; return this._http.get("http://localhost:2724/a ...

Trigger a click event on a nested Angular 13 component to remove a class from its grandparent component

In my Angular 13 project, I am working with 3 components: Child Component : Burger-menu Parent Component : Header Grand-Parent Component : app.component.html Within the burger-menu component, there is a close button. When this button is clicked, I want t ...

Creating a new TypeScript file via the command line: A step-by-step guide!

When I want to create a new file named main.ts, I try to write the command but it keeps showing an error. "bash: code: command not found" https://i.stack.imgur.com/cpDy3.png ...

Setting up jsonServer in gulp with typescript: A guide

Previously, I had set up a json server and used the following code to start it: I found guidance in this GitHub repository. Starting angular2 project with gulp gulp-live-server.js var gulpCore = require('gulp'); var gulpParam = require('g ...

Send automatically generated information as a component of a submission

Currently, I am struggling to submit dynamically generated content from a table within my form. The form is being generated using Angular 6, but I am having difficulty representing the dynamic content in the FormGroup declaration. import { Component } fr ...

Designing the File and Folder Organization for Next.js Frontend and AWS Cloud Development Kit (CDK) Backend

When it comes to creating websites with serverless backends, I've been thinking about the best practices for folder structure. Currently, my setup includes a Next.js frontend and an AWS CDK backend. The way I've structured the folders has the bac ...

Error message in React NodeJs stack: "The property ' ' does not exist on type 'never' in Typescript."

Recently, I have been immersing myself in learning NodeJs, Express, React, monogoDB and Typescript after working extensively with MVC C# SQL Databases. For my first project, I created a simple Hello World program that interacts with an Express server to d ...

Angular and Spring not cooperating with GET request. What's the issue?

I have been working on integrating a simple GET request to send an Object of type SearchMessage from my Spring Boot server to my Angular client application. After running the server application and verifying that the JSON content is correctly displayed at ...

SvelteKit is having trouble with identifying Typescript syntax

I was working on a SvelteKit project with TypeScript (set up with Vite) and everything was running smoothly with "npm run dev". However, when I attempted to publish the app on Github Pages, an error popped up (on localhost) as I hovered over the only link ...

Is it possible for Typescript to allow extracted interfaces while excluding properties from another interface?

I've been searching for information on the specific features of this. Despite my efforts on Google, I have been unable to find the details. Any help would be greatly appreciated! interface Numbers { number: number; number2: number; number ...

Is my implementation of this [^{}]+(?=}) regex pattern in TypeScript accurate?

Hey there! I'm currently working on extracting values that are inside curly braces "{value}". Do you think the regular expression [^{}]+(?=}) I am using is correct? let url = "/{id}/{name}/{age}"; let params = url.match('[^{\}]+(? ...

What is the process for loading a model using tf.loadModel from firebase storage?

I'm currently working on an app using the ionic3 framework that recognizes hand-drawn characters. However, I am encountering difficulties with importing the model into my project. The model was initially imported from Keras and converted using tensorf ...

In Angular, use the ngFor directive to iterate through items in a collection and add a character to each item except

Currently, I am iterating through my data list and displaying it in the view using spans: <span *ngFor="let d of myData"> {{d.name}}, </span> As shown above, I am adding a comma ',' at the end of each item to ensure a coherent displ ...