如何从工厂类返回类对象在角度的组件中使用它

问题描述 投票:0回答:0

我有一个名为

MapThemeFactory
的工厂类。有一种方法叫做
getTheme 
方法,我正在传递
themeId
。使用我得到的 主题,然后使用 switch case 我正在创建类的实例。 这里
CustomMapThemeExe 
class是一个抽象类。
StreetThemeExe 
VintageThemeExe 
课程 从
CustomMapThemeExe 
类延伸。

这是 MapThemeFactory 类

// A factory class that creates a MapTheme object based on the selected theme type.
export class MapThemeFactory {
  private config: MapReportConfigModel;
  theme: any;

  constructor(private http: HttpClient, config: MapReportConfigModel) {
    this.config = config;
  }

  getTheme( themeId: number): Observable<{theme: MapTheme; customHtmlElement: any[]; customLayers: any[];}> {
    // Retrieve the theme object based on the themeId
    const selectedTheme = this.config.theme.list.find(
      (item) => item.id === themeId
    );

    let themeExe: CustomMapThemeExe;

    switch (selectedTheme.name) {
      case "street":
        themeExe = new StreetThemeExe();
        break;
      case "vintage":
        themeExe = new VintageThemeExe();
        break;
      default:
    }

    // Load the theme properties from the config file
    const themeProperties$ = this.loadThemeProperties(selectedTheme.config);
    const mapThemeModel$ = themeProperties$.pipe(
      map((themeData) => {
        // Create the MapThemeModel object
        const themeModel: MapThemeModel = {
          style: themeData.style,
          css: themeData.css,
          marker: themeData.marker,
        };
        return themeModel;
      })
    );

    // Use forkJoin to wait for both the theme properties and the theme executor to be loaded
    return forkJoin([themeProperties$, mapThemeModel$]).pipe(
      map(([themeData, themeModel]) => {
        const customHtmlElement = themeData.externalComponents;
        customHtmlElement?.forEach((elementString) => {
          const element = this.cleanHtmlElement(elementString);
          return themeExe.setCustomHtmlElement(element);
        });

        const layers = themeData.layers;
        const sources = themeData.sources;
        const newLayers = themeExe.setCustomLayers(layers, sources);

        // Create the MapTheme object
        const mapTheme: MapTheme = new MapTheme(themeModel, themeExe);
        // Return an object containing the theme executor and the MapTheme object
        return { theme: mapTheme, customHtmlElement, customLayers: newLayers };
      })
    );
  }

  //-----------------------------------------Helper methods--------------------------------------------

  // Loads the theme properties from the specified config file.
  private loadThemeProperties(configFilePath: string): Observable<any> {
    return this.http.get(configFilePath);
  }

  // Takes in a string and removes all spaces and brackets from it
  private cleanHtmlElement(elementString: string): HTMLElement {
    const element = document.createElement("div");
    element.innerHTML = elementString.replace(/^\[|\]$/g, "").trim();
    return element;
  }
}

在这部分的这个 getTheme 方法中,我调用

themeExe.setCustomHtmlElement(element)
方法和
themeExe.setCustomLayers(layers, sources)
方法。在这里,我将
customHtmlElement 
传递给
themeExe.setCustomHtmlElement(element)
方法和图层,传递给
setCustomLayers
method.

// Use forkJoin to wait for both the theme properties and the theme executor to be loaded
    return forkJoin([themeProperties$, mapThemeModel$]).pipe(
      map(([themeData, themeModel]) => {
        const customHtmlElement = themeData.externalComponents;
        customHtmlElement?.forEach((elementString) => {
          const element = this.cleanHtmlElement(elementString);
          themeExe.setCustomHtmlElement(element);
        });

        const layers = themeData.layers;
        const sources = themeData.sources;
        const newLayers = themeExe.setCustomLayers(layers, sources);

        // Create the MapTheme object
        const mapTheme: MapTheme = new MapTheme(themeModel, themeExe);
        // Return an object containing the theme executor and the MapTheme object
        return { theme: mapTheme, customHtmlElement, customLayers: newLayers };
})

这是 CustomMapThemeExe 类

export abstract class CustomMapThemeExe {
  setCustomHtmlElement(htmlElements: any): any {}
  setCustomLayers(layers: any, sources: any): any {}
}

这是 StreetThemeExe 类

export class StreetThemeExe extends CustomMapThemeExe {}

这是 VintageThemeExe 类

我想更改这里的 setCustomHtmlElement 和 setCustomLayers 方法。

export class VintageThemeExe extends CustomMapThemeExe {
  setCustomHtmlElement(htmlElements: any): any {
    const element = document.createElement("div");
    element.appendChild(htmlElements);
    return element;
  }

  setCustomLayers(layers: any, sources: any): any {
    const newSource = sources || {};
    const newLayer = layers || [];

    const newLayers = {
      newSource,
      newLayer,
    };
    return newLayers;
  }
}

在从工厂类

VintageThemeExe 
返回元素和newLayer之后
getTheme 
方法返回它。在这个
MapContainerComponent ``onThemeChange 
方法中我设置了
customHtmlElement 
customLayers
使用这些方法进行映射。
addCustomSourcesAndLayers 
addCustomHtmlElementAndCss
.

这是 MapContainerComponent

export class MapContainerComponent implements OnInit {
  @Input() mapReportConfigData: MapReportConfigModel;

  map: mapboxgl.Map;
  mapIndividualsList: MapIndividualsViewModel[];
  private markers: mapboxgl.Marker[] = [];

  constructor(
    private messageDialogService: MessageDialogService,
    private translateHandler: TranslateHandler,
    private mapReportService: MapReportService
  ) {}

  ngOnInit(): void {
    this.initMap();

    this.subscribeToThemeData();
  }

  initMap(): void {
    try {
      const {
        accessToken,
        container,
        zoomLevel,
        centerLat,
        centerLng,
        projection,
      } = this.mapReportConfigData;
      mapboxgl!.accessToken = accessToken;
      this.map = new mapboxgl.Map({
        container,
        zoom: zoomLevel,
        center: [centerLat, centerLng],
      });
      this.map.setProjection(projection);

      // If mapbox server issue
      this.map.on("error", (response) => {
        this.showError(
          "lbl_error_heading",
          "mapReport.err_no_map_reports",
          "mapReport.err_generating_map_report_message"
        ).subscribe(() => {
          // Return back to dashboard
          window.location.href = "#0";
          return;
        });
      });
    } catch (error) {
      throw new CustomError(error.message, 404, false);
    }
  }

  onThemeChange(theme: any) {
    // Remove previously added custom layers, sources and html element
    this.removeCustomElements(theme);

    // Add style
    this.map.setStyle(theme.theme.themeModel.style);

    // Add custom html elements and css
    this.addCustomHtmlElementAndCss(theme);

    // Call initMapIndividuals method
    this.initMapIndividuals(theme);

    // Add custom sources, layers
    this.map.once("styledata", () => {
      this.addCustomSourcesAndLayers(theme);
    });
  }

  // Get Individuals List
  initMapIndividuals(theme: any) {
    this.mapReportService.getAllMapIndividuals().subscribe(
      (individualsData) => {
        this.mapIndividualsList = individualsData;
        this.initMarkers(theme);
      },
      // If map individuals not found
      (error: HttpErrorResponse) => {
        this.showError(
          "lbl_error_heading",
          error.status.toString(),
          error.status == 404 ? "mapReport.err_no_map_individuals" : null
        );
      }
    );
  }

  // Add markers to the map
  initMarkers(theme: any) {
    // Remove previously added markers
    this.markers.forEach((m) => m.remove());
    this.markers = [];

    this.mapIndividualsList.forEach((individual) => {
      let marker: mapboxgl.Marker;
      if (theme.theme.themeModel.marker) {
        const markerImage = document.createElement("img");
        markerImage.src = theme.theme.themeModel.marker;
        markerImage.className = "marker";

        marker = new mapboxgl.Marker({
          element: markerImage,
          scale: this.mapReportConfigData.markerScale,
        });
      } else {
        marker = new mapboxgl.Marker({
          scale: this.mapReportConfigData.markerScale,
        });
      }

      const m = marker
        .setLngLat([individual.longitude, individual.latitude])
        .addTo(this.map);
      this.markers.push(m);
    });
  }

  // -----------------------------------Helper methods---------------------------------------------

  private showError(
    title: string,
    info: string,
    prompt: string
  ): Observable<any> {
    return this.messageDialogService.openError(
      this.translateHandler.translate(title),
      this.translateHandler.translate(info),
      prompt == null ? null : this.translateHandler.translate(prompt)
    );
  }

  // akes in a string and removes all spaces and brackets from it
  private cleanHtmlElement(elementString: string): HTMLElement {
    const element = document.createElement("div");
    element.innerHTML = elementString.replace(/^\[|\]$/g, "").trim();
    return element;
  }

  // Notify the theme change and called onThemeChange method
  private subscribeToThemeData(): void {
    this.mapReportService.themeData$.subscribe((data) => {
      if (data) {
        this.onThemeChange(data);
      }
    });
  }

  // Remove previously added custom layers, sources and html element
  private removeCustomElements(theme: any) {
    // Remove previously added custom html elements
    const customElements = this.map
      ? this.map.getContainer().querySelectorAll(".custom-html-element")
      : null;
    if (customElements && customElements.length > 0) {
      customElements.forEach((element) => {
        if (this.map) {
          this.map.getContainer().removeChild(element);
        }
      });
    }

    // // Remove custom sources and layers
    if (theme.customLayers) {
      const sources = theme.customLayers.newSource;
      Object.keys(sources).forEach((sourceId) => {
        const source = sources[sourceId];
        if (source.type && this.map.getSource(sourceId)) {
          this.map.removeSource(sourceId);
        }
      });

      if (theme.customLayers.newLayer) {
        theme.customLayers.newLayer.forEach((layer) => {
          if (this.map.getLayer(layer.id)) {
            this.map.removeLayer(layer.id);
          }
        });
      }
    }
  }

  // Add custom sources , layers
  private addCustomSourcesAndLayers(theme: any) {
    this.map.once("styledata", () => {
      if (theme.customLayers) {
        const sources = theme.customLayers.newSource;
        // Add custom sources to map
        Object.keys(sources).forEach((sourceId) => {
          const source = sources[sourceId];
          if (source.type && !this.map.getSource(sourceId)) {
            this.map.addSource(sourceId, source);
          }
        });

        if (theme.customLayers.newLayer) {
          theme.customLayers.newLayer.forEach((layer) => {
            if (!this.map.getLayer(layer.id)) {
              this.map.addLayer(layer);
            }
          });
        }
      }
    });
  }

  // Add custom html elements and css
  private addCustomHtmlElementAndCss(theme: any) {
    // Add custom html elements to map container
    if (theme.customHtmlElement) {
      theme.customHtmlElement.forEach((elementString) => {
        const element = this.cleanHtmlElement(elementString);
        if (this.map) {
          element.classList.add("custom-html-element");
          this.map.getContainer().appendChild(element);
        }
      });
    }

    // Apply css
    if (theme.theme.themeModel.css) {
      const head = document.getElementsByTagName("head")[0];
      const link = document.createElement("link");
      link.rel = "stylesheet";
      link.href = theme.theme.themeModel.css;
      head.appendChild(link);
    }
  }
}

但在这里我想做这样的事情。我想将这两种方法移到 VintageThemeExe 类中。 addCustomHtmlElementAndCss 和 addCustomSourcesAndLayers。这里我不想在 VintageThemeExe 类中使用此名称,该类已经有 2 个方法 setCustomHtmlElement 和 setCustomLayers。所以 addCustomHtmlElementAndCss = setCustomHtmlElement 和 addCustomSourcesAndLayers = setCustomLayers.

之后在工厂类中调用 setCustomHtmlElement 和 setCustomLayers 方法后我想返回 themeExe 类的实例。

之后在 onThemeChange 中我想调用 setCustomHtmlElement 和 setCustomLayers 方法。 因为它很容易变化,我可以单独保留 customHtmlElement 和 customLayers 部分。

那我该怎么做。

angular typescript mapbox-gl-js
© www.soinside.com 2019 - 2024. All rights reserved.