typescript Angular 2 - 为什么我需要 zone.run()?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/37148813/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Angular 2 - Why do I need zone.run()?
提问by rykeeboy
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...
我正在尝试在 Angular 2 中创建一个组件,该组件显示来自服务的数据。该服务基本上是在用户输入一些数据后从 json 文件加载一些数据。我一直在尝试更新组件,但它似乎无法识别更改,除非我在从我的服务提交事件后调用 zone.run()。我的组件代码如下...
@Component({
selector: 'assess-asset-group',
directives: [AssetComponent, AssetHeaderComponent, NgFor, NgIf],
template: `
<div *ngIf="assetService.schema != null">
<div class="asset-group" *ngFor="#assetTypeName of assetService.schema.assetTypeNames">
<div class="asset-type-title"><span>{{assetService.schema.assetTypes[assetTypeName].name}}s</span></div>
<table class="asset-group-table" cellpadding=0 cellspacing=0>
<thead>
<tr assess-asset-header [assetType]="assetService.schema.assetTypes[assetTypeName]"></tr>
</thead>
<tbody>
<tr assess-asset *ngFor="#asset of assetService.assetsForType(assetTypeName)" [asset]="asset"></tr>
</tbody>
</table>
<button class="new-asset-btn" (click)="assetService.addAsset(assetTypeName)">New</button>
</div>
</div>`,
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) => { this.zone.run(() => { }) });
}
ngOnDestroy() {
this.assetService.proejectLoadedEmitter.unsubscribe();
}
}
Am I doing something wrong or is this what I need to do in order to update the view?
我做错了什么还是这是我需要做的才能更新视图?
UPDATE - AssetService Class
更新 - AssetService 类
@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<any> = 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 => {
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 => {
strucToAssetMap[p] = this.findValueInObject(strucObj, p).reverse();
});
// @TODO Load custom properties
let assetsObj = JSON.parse(assetsStr);
var c = null;
strucToAssetMap["AS_ASSETS"].forEach(p => {
if(c == null){
c = assetsObj[p];
}else{
c = c[p];
}
});
c.forEach((asset) => {
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) => {
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 && typeof val == 'object'){
let v = this.findValueInObject(val, property, path);
if(v != null){
path.push(x);
return path;
}
}
}
return null;
}
}
回答by Günter Z?chbauer
This would require knowledge about the inner workings of the AssetService
you're using.
这需要了解AssetService
您正在使用的内部工作原理。
Angular runs the code of your components within its zone where most async APIs (addEventListener
, setTimeout
, ...) are patched so the zone can notify Angular when such an async callback has happend. This is when Angular runs change detection.
角运行大多数异步的API(你的组件其区域内的代码addEventListener
,setTimeout
...)进行修补,因此区可通知角时,这样的异步回调有happend。这是 Angular 运行变更检测的时候。
If you initialized AssetService
outside Angular or AssetService
by other means executes code outside Angulars zone, then Angular doesn't get notified about happened async callbacks and doesn't run change detection.
如果您AssetService
在 Angular 外部初始化或AssetService
通过其他方式在 Angulars 区域之外执行代码,那么 Angular 不会收到有关发生的异步回调的通知,也不会运行更改检测。
With zone.run(...)
you explicitely make code execute inside Angulars zone and change detection is run afterwards.
与zone.run(...)
您明确地使代码在 Angulars 区域内执行,然后运行更改检测。
回答by Eric N
Is it possibly caused by referencing assetService, the argument, and not this.assetService, in your views? Maybe that is causing Angular's change detection not to be triggered without calling zone.run().
是否可能是由于在您的视图中引用了 assetService、参数而不是 this.assetService 引起的?也许这会导致在不调用 zone.run() 的情况下不会触发 Angular 的更改检测。