<commit_before>import {
  Injectable, ViewContainerRef, ComponentFactoryResolver, ApplicationRef, Injector,
  ComponentFactory
} from '@angular/core';
import {BehaviorSubject} from "rxjs";
import {getSymbolObservable} from "rxjs/symbol/observable";
import {SpawnReference} from "./interfaces/SpawnReference";

@Injectable()
export class AFSpawnService {


  constructor(
    private cfr: ComponentFactoryResolver
    , private appRef: ApplicationRef
    , private injector: Injector
  ) { }

  createComponent(type: any, vcr?: ViewContainerRef, context?: any): SpawnReference{

    // Resolve the factory for incoming component `type`.
    let factory = this.cfr.resolveComponentFactory(type);

    // Create an instance of the component, and add it to the DOM
    let componentRef;
    if(vcr){
      componentRef = vcr.createComponent(factory);
    } else {
      componentRef = factory.create(this.injector);
      this.appRef.attachView(componentRef.hostView);
      document.body.appendChild( (componentRef.hostView as any).rootNodes[0]);
    }

    // Wire up the outputs, and get reference to un-wire outputs
    let unsubs = this._wireOutputs(factory, componentRef, context);

    // Turn the provided inputs into an observable (if not already an observable)
    let observableSymbol = getSymbolObservable(window);
    let context$;
    if(context && context[observableSymbol]){
      context$ = context;
    } else {
      context$ = new BehaviorSubject(context);
    }

    // Subscribe to the new observable for updated input values
    unsubs.push( context$.subscribe(()=>{
      factory.inputs.forEach(i=>{
        if(context[i.propName] !== undefined){
          componentRef.instance[i.propName] = context[i.propName];
        }
      })
    }) );

    // This function will be returned to the caller, to be called when their context is destroyed
    let detach = ()=>{
      !vcr && this.appRef.detachView(componentRef.hostView);
      componentRef.destroy();
      unsubs.forEach(u=>u());
    };

    // This function will be returned to the caller, to be called when there are new values for the inputs
    let next = (data)=>{
      if(context$ == context){
        throw `When passing an observable as a context, you cannot call the \`.next\` function from the result. 
                 If you wish to update the values in your context, send the data through the observable that you 
                 passed in as the context.`;
      }
      context$.next(data);
    };

    return {
      detach,
      next,
    }
  }

  // Internal function to add event emitters for each of the provide outputs
  _wireOutputs(factory: ComponentFactory<any>, componentRef: any, context: {[key:string]: any}): Array<Function>{
    let unsubs = [];
    factory.outputs.forEach(o=>{
      if(context[o.propName] && context[o.propName] instanceof Function){
        unsubs.push(componentRef.instance[o.propName].subscribe(context[o.propName]));
      }
    });
    return unsubs;
  }

}<commit_msg>Made unsubs a array of Subscriptions and properly unsubscribed during detach (#4)<commit_after>import {
  Injectable, ViewContainerRef, ComponentFactoryResolver, ApplicationRef, Injector,
  ComponentFactory
} from '@angular/core';
import {BehaviorSubject, Subscription} from "rxjs";
import {getSymbolObservable} from "rxjs/symbol/observable";
import {SpawnReference} from "./interfaces/SpawnReference";

@Injectable()
export class AFSpawnService {


  constructor(
    private cfr: ComponentFactoryResolver
    , private appRef: ApplicationRef
    , private injector: Injector
  ) { }

  createComponent(type: any, vcr?: ViewContainerRef, context?: any): SpawnReference{

    // Resolve the factory for incoming component `type`.
    let factory = this.cfr.resolveComponentFactory(type);

    // Create an instance of the component, and add it to the DOM
    let componentRef;
    if(vcr){
      componentRef = vcr.createComponent(factory);
    } else {
      componentRef = factory.create(this.injector);
      this.appRef.attachView(componentRef.hostView);
      document.body.appendChild( (componentRef.hostView as any).rootNodes[0]);
    }

    // Wire up the outputs, and get reference to un-wire outputs
    let unsubs = this._wireOutputs(factory, componentRef, context);

    // Turn the provided inputs into an observable (if not already an observable)
    let observableSymbol = getSymbolObservable(window);
    let context$;
    if(context && context[observableSymbol]){
      context$ = context;
    } else {
      context$ = new BehaviorSubject(context);
    }

    // Subscribe to the new observable for updated input values
    unsubs.push( context$.subscribe(()=>{
      factory.inputs.forEach(i=>{
        if(context[i.propName] !== undefined){
          componentRef.instance[i.propName] = context[i.propName];
        }
      })
    }) );

    // This function will be returned to the caller, to be called when their context is destroyed
    let detach = ()=>{
      if (!vcr) {
        this.appRef.detachView(componentRef.hostView);
      }
      componentRef.destroy();
      unsubs.map(u => { if (!u.closed) { u.unsubscribe(); } });
    };

    // This function will be returned to the caller, to be called when there are new values for the inputs
    let next = (data)=>{
      if(context$ == context){
        throw `When passing an observable as a context, you cannot call the \`.next\` function from the result. 
                 If you wish to update the values in your context, send the data through the observable that you 
                 passed in as the context.`;
      }
      context$.next(data);
    };

    return {
      detach,
      next,
    }
  }

  // Internal function to add event emitters for each of the provide outputs
  _wireOutputs(factory: ComponentFactory<any>, componentRef: any, context: {[key:string]: any}): Array<Subscription>{
    let unsubs = [];
    factory.outputs.forEach(o=>{
      if(context[o.propName] && context[o.propName] instanceof Function){
        unsubs.push(componentRef.instance[o.propName].subscribe(context[o.propName]));
      }
    });
    return unsubs;
  }

}
{'fraction_non_alphanumeric': 0.10843373493975904, 'fraction_numerical': 0.0004884402474763921, 'mean_word_length': 3.147873058744092, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6931107', 'n_tokens_mistral': 1741, 'n_tokens_neox': 1698, 'n_words': 524}