每次刷新后嵌套树视图恒常性 [英] Tree view constancy being nested after every refresh

查看:21
本文介绍了每次刷新后嵌套树视图恒常性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是来自 c# 背景的 Angular (7) 新手.我正在使用 asp.net core 2.2 并使用 angular new angular 项目(home、counter、fetch-data)附带的默认模板.树视图被绑定并且来自控制器.

我期待

<代码>>欧洲>>英国>>>曼彻斯特>>>伦敦>>>>伦敦城>>>>斯特拉特福>>德国

但是,每次我扩展时我都会得到

<代码>>欧洲>>欧洲>>>欧洲>>>>欧洲

等等

我的代码(加载 ISS express 后显示的主页)

测试组件<app-tree-view></app-tree-view>

tree-view.component.html

    <li *ngFor="让 n 个节点"><span><input type="checkbox" [checked]="n.checked" (click)="n.toggle()"/></span>{{ n.name }}><div *ngIf="n.expanded"><app-tree-view [nodes]="n.nodes"></app-tree-view>

tree-view.component.ts

import { Component, Inject, Input, OnInit } from '@angular/core';从'@angular/common/http' 导入 { HttpClient };@成分({选择器:应用程序树视图",templateUrl: './tree-view.component.html'})导出类 TreeViewComponent {@Input() 节点:数组<节点>;构造函数(http:HttpClient,@Inject('BASE_URL')baseUrl:字符串){this.nodes = [];http.get(baseUrl + 'api/FunctionNodes/GetNodes').subscribe(result => {this.nodes = this.RecursiveMapNodes(result.map(x => new Node(x.id, x.name, x.nodes)));}, 错误 =>控制台错误(错误));}RecursiveMapNodes(nodes: Array): Array{var 结果 = Array();for(让节点的节点){var n = new Node(node.id, node.name, this.RecursiveMapNodes(node.nodes));结果.push(n);}返回结果;}}导出类节点{身份证号码;名称:字符串;节点:数组<节点>;检查:布尔值;扩展:布尔值;构造函数(ID:编号,名称:字符串,节点:数组<节点>){this.id = id;this.name = 名称;this.nodes = 节点;this.checked = false;this.expanded = false;}切换(){this.expanded = !this.expanded;}查看() {让 newState = !this.checked;this.checked = newState;this.checkRecursive(newState);}检查递归(状态){this.nodes.forEach(d => {d.checked = 状态;d.checkRecursive(state);})}}

FunctionNodesController.cs

 [Route("api/[controller]")]公共类 FunctionNodesController :控制器{[HttpGet("[动作]")]公共 IEnumerable<节点>获取节点(){var node_1 = new Node() { Id = 1, Name = "Europe" };var node_1_1 = new Node() { Id = 2, Name = "England" };var node_1_1_1 = new Node() { Id = 3, Name = "Manchester" };var node_1_1_2 = new Node() { Id = 4, Name = "London" };var node_2_1_1 = new Node() { Id = 5, Name = "London City" };var node_2_1_2 = new Node() { Id = 6, Name = "Stratford" };var node_1_2 = new Node() { Id = 7, Name = "Germany" };node_1.Nodes.Add(node_1_1);node_1_1.Nodes.Add(node_1_1_1);node_1_1.Nodes.Add(node_1_1_2);node_1_1_2.Nodes.Add(node_2_1_1);node_1_1_2.Nodes.Add(node_2_1_2);node_1.Nodes.Add(node_1_2);return new List() { node_1 };}公共类节点{公共 int Id { 获取;放;}公共字符串名称{获取;放;}公共列表<节点>节点{得到;放;}公共节点(){Nodes = new List();}}}

app.module.tss

import { BrowserModule } from '@angular/platform-b​​rowser';从'@angular/core' 导入 { NgModule };从'@angular/forms'导入{FormsModule};从'@angular/common/http'导入{HttpClientModule};从 '@angular/router' 导入 { RouterModule };从 './app.component' 导入 { AppComponent };从 './nav-menu/nav-menu.component' 导入 { NavMenuComponent };从 './home/home.component' 导入 { HomeComponent };从 './counter/counter.component' 导入 { CounterComponent };import { FetchDataComponent } from './fetch-data/fetch-data.component';import { MainWindowComponent } from './main-window/main-window.component';从 './tree-view/tree-view.component' 导入 { TreeViewComponent };@NgModule({声明: [应用组件,导航菜单组件,主页组件,反分量,获取数据组件,主窗口组件,树视图组件],进口:[BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),HttpClient 模块,RouterModule.forRoot([{ path: '', 组件: MainWindowComponent, pathMatch: 'full' },{ path: 'counter', 组件: CounterComponent },{ path: 'fetch-data', 组件: FetchDataComponent },])],提供者:[],引导程序:[AppComponent]})导出类 AppModule { }

解决方案

您好,我创建了一个小演示,您可以将其用作参考,对于您正在解决的问题,您可以在 CodeSandbox.

由于我对 C# 部分不太了解,我创建了一个模拟后端服务,它应该充当您的后端.

关于这个问题,为什么它不起作用,正如您在评论中已经提到的,每次您在内部初始化(进入一个级别)您的 tree-view.component.ts您正在获取数据的是构造函数,这导致始终显示欧洲"作为结果.

在创建递归元素(树等)时,您必须始终向递归组件(在您的情况下为 tree-view.component.ts)提供树的下一层.

例如第一个 'Europe' => ['England' => ['Manchester' , 'London' => ['London city', 'Stratford] ] , 'Germany'] ,其中每个 => 正在构建新的 tree-view.component.ts

//模板<div [ngStyle]="{'margin-left':level*12 +'px'}" *ngFor="let node of nodes"><h1>当前节点 = {{node?.name}}</h1>

<div *ngIf="open"><app-tree [level]="level+1" [nodesForDisplay]="node.nodes"></app-tree>

//成分@成分({选择器:应用程序树",templateUrl: "./tree.component.html"})导出类 TreeComponent 实现 OnInit {@Input("nodesForDisplay") 节点;@Input("level") level = 0;打开 = 假;}

因此,为了避免这种始终获取第一层的死锁,您可以尝试创建一个包装父组件,它处理获取并将数据传递给递归组件.

另一个技巧,是从组件中删除 http 调用并将其放置在专用服务中,您将在需要的地方注入组件内部(如示例中所示)

//模板<app-tree [nodesForDisplay]="{{存储数据的地方}}"></app-tree>//成分@成分({选择器:应用程序容器",templateUrl: "./app.component.html",styleUrls: ["./app.component.css"]})导出类 ContainerComponent {...构造函数(公共提取器:{{您的服务}}){}ngOnInit() {this.fetcher.{{获取数据的方法}}.subscribe(x =>{this.{{存储数据的地方}} = x})}}

I am new to Angular (7) coming from a c# background. I am using asp.net core 2.2 and using the default template which comes with angular new angular project( home, counter, fetch-data). The tree view is bound and is coming from a controller.

I am expecting

> Europe
>>  England
>>>   Manchester
>>>   London
>>>>    London City
>>>>    Stratford
>>  Germany

however, i'm getting, every time i expand

>   Europe
>>    Europe
>>>      Europe
>>>>        Europe

and so on

my code (home page which shows as soon as ISS express is loaded)

<p>
Testing Componenets
<app-tree-view> </app-tree-view>

tree-view.component.html

<ul>
  <li *ngFor="let n of nodes">
    <span><input type="checkbox" [checked]="n.checked" (click)="n.toggle()" /></span>
    {{ n.name }}>
    <div *ngIf="n.expanded">
      <app-tree-view [nodes]="n.nodes"></app-tree-view>
    </div>
  </li>
</ul>

tree-view.component.ts

import { Component, Inject, Input, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-tree-view',
  templateUrl: './tree-view.component.html'
})
export class TreeViewComponent {

  @Input() nodes: Array<Node>;

  constructor(http: HttpClient, @Inject('BASE_URL') baseUrl: string) {
    this.nodes = [];
    http.get<Node[]>(baseUrl + 'api/FunctionNodes/GetNodes').subscribe(result => {
      this.nodes = this.RecursiveMapNodes(result.map(x => new Node(x.id, x.name, x.nodes)));
    }, error => console.error(error));
  }

  RecursiveMapNodes(nodes: Array<Node>): Array<Node> {
    var result = Array<Node>();
    for (let node of nodes) {
      var n = new Node(node.id, node.name, this.RecursiveMapNodes(node.nodes));
      result.push(n);
    }
    return result;
  }
}

export class Node {
  id: number;
  name: string;
  nodes: Array<Node>;
  checked: boolean;
  expanded: boolean;

  constructor(id: number, name: string, nodes: Array<Node>) {
    this.id = id;
    this.name = name;
    this.nodes = nodes;
    this.checked = false;
    this.expanded = false;
  }

  toggle() {
    this.expanded = !this.expanded;
  }
  check() {
    let newState = !this.checked;
    this.checked = newState;
    this.checkRecursive(newState);
  }

  checkRecursive(state) {
    this.nodes.forEach(d => {
      d.checked = state;
      d.checkRecursive(state);
    })
  }

}

FunctionNodesController.cs

 [Route("api/[controller]")]
    public class FunctionNodesController : Controller
    {
        [HttpGet("[action]")]
        public IEnumerable<Node> GetNodes()
        {
            var node_1 = new Node() { Id = 1, Name = "Europe" };
            var node_1_1 = new Node() { Id = 2, Name = "England" };
            var node_1_1_1 = new Node() { Id = 3, Name = "Manchester" };
            var node_1_1_2 = new Node() { Id = 4, Name = "London" };
            var node_2_1_1 = new Node() { Id = 5, Name = "London City" };
            var node_2_1_2 = new Node() { Id = 6, Name = "Stratford" };
            var node_1_2 = new Node() { Id = 7, Name = "Germany" };

            node_1.Nodes.Add(node_1_1);
            node_1_1.Nodes.Add(node_1_1_1);
            node_1_1.Nodes.Add(node_1_1_2);
            node_1_1_2.Nodes.Add(node_2_1_1);
            node_1_1_2.Nodes.Add(node_2_1_2);
            node_1.Nodes.Add(node_1_2);
            return new List<Node>() { node_1 };
        }

        public class Node
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public List<Node> Nodes { get; set; }
            public Node()
            {
                Nodes = new List<Node>();
            }
        }
    }

app.module.tss

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { RouterModule } from '@angular/router';

import { AppComponent } from './app.component';
import { NavMenuComponent } from './nav-menu/nav-menu.component';
import { HomeComponent } from './home/home.component';
import { CounterComponent } from './counter/counter.component';
import { FetchDataComponent } from './fetch-data/fetch-data.component';
import { MainWindowComponent } from './main-window/main-window.component';
import { TreeViewComponent } from './tree-view/tree-view.component';

@NgModule({
  declarations: [
    AppComponent,
    NavMenuComponent,
    HomeComponent,
    CounterComponent,
    FetchDataComponent,
    MainWindowComponent,
    TreeViewComponent
  ],
  imports: [
    BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
    HttpClientModule,
    RouterModule.forRoot([
      { path: '', component: MainWindowComponent, pathMatch: 'full' },
      { path: 'counter', component: CounterComponent },
      { path: 'fetch-data', component: FetchDataComponent },
    ])
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

解决方案

Hello I have created a little demo that you can use as reference, for the problem that you are tackling, you can find it here at CodeSandbox.

As im not very knowledgeable about the C# part, I have created a mock back-end service that is supposed to act as your back-end.

About the question, why does it not work, as you had already mentioned in the comments, every time you are initializing (going a level in) your tree-view.component.ts inside of it's constructor you are fetching the data, which results in always getting 'Europe' displayed as result.

When creating recursive elements (trees and etc.) you must always provide to the recursive component (in your case tree-view.component.ts) the next layer of the tree.

For example first 'Europe' => ['England' => ['Manchester' , 'London' => ['London city', 'Stratford] ] , 'Germany'] , where each => is building new tree-view.component.ts

// Template
<div [ngStyle]="{'margin-left':level*12 +'px'}" *ngFor="let node of nodes">
  <h1>Current node = {{node?.name}}</h1>
  <div
    (click)="open=!open"
    style="cursor: pointer;"
    *ngIf="node?.nodes?.length!==0"
  >
    Expand nodes
  </div>
  <div *ngIf="open">
    <app-tree [level]="level+1" [nodesForDisplay]="node.nodes"></app-tree>
  </div>
</div>
// Component
@Component({
  selector: "app-tree",
  templateUrl: "./tree.component.html"
})
export class TreeComponent implements OnInit {
  @Input("nodesForDisplay") nodes;
  @Input("level") level = 0;
  open = false;
}

So in order to escape this deadlock, of always fetching the first layer, you can try creating a wrapper parent component, that handles the fetching and passes the data to the recursive components.

Another tip, is to remove the http call from the component and place it in a dedicated service, that you will inject inside of the components where needed (as done in the example)

// Template
<app-tree [nodesForDisplay]="{{place to strore the data}}"></app-tree>

// Component
@Component({
  selector: "app-container",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class ContainerComponent {
  ...
  constructor(public fetcher: {{Your Service}}) {}
  ngOnInit() {
  this.fetcher.{{method to fethc the data}}.subscribe(x =>{
  this.{{place to strore the data}} = x
  })
  }
}

这篇关于每次刷新后嵌套树视图恒常性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
C#/.NET最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆