【问题标题】:Creating view structure using angular使用角度创建视图结构
【发布时间】:2019-08-02 23:45:23
【问题描述】:

下面是列表项的数组,

  data = [
    {
      "type": "view-item",
      "depth": 0,
      "text": "View Item One",
    }
  ]

注意:-depth 表示项目的层级。

我可以显示所有项目。

如何做到这一点?请帮忙。

【问题讨论】:

  • 这与 Angular 关系不大。您需要想出一个算法来将您的数据转换为嵌套的元素列表,然后使用递归组件来打印它。不过,如果您知道最大嵌套深度,则可以简化它。
  • 我赞同@LazarLjubenović 的评论。另外.. 在您的问题上标记 JavaScript 或 TypeScript 会更好。
  • 其在Typescriptdepth 级别可以是第N 级
  • 要提出解决方案,必须有某种模式,例如ol 应该是ul 的子列表。在这种情况下,olul 的第三个元素的子列表
  • 如果你的日期变成 {type:..,test:..children:[{type:..test:..} 很容易,否则你总是可以使用 [styles.margin-left ]="{{1*item.depth+'em'}}

标签: angular typescript


【解决方案1】:

首先,您需要将扁平数组转换为嵌套数组。然后你就可以递归循环它来创建你需要的结构。

这是从原始结构构建扁平数组的解决方案。为简单起见,我删除了 type,您还需要在应用程序中维护它,以便您以后可以选择是使用 ol 还是 ul

interface FlatItem {
  depth: number
  text: string
}

interface Item {
  text: string
  children: Item[]
}

function unflatten(flatItems: FlatItem[]): Item {
  const root: Item = { text: 'root', children: [] }
  const stack: Item[] = []

  const firstChildOfRoot = {
    text: flatItems[0].text,
    children: [],
  }
  root.children.push(firstChildOfRoot)
  stack.push(root)
  stack.push(firstChildOfRoot)

  for (let i = 1; i < flatItems.length; i++) {
    const flatItem = flatItems[i]
    const depthDiff = flatItem.depth - (stack.length - 1)
    if (depthDiff <= 0) {
      removeFromEnd(stack, -depthDiff + 1)
    }
    const stackTop = stack[stack.length - 1]
    const newEl = {
      text: flatItem.text,
      children: [],
    }
    stackTop.children.push(newEl)
    stack.push(newEl)
  }

  return root
}

function removeFromEnd<T>(array: T[], count: number) {
  array.splice(array.length - count, count)
}

这个想法是维护最近推送的元素的堆栈。当更深一层时,我们添加到堆栈中。当进入n 级别时,我们首先将n 项目从堆栈中弹出,然后将一个子项添加到当前堆栈顶部。这里还有一些“加/减”修正,因为你的深度从1 开始,而不是0。此外,还有一个名为 root 的附加包装元素,您以后不必打印它,但可以用作将所有 depth: 1 元素组合在一起的伞元素(基本上,您假设存在一个虚构的 depth: 0,它是森林的根)。

我已经在纯 TypeScript 中实现了上述 on StackBlitz(只是算法,没有 Angular),并添加了一个打印字符串的虚拟“渲染”方法。

const flatItems: FlatItem[] = [
  { text: 'A', depth: 1 },
  { text: 'B', depth: 1 },
  { text: 'C', depth: 2 },
  { text: 'D', depth: 2 },
  { text: 'E', depth: 3 },
  { text: 'F', depth: 2 },
  { text: 'G', depth: 2 },
  { text: 'H', depth: 3 },
  { text: 'I', depth: 4 },
  { text: 'J', depth: 4 },
  { text: 'K', depth: 4 },
  { text: 'L', depth: 2 },
  { text: 'M', depth: 1 },
  { text: 'N', depth: 2 },
  { text: 'O', depth: 3 },
  { text: 'P', depth: 1 },
  { text: 'Q', depth: 2 },
  { text: 'R', depth: 3 },
  { text: 'S', depth: 4 },
  { text: 'T', depth: 4 },
  { text: 'U', depth: 4 },
  { text: 'V', depth: 3 },
  { text: 'W', depth: 3 },
]

-A
-B
---C
---D
-----E
---F
---G
-----H
-------I
-------J
-------K
---L
-M
---N
-----O
-P
---Q
-----R
-------S
-------T
-------U
-----V
-----W

现在要从这些元素中创建 DOM 元素,您需要创建一个递归组件,它会不断调用自身,除非元素中没有子元素(这是您摆脱递归的方式)。

请注意,您的示例标记不正确。您不能将ol 直接嵌套到ol 中。你需要有一个li 元素,有它自己的文本,然后添加下一个ol inside。有关如何正确标记嵌套列表的更多详细信息,请参阅 Proper way to make HTML nested list?

这是未经测试的,但它应该遵循以下几行。组件的选择器是tree-view,但您显然可以将其更改为您需要的任何内容。组件类实现了上面代码中Item的接口。

<li>{{ text }}</li>
<ol *ngIf="children.length > 0">
  <tree-view
    *ngFor="let child of children"
    [children]="child.children"
    [text]="child.text"
  ></tree-view>
</ol>

您还需要额外的*ngIf 来在打印olul 之间切换,具体取决于我遗漏的类型,但这是一项微不足道的任务。

【讨论】:

  • 非常感谢,它成功了。你能删除 root 元素吗,因为它向 dom 添加了添加元素
  • isRoot 输入添加到递归组件并使其默认值为true。然后,当您使用递归组件inside 递归组件本身时,传入false。然后使用*ngIf 控制渲染包装器。
  • 你能检查一下吗,stackblitz.com/edit/…
  • @stacks 看来结果就是你所需要的。有什么问题?
  • 这是一个问题,如果是unordered-list-item,它仍然显示为orderered-list-item
【解决方案2】:

试试这个:

let orderedData = data.filter((oData) => {
    return (oData.depth == 2)
});

let unorderedData = data.filter((unoData) => {
    return (oData.depth == 1)
});

<ul>
    <li *ngFor="let item of unorderedData ">{{item.text}}</li>
    <ol>
        <li *ngFor="let item of orderedData">{{item.text}}</li>
    </ol>
</ul>

【讨论】:

  • 这不会保持元素的顺序。
  • 这并不能完全满足我的需求,ul ol 应该是动态的,在解决方案中它是硬编码的
猜你喜欢
  • 1970-01-01
  • 2012-08-16
  • 1970-01-01
  • 2015-02-26
  • 1970-01-01
  • 1970-01-01
  • 2019-10-14
  • 1970-01-01
相关资源
最近更新 更多