title: Angular 2 - Why do I need zone.run()?
body: <p>I am trying to create a component in Angular 2 which displays data from a service. The service basically loads some data from a json file after some input from the user. I've been trying to get the component to update but it does not seem to recognize changes unless I call zone.run() after submitting an event from from my service. My code is as follows for the component... </p>
  
  <pre><code>@Component({
      selector: 'assess-asset-group',
      directives: [AssetComponent, AssetHeaderComponent, NgFor, NgIf],
      template: `
  
              &lt;div *ngIf="assetService.schema != null"&gt;
                  &lt;div class="asset-group" *ngFor="#assetTypeName of assetService.schema.assetTypeNames"&gt; 
                      &lt;div class="asset-type-title"&gt;&lt;span&gt;{{assetService.schema.assetTypes[assetTypeName].name}}s&lt;/span&gt;&lt;/div&gt; 
                      &lt;table class="asset-group-table" cellpadding=0 cellspacing=0&gt;
                          &lt;thead&gt;
                              &lt;tr assess-asset-header [assetType]="assetService.schema.assetTypes[assetTypeName]"&gt;&lt;/tr&gt;
                          &lt;/thead&gt;
                          &lt;tbody&gt;
                              &lt;tr assess-asset *ngFor="#asset of assetService.assetsForType(assetTypeName)" [asset]="asset"&gt;&lt;/tr&gt;
                          &lt;/tbody&gt;
                      &lt;/table&gt;
                      &lt;button class="new-asset-btn" (click)="assetService.addAsset(assetTypeName)"&gt;New&lt;/button&gt;
                  &lt;/div&gt;
              &lt;/div&gt;`,
      providers: [provide(AssetService, {useValue: injector.get(AssetService)})]
  })
  export class AssetGroupComponent {
  
      public assetService: AssetService;
      public zone: NgZone;
  
      constructor( @Inject(AssetService) assetService: AssetService, zone: NgZone) {
          this.assetService = assetService;
          this.zone = zone;
      }
  
      ngOnInit() {
          this.assetService.proejectLoadedEmitter.subscribe((e) =&gt; { this.zone.run(() =&gt; { }) });
      }
  
      ngOnDestroy() {
          this.assetService.proejectLoadedEmitter.unsubscribe();
      }
  }
  </code></pre>
  
  <p>Am I doing something wrong or is this what I need to do in order to update the view?</p>
  
  <p>UPDATE - AssetService Class</p>
  
  <pre><code>@Injectable()
  export class AssetService{
      public assets: Assets.Asset[] = [];
      public assetTypeDefinitions: any = null;
  
      public schema: Schema = null;
      public assetsAsObj: any = null; // Asset file loaded as object
  
      @Output() proejectLoadedEmitter: EventEmitter&lt;any&gt; = new EventEmitter();
  
      constructor(){
      }
  
      public loadProject(config: Project){
          // Load schema
          // populate AssetTypeDefinitions as object keyed by type
          let data = fs.readFileSync(config.schemaPath, 'utf8');
          if (!data) {
              utils.logError("Error reading schema file");
              return;
          }
          let struc = fs.readFileSync(config.structurePath, 'utf8');
          if (!struc) {
              utils.logError("Error reading structure file");
              return;
          }
  
          this.schema = new Schema(JSON.parse(data), struc);
          this.readAssets(config.assetFilePath);
      }
  
      /**
       * @brief Adds a new asset to the assets array 
       * @details Constructs the asset based on the type and populates
       *  its fields with appropreiate default values
       * 
       * @param type The type of the asset - specified in the schema
       */
      public addAsset(type: string): void {
          // Need to make sure there is a loaded type definition for the specified type
          if(!this.schema.assetTypes.hasOwnProperty(type)){
              utils.logError("Error occured during call to addAsset - type \"" + type + "\" is not specified in the loaded schema");
              return;
          }
          // Creeate a new asset object - passing in the type definition from the schema
          this.assets.push(new Assets.Asset(this.schema.assetTypes[type]));
      }   
  
      /**
       * Write the current assets to a file using the specified format
       * If the outputPasth isn't specied try and load it from the project.json file
       */
      public writeAssets(format:AssetWriteFormat, outputPath?: string) : void {
  
          var outStructureStr = this.schema.structureStr;
          // insert AS properties from schema into output assets
          this.schema.properties.forEach(prop =&gt; {
              outStructureStr = outStructureStr.replace(new RegExp('"' + prop +'"', 'i'), this.retriveValueForSchemaProperty(prop));
          });
  
          fs.writeFileSync("C:/Projects/Assess/assets.json", outStructureStr);
      }
  
      public readAssets(inputPath?: string) : void{
          let assetsStr = fs.readFileSync(inputPath, 'utf8');
  
          let strucToAssetMap = {};
          let strucObj = JSON.parse(this.schema.structureStr);
          this.schema.properties.forEach(p =&gt; {
              strucToAssetMap[p] = this.findValueInObject(strucObj, p).reverse();
          });
  
          // @TODO Load custom properties
          let assetsObj = JSON.parse(assetsStr);
          var c = null;
          strucToAssetMap["AS_ASSETS"].forEach(p =&gt; {
              if(c == null){
                  c = assetsObj[p];
              }else{
                  c = c[p];
              }
          });
          c.forEach((asset) =&gt; {
              let a:Assets.Asset = new Assets.Asset(this.schema.assetTypes[asset.type], asset);
              this.assets.push(a);
          });
          console.log(this.assets);
          this.proejectLoadedEmitter.emit(null);
      }
  
      public assetsForType(type:string): Assets.Asset[]{
          var ret: Assets.Asset[] = [];
          for(let idx in this.assets){
              if(this.assets[idx].definition.type === type){
                  ret.push(this.assets[idx]);
              }
          }
          return ret;
      }
  
      public retriveValueForSchemaProperty(property: string) : string{
          if(AS_SchemaTypes.indexOf(property) != -1){
              switch (property) {
                  case "AS_ASSETS":
                      let outAssets = [];
                      this.assets.forEach((asset) =&gt; {
                          let outAsset = {};
                          outAsset["type"] = asset.definition.type;
  
                          for (let key in asset.fields) {
                              outAsset[key] = asset.fields[key].value;
                          }
                          outAssets.push(outAsset);
                      });
                      return JSON.stringify(outAssets, null, "\t");
              }
          }else{
              // @TODO Retrive custom properties
              return '"DDDDDD"';
          }
          return "";
      }
  
      public findValueInObject(obj: any, property: string, path: any[] = []): any[] {
          for(let x in obj){;
              let val = obj[x];
              if (val == property){
                  path.push(x);
                  return path;
              }
              else if(val != null &amp;&amp; typeof val == 'object'){
                  let v = this.findValueInObject(val, property, path);
                  if(v != null){
                      path.push(x);   
                      return path;
                  }
              }
          }
          return null;
      }
  }
  </code></pre>