角度2-为什么我需要zone.run()? [英] Angular 2 - Why do I need zone.run()?

查看:60
本文介绍了角度2-为什么我需要zone.run()?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在Angular 2中创建一个组件,该组件显示服务中的数据.在用户进行一些输入之后,该服务基本上从json文件中加载了一些数据.我一直在尝试更新组件,但除非从服务提交事件后调用zone.run(),否则它似乎无法识别更改.我的代码如下所示:

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...

@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?

更新-AssetService类

UPDATE - AssetService Class

@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;
    }
}

推荐答案

这需要您所使用的AssetService的内部工作原理的知识.

This would require knowledge about the inner workings of the AssetService you're using.

Angular在其修补了大多数异步API(addEventListenersetTimeout,...)的区域中运行组件的代码,以便该区域可以在发生此类异步回调时通知Angular.这是Angular运行更改检测的时间.

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.

如果您在Angular外部初始化AssetService或通过其他方式在Angulars区域之外执行AssetService的代码,那么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.

使用zone.run(...),您可以明确地使代码在Angulars区域内执行,然后运行更改检测.

With zone.run(...) you explicitely make code execute inside Angulars zone and change detection is run afterwards.

这篇关于角度2-为什么我需要zone.run()?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆