如何在Angular 12+中转换使用带window.prototype的JavaScript函数
AngularJS迁移至Angular 12+:JSONLoop构造函数的改造与使用方案
问题背景
从AngularJS迁移到Angular 12+时,原项目中挂载在window对象上的JSONLoop构造函数(含原型方法)无法直接在Angular组件中使用,需要进行适配改造。以下是原代码、旧使用方式及示例数据:
原JavaScript代码
(function(window) { var nodes = []; window.JSONLoop = function(obj, idPropertyName, childrenPropertyName) { this.id = idPropertyName; this.children = childrenPropertyName; this.count = 0; this.countNodes(obj); this.total = this.count + 0; } window.JSONLoop.prototype = { constructor: JSONLoop, isEmpty: function(obj) { for(var property in obj) { return false; } return true; }, countNodes: function(obj) { var that = this; this.count++; return (function() { if (!obj || that.isEmpty(obj)) { return false; } else { if (obj[that.children]) { obj[that.children].forEach(function(child) { that.countNodes(child); }); } } })(); }, findNodeById: function(obj, id, callback) { if (obj[this.id] === id) { this.count = this.total + 0; callback(null, obj); } else { if (this.count === 1) { this.count = this.total + 0; callback('the node does not exist', null); } this.count--; if (obj[this.children]) { var that = this; obj[this.children].forEach(function(node) { that.findNodeById(node, id, callback); }); } } } }; }(window));
AngularJS中的使用方式
var jsonloop = new JSONLoop(data[0],'id', 'childBeans'); jsonloop.findNodeById(data[0], dataId.substr(2), function(err, node) { if (err) { childItemObj = ""; } else { childItemObj = node; } });
示例JSON数据
{ "hierarchy": "3", "hierarchyStr": "product", "id": "1730637", "levelId": null, "parentId": "0", "smartBeans": [ { "hierarchy": "3.3", "hierarchyStr": "product~@product-1", "id": "1730637-1730638" }, { "hierarchy": "3.4", "hierarchyStr": "product~@product-2", "id": "1730637-1730670" }, { "hierarchy": "3.5", "hierarchyStr": "product~@product-3", "id": "1730637-1730686" }, { "hierarchy": "3.6", "hierarchyStr": "product~@product-4", "id": "1730637-1730696" } ] }
解决方案
1. 将JSONLoop转换为TypeScript类
把原构造函数和原型方法改写成ES6类,适配Angular的TypeScript环境,同时优化this指向问题:
export class JSONLoop<T> { private idProp: string; private childrenProp: string; public count: number; public total: number; constructor(obj: T, idPropertyName: string, childrenPropertyName: string) { this.idProp = idPropertyName; this.childrenProp = childrenPropertyName; this.count = 0; this.countNodes(obj); this.total = this.count; } private isEmpty(obj: any): boolean { for (const property in obj) { return false; } return true; } private countNodes(obj: T): void { this.count++; if (!obj || this.isEmpty(obj)) { return; } const children = (obj as any)[this.childrenProp]; if (children && Array.isArray(children)) { children.forEach(child => this.countNodes(child)); } } // 保留原回调风格方法 findNodeById(obj: T, id: string, callback: (err: string | null, node: T | null) => void): void { if ((obj as any)[this.idProp] === id) { this.count = this.total; callback(null, obj); return; } if (this.count === 1) { this.count = this.total; callback('the node does not exist', null); return; } this.count--; const children = (obj as any)[this.childrenProp]; if (children && Array.isArray(children)) { children.forEach(node => this.findNodeById(node, id, callback)); } } // 新增Promise风格方法,更适配Angular异步场景 async findNodeByIdAsync(obj: T, id: string): Promise<T> { return new Promise((resolve, reject) => { this.findNodeById(obj, id, (err, node) => { if (err) { reject(new Error(err)); } else if (node) { resolve(node); } else { reject(new Error('Node not found')); } }); }); } }
2. 在Angular组件中使用
在组件中导入JSONLoop类,然后像原方式一样创建实例并调用方法:
import { Component, OnInit } from '@angular/core'; import { JSONLoop } from './json-loop'; // 根据实际文件路径调整 @Component({ selector: 'app-your-component', templateUrl: './your-component.component.html', styleUrls: ['./your-component.component.css'] }) export class YourComponent implements OnInit { childItemObj: any; ngOnInit(): void { // 假设data是从接口或其他地方获取的树形数据 const data = [/* 你的树形数据 */]; const dataId = 'xx1730637'; // 示例ID // 回调风格使用 const jsonloop = new JSONLoop(data[0], 'id', 'smartBeans'); // 注意这里children属性是smartBeans,和示例JSON对应 jsonloop.findNodeById(data[0], dataId.substr(2), (err, node) => { if (err) { this.childItemObj = ''; } else { this.childItemObj = node; } }); // Promise风格使用(推荐) jsonloop.findNodeByIdAsync(data[0], dataId.substr(2)) .then(node => this.childItemObj = node) .catch(() => this.childItemObj = ''); } }
3. 注意事项
- 确保传入的
childrenPropertyName和你的JSON数据中的子节点数组键名一致(示例中是smartBeans,原AngularJS代码中是childBeans,根据实际数据调整) - TypeScript泛型
T可以替换为你定义的树形节点接口,增强类型安全 - 原代码中
count和total的逻辑可以根据需求验证是否需要保留,若仅需查找节点功能,可简化相关计数逻辑
内容的提问来源于stack exchange,提问作者Raghavi
相关产品推荐
相关产品推荐

