Angular2:如何转换我的树模型以适应不同的界面?

时间:2016-07-04 22:28:43

标签: angular primeng

假设我的员工类看起来像这样:

export class Employee {

    jobTitle: string;
    firstName: string;
    lastName: string;

    boss: Employee;
    workers: Employee[];

    constructor(jobTitle: string, firstName: string, lastName: string){
        this.jobTitle = jobTitle;
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public addWorker(worker: Employee){
        this.wokers.push(worker);
        worker.boss = this;
    }

}

假设我们的初始Employee看起来像这样:

function getInitialStaff(): Employee {

    let headWaiter: Employee = new Employee( "Head Waiter", "Sarah", "Hilton" );
    headWaiter.addWorker(new Employee("Bus Boy", "Joe", "BeefEater"));
    headWaiter.addWorker(new Employee("Waiter", "Randy", "Rainman"));

    let headCheif: Employee = new Employee( "Head Cheif", "Phil", "Yorstomux" );
    headWaiter.addWorker(new Employee("Dishwasher", "Andy", "Shark"));

    let owner: Employee = new Employee( "Owner", "Boris", "Laughington" );
    owner.addWorker(headWaiter);
    owner.addWorker(headCheif);
    return owner;
}

我们还可以假设有时员工会被雇用或解雇。有点像这样:

window.setTimeout(()=>{
    let someEmployee: Employee = /* Some existing employee */
    someEmployee.addWorker("someTitle", "someName", "someLastName");
}, someAmountOfTime);

我希望以PrimeNg tree显示员工。

树窗口小部件要求显示的数据符合treeNode接口,如下所示:

export interface TreeNode {
    label?: string;
    data?: any;
    icon?: any;
    expandedIcon?: any;
    collapsedIcon?: any;
    children?: TreeNode[];
    leaf?: boolean;
}

主要问题:如果不更改我的Employee类,将数据转换为TreeNodes的最佳方法是什么?

我正在考虑的事情:

  • 当我添加一个新员工时,我想维护我的树状态(什么是开放状态,选择什么状态等)
  • 我需要能够轻松检索当前所选的员工。
  • 我试图避免维护两个重复的模型。
  • 我希望以合理但可扩展的方式利用Angular2的变更检测

这就是我的EmployeeTree组件目前的样子:

@Component({
    selector: 'employee-tree',
    template: `
        <div>
            <p-tree>
                /* Open Question, I need to transform owner and then tie the resulting TreeNode to the tree */
            </p-tree>
        </div>
    `
    directives: [ Tree ]
})
export class EmployeeTree {
    @Input()
    private owner: Employee;
}

选项1 - 创建一个将Employee转换为TreeNode的管道

我想这会让我的模板看起来像这样:

<p-tree [value]="owner | convertToTreeNode"></p-tree>

我会创建一个像这样的管道:

@Pipe({name: 'convertToTreeNode'})
export class ToTreeNode implements PipeTransform {
    transform(employee: Employee): TreeNode {

        let treeNode: TreeNode = {
            label: employee.firstName + " " + employee.lastName,
            data: employee,
            children: []
        };

        employee.workers.forEach(worker => {
            treeNode.children.push(this.transform(worker));
        })

        return treeNode;

    }
}

优点/缺点:

  • 这利用了角度变化检测
  • 如果这是纯管道,可能会错过更改
  • 如果这是一个不纯的管道,它可能太重了
  • 使组件非常干净,并将责任委派给管道
  • 在添加新项目时,不确定这是否会保留我的gui状态(选择的内容以及打开/关闭的内容)

选项2 - 组件内部为树

维护一个单独的结构
@Component({
    selector: 'employee-tree',
    template: `
        <div>
            <p-tree [data]="employeeTree">
            </p-tree>
        </div>
    `
    directives: [ Tree ]
})
export class EmployeeTree {
    @Input()
    private owner: Employee;

    private employeeTree: TreeNode;

    // TODO - Somehow watch the owner for changes (how?) and then repopulate the employeeTree
}

我认为我可以让Employee发出一个在它发生变化时涓涓细流的事件。这可以给我一些东西。

一般来说,使用Angular2做这样的事情的最佳方法是什么。

总而言之,我想使用一个小部件,它希望数据采用与我存储的略微不同格式的格式。将数据按入窗口小部件的标准angular2方法是什么?是否有特殊的角度酱可以帮助我?我应该保留两个单独的型号吗?我的树可能有100个或者可能有1000个物品,所以我有点担心性能。

2 个答案:

答案 0 :(得分:0)

我会使用Lodash map。这是他们的罐头示例:

import * as _ from 'lodash';
var users = [
  { 'user': 'barney' },
  { 'user': 'fred' }
];

// The `_.property` iteratee shorthand.
_.map(users, 'user');
// => ['barney', 'fred']

这是我对此的歪曲:

let emplData = [
         {  "jobTitle": "Paperboy",
            "firstName": "Fred",
            "lastName":  "Bloggs"
         },
         {  "jobTitle": "Tea boy",
            "firstName": "Charlie",
            "lastName":  "Creamslice"
         }         
    ]
   let resEl = document.getElementById("result");
   resEl.innerText =JSON.stringify(emplData.map(buildEmployeeNamesOnlyAndAddSeqNo));

    function buildEmployeeNamesOnlyAndAddSeqNo({firstName,lastName},index) {
       return {seqNo:index, firstName, lastName}
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.16.2/lodash.js"></script>

  <div id="result">
      
   </div>

答案 1 :(得分:0)

您可以拥有Employee的子类,它符合TreeNode

然后在你的组件中

    @Component({
        selector: 'employee-tree',
        template: `
            <div>
                <p-tree [data]="employeeNode.label">
                </p-tree>
            </div>
        `
        directives: [ Tree ]
    })
    export class EmployeeNode extends Employee implements TreeNode {

        // -- No need for this anymore -- @Input() private owner: EmployeeNode;
        @Input() private jobTitleInput: String;
        @Input() private firstNameInput: String;
        @Input() private lastNameInput: String;

        constructor(){
            super(jobTitleInput, firstNameInput, lastNameInput);

            // Populate TreeNode here for example label from TreeNode
            this.label = `${firstName} ${lastName}`;
      }

        //private employeeTree: TreeNode; -- No need for TreeNode object any more as EmployeeNode contains both TreeNode and Employee

        // TODO - Somehow watch the owner for changes (how?) and then repopulate the employeeTree : Just add a setter for each property 
of Employee that you want to also affect TreeNode field and inside that setter update in your EmployeeNode class the field of TreeNode Interface like 'this.label = 'updated value'
    }