I am completly new to HTML/Angular.
I was following the basic tutorial from the angular website (https://angular2-tree.readme.io/docs#basic-usage).
The problem I now have is that I do not understand how I call the tree in my HTML file with the statement from the website:
<tree-root [nodes]="nodes" [options]="options"></tree-root>
To me it is not clear how the output class is important/defines the nodes that are in the tree and what the JSON "code" below the statement means. Any help is greatly appreciated!
EDIT: my component.ts class
#Component({
selector: 'app-component',
template: '<tree-root [nodes]="nodes" [options]="options"></tree-root>',
templateUrl: './app.component.html'
})
export class App2Component {
nodes = [
{
id: 1,
name: 'root1',
children: [
{ id: 2, name: 'child1' },
{ id: 3, name: 'child2' }
]
},
{
id: 4,
name: 'root2',
children: [
{ id: 5, name: 'child2.1' },
{
id: 6,
name: 'child2.2',
children: [
{ id: 7, name: 'subsub' }
]
}
]
}
];
options = {};
}
and my component.html class
<div class="container">
<div class="jumbotron">
<h1>Bootstrap Tutorial</h1>
<h2>Project 2 Demo</h2>
</div>
<div class="panel panel-primary">
<div class="panel-heading">
Status
</div>
<div class="panel-body">
<h3>{{title}}</h3>
<tree-root [nodes]="[
{
id: 1,
name: 'root1',
children: [
{
id: 2,
name: 'child1'
}, {
id: 3,
name: 'child2'
}
]
}
]" [options]="options"></tree-root>
</div>
</div>
</div>
Angular Architecture:-
1) <body> tag contains - root/parent component as <app-root></app-root> ie
parent module which contains a .ts,.html,.css,.spec files.
2) All other modules are inserted/loads inside app.component.html template
either by passing selector of components or loading component with
<router-outlet></router-outlet> and data is passed to child components
through selector of child component(tree-root) and child component
receive that data from parent component through Input();
app.component.ts
#Component({
selector: 'app',
template: '<tree-root [nodes1]="nodes" [options1]="options"></tree-root>'
});
export class App {
nodes = [
{
id: 1,
name: 'root1',
children: [
{ id: 2, name: 'child1' },
{ id: 3, name: 'child2' }
]
},
{
id: 4,
name: 'root2',
children: [
{ id: 5, name: 'child2.1' },
{
id: 6,
name: 'child2.2',
children: [
{ id: 7, name: 'subsub' }
]
}
]
}
];
options = {};
}
tree-root.component.ts
#Component({
selector: 'tree-root', <--- selector
template: ''
});
export treeclassComponent implements OnInit{
#Input() nodes1: any <---- Recieve input from app.component.ts(parent) here
#Input() options1: any <----Recieve input here
constructor(){}
ngOnInit() {
console.log(nodes);
console.log(options);
}
I also recommend the Angular tutorial and the docs.
The JSON array describes the tree nodes and their children (see here).
The tree-component receives two input parameters (see #Input), which are provided by the node and options property.
The nodes array delivers the information about the tree's node and children of the tree.
The tree-component will iterate over the given input array and creates the tree as you see it.
Update:
To create a tree you need to create an array of objects in your *.component.ts, like following:
this.nodes = [
{
id: 1,
name: 'root1',
children: [
{
id: 2,
name: 'child1'
}, {
id: 3,
name: 'child2'
}
]
}
];
The sample above is from Angular2 Tree Nodes.
Update 2:
As I see you can simply use your previously posted html, because you declared the nodes array in your component:
<tree-root [nodes]="nodes" [options]="options"></tree-root>
Also you need to remove the template from your #Component definition, it should look like this:
#Component({
selector: 'app-component',
templateUrl: './app.component.html'
})
....
Related
As is shown in the image, there are three objects in the current array. Those three objects are the "parents" and each parent has its own array of "children". This is the tricky part; a parent element can also be a child element of another parent element and can have its own child elements. This part is dynamic, meaning that users will create more "parent" elements and more "children" elements.
I want to display this JSON from the image in the front-end using Aurelia. How would I do that? Any ideas are very much welcome.
Aleksandar.
What you intend to do - render a tree structure on the DOM - using Aurelia, can be achieved with a bunch of options.
Being a recursive structure, it is convenient to define the structure that way; for example (note that the example assumes Typescript and bootstrap):
export interface TreeNode {
name: string;
children?: TreeNode[];
}
For the render, a solution is to define a Custom component holding the "Tree" and a custom component to render the nodes of the tree (in a recursive way).
So, you could do:
<require from="./tree"></require>
...
<tree source.bind="treeData"></tree>
The treeData could be:
treeData: TreeNodeModel[] = [
{ name: "node01", children: [{ name: "child011" }, { name: "child012" }, { name: "child013" }] },
{ name: "node02", children: [{ name: "child021" }, { name: "child022", children: [{ name: "child0221" }, { name: "child0222" }, { name: "child0223" }] }] },
{ name: "node03", children: [{ name: "child031" }] },
{ name: "node04", children: [{ name: "child041" }] },
{ name: "node05", children: [{ name: "child051" }] },
{ name: "node06", children: [{ name: "child061" }] },
{ name: "node07", children: [{ name: "child071" }] },
The implementation of the Tree custom componente would be:
import { bindable } from "aurelia-framework";
import { TreeNodeModel } from "./model";
export class Tree {
#bindable source: TreeNodeModel[];
}
The view for the Tree custom component:
<template>
<require from="./node"></require>
<div class="container">
<div repeat.for="node of source">
<node node.bind="node"></node>
</div>
</div>
</template>
The node custom component:
import { bindable } from "aurelia-framework";
import { TreeNodeModel } from "./model";
export class Node {
#bindable node: TreeNodeModel;
#bindable indent: number = 0;
}
The corresponding view:
<template>
<div style="margin-left: ${indent * 8}px;">
<div>${node.name}</div>
<div repeat.for="child of node.children">
<node node.bind="child" indent.bind="indent + 1"></node>
</div>
</div>
</template>
A working example with al the code is available at:
https://codesandbox.io/s/aurelia-recursive-tree-nodes-6fi9e?file=/src/node.html:0-226
Best wishes.
I'm using i18n(v9) to translate a lot of text in a big react project. It's working as intended in cases like:
<Intro
title={details.title}
subtitle={t('resume-upload subtitle')}
description={t('resume-upload description 2')}
/>
However, In a form component that uses these 2 imports:
import { Form } from 'mobx-react-form';
import validatorjs from 'validatorjs';
When I try to translate labels within the code like this:
setup() {
const { t } = this.props;
return {
fields: [
{
name: 'step',
value: 0
},
{
name: 'firstName',
label: t('Firstname'),
rules: 'required|string|between:2,25'
},
{
name: 'lastName',
label: t('Achternaam'),
rules: 'required|string|between:2,25'
},
{
name: 'emailaddress',
label: t('Email'),
rules: 'required|email|string'
},
{
name: 'phonenumber',
label: t('Telephone number'),
rules: 'required|string|telephone'
},
{
name: 'cv',
label: t('resume')
},
{
name: 'terms',
label: 'Terms',
value: false
},
{
name: 'newFile',
label: '',
value: true
},
{
name: 'noFile',
label: '',
value: false
}
]
};
}
}
export default withNamespaces()(UploadForm);
The t function gives an error in a parent file:
TypeError: form.values is not a function
Is there a way to translate json files like the way I'm attempting?
I am using ng-material-multilevel-menu plugin to create multilevel dropdown. I am following this article, but getting below runtime error
Can't bind to 'configuration'
since it isn't a known property of 'ng-material-multilevel-menu'.
1. If 'configuration' is an Angular directive, then add 'CommonModule' to the '#NgModule.imports' of this component.
2. To allow any property add 'NO_ERRORS_SCHEMA' to the '#NgModule.schemas' of this component.
Can't bind to 'items' since it isn't a known property of
'ng-material-multilevel-menu'.
1. If 'items' is an Angular directive, then add 'CommonModule' to the '#NgModule.imports' of this component.
2. To allow any property add 'NO_ERRORS_SCHEMA' to the '#NgModule.schemas' of this component.
'ng-material-multilevel-menu' is not a known element:
1. If 'ng-material-multilevel-menu' is an Angular component, then verify that it is part of this module.
2. If 'ng-material-multilevel-menu' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '#NgModule.schemas' of this component
to suppress this message.
This is my code in .html file
<div>
<ng-material-multilevel-menu [configuration]='config' [items]='appitems' (selectedItem)="selectedItem($event)">
</ng-material-multilevel-menu>
</div>
This is my code in .ts file
import { Component, OnInit, NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { NgMaterialMultilevelMenuModule } from 'ng-material-multilevel-menu';
import { AppComponent } from '../app.component';
#Component({
selector: 'app-products',
templateUrl: './products.component.html',
styleUrls: ['./products.component.css']
})
#NgModule({
declarations: [
],
imports: [
BrowserModule,
NgMaterialMultilevelMenuModule // Import here
],
providers: [],
bootstrap: [AppComponent]
})
export class ProductsComponent implements OnInit {
constructor(private employeeService: ProductService) {
}
ngOnInit() {
var appitems = [
{
label: 'Item 1 (with Font awesome icon)',
faIcon: 'fab fa-500px',
items: [
{
label: 'Item 1.1',
link: '/item-1-1',
faIcon: 'fab fa-accusoft'
},
{
label: 'Item 1.2',
faIcon: 'fab fa-accessible-icon',
items: [
{
label: 'Item 1.2.1',
link: '/item-1-2-1',
faIcon: 'fas fa-allergies'
},
{
label: 'Item 1.2.2',
faIcon: 'fas fa-ambulance',
items: [
{
label: 'Item 1.2.2.1',
link: 'item-1-2-2-1',
faIcon: 'fas fa-anchor'
}
]
}
]
}
]
},
];
});
}
How can I solve this issue?
Remove #NgModule section from this component file. Add NgMaterialMultilevelMenuModule in imports section of your app.module.ts file.
And declare appitems as global variable above the constructor like below:
export class ProductsComponent implements OnInit {
appitems: any = [];
constructor(private employeeService: ProductService) {
}
ngOnInit() {
this.appitems = [
{
label: 'Item 1 (with Font awesome icon)',
faIcon: 'fab fa-500px',
items: [
{
label: 'Item 1.1',
link: '/item-1-1',
faIcon: 'fab fa-accusoft'
},
{
label: 'Item 1.2',
faIcon: 'fab fa-accessible-icon',
items: [
{
label: 'Item 1.2.1',
link: '/item-1-2-1',
faIcon: 'fas fa-allergies'
},
{
label: 'Item 1.2.2',
faIcon: 'fas fa-ambulance',
items: [
{
label: 'Item 1.2.2.1',
link: 'item-1-2-2-1',
faIcon: 'fas fa-anchor'
}
]
}
]
}
]
},
];
});
}
First: Do not use var, just use it like this appitems=[...]
Second: You did not declare the config variable in your controller.
Third: You need to add the NgMaterialMultilevelMenuModule in the AppModule class not in the component you created.
Just define config in your ProductsComponent :
config = {
paddingAtStart: true,
interfaceWithRoute: true,
classname: 'my-custom-class',
listBackgroundColor: `rgb(208, 241, 239)`,
fontColor: `rgb(8, 54, 71)`,
backgroundColor: `rgb(208, 241, 239)`,
selectedListFontColor: `red`,
highlightOnSelect: true,
collapseOnSelect: true,
rtlLayout: false
};
I am currently in the process of trying to build out a hierarchy view of users. My end goal is to generate the view using this hierarchy view
or something similar.
The difficulty lies in how the JSON objects used to generate the hierarchy are given. This is a sample response (this response can be much bigger), where pid is the parent id and depth is the distance from the first parent.
response = [
{uid: "abc", pid: null, depth: 1, parent: true},
{uid: "def", pid: "abc", depth: 2, parent: false},
{uid: "ghi", pid: "abc", depth: 2, parent: true},
{uid: "jkl", pid: "ghi", depth: 3, parent: false},
{uid: "mno", pid: "ghi", depth: 3, parent: false},
]
To explain the above response better, here is the visual hierarchy view of it:
image
A lot of the answers and solutions I've seen so far utilize JSON with children nested in each one. Is it possible to generate the view using the json model above?
Any help or insight would be much appreciated! Thanks!
First, you need to convert your self-reference table to a hierarchical table (tree). I suggest you use a custom pipe to do this since you will be able to reuse this pipe in other places.
You can use Reactgular's code, my code from the StackOverflow thread, or write your own code. I create my converter pipe with Reactgular's code:
converter.pipe.ts
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'converter'
})
export class ConverterPipe implements PipeTransform {
transform(array: any[], id: string = 'uid', parentId: string = 'pid'): any[] {
const map = array.reduce(
(acc, node) => ((node.items = []), (acc[node[id]] = node), acc),
{}
);
return Object.values(map)
.map(
node => (node[parentId] && map[node[parentId]].items.push(node), node)
)
.filter(node => node[parentId] === null);
}
}
Don't forget to add it to the declaration section of your module:
app.module.ts
import { ConverterPipe } from './converter.pipe';
#NgModule({
declarations: [
ConverterPipe
]
})
export class AppModule { }
Now, you can create your component template and use the approach from the Hierarchy View CodePen. Since you need different markup for branches and leaves, it's handy to use NgTemplateOutlet's and NgIf structural directives. This's a good idea to move the level markup in a template and reuse it when you need to render a tree in Angular. The same idea is illustrated in my answer. Based on the provided CodePen code, your Angular markup may look as the following:
app.component.html
<div class="hv-wrapper">
<ng-template #Item let-item>
<ng-container *ngIf="!item.items.length; else Component">
<p>{{ item.uid }}</p>
</ng-container>
<ng-template #Component>
<div class="hv-item">
<div class="hv-item-parent">
<p>{{ item.uid }}</p>
</div>
<div class="hv-item-children">
<div class="hv-item-child" *ngFor="let child of item.items">
<ng-container
*ngTemplateOutlet="Item; context: { $implicit: child }"
></ng-container>
</div>
</div>
</div>
</ng-template>
</ng-template>
<ng-container *ngFor="let child of response | converter"
><ng-container
*ngTemplateOutlet="Item; context: { $implicit: child }"
></ng-container
></ng-container>
</div>
Here, response is your original array:
app.component.ts
export class AppComponent {
response = [
{ uid: 'abc', pid: null, depth: 1, parent: true },
{ uid: 'def', pid: 'abc', depth: 2, parent: true },
{ uid: 'ghi', pid: 'abc', depth: 2, parent: false },
{ uid: 'jkl', pid: 'ghi', depth: 3, parent: false },
{ uid: 'mno', pid: 'ghi', depth: 3, parent: false }
];
}
Don't forget to use the CodePen SASS styles in your project.
After this, you will a graph like:
This is a StackBlitz project that demonstrates this approach in action.
You can use a reducer to convert the flat array into UID map of nodes, and once you have the map you can populate the children easily. You can then just pick out the root node and use that to render the HTML.
const map = [
{uid: "abc", pid: null, depth: 1, parent: true},
{uid: "def", pid: "abc", depth: 2, parent: true},
{uid: "ghi", pid: "abc", depth: 2, parent: false},
{uid: "jkl", pid: "ghi", depth: 3, parent: false},
{uid: "mno", pid: "ghi", depth: 3, parent: false},
].reduce((acc, node) => (node.children = [], acc[node.uid] = node, acc), {});
const [root] = Object.values(map)
.map(node => (node.pid && map[node.pid].children.push(node), node))
.filter(node => node.pid === null);
console.log(root);
You can render the tree by using the same component recursively, and have the template render the children.
#Component({
selector: 'app-node',
template: `
<span>Node</span>
<app-node [node]="child" *ngFor="let child of node.children"></app-node>
`
})
export class NodeComponent {
#Input()
public node: any;
}
It's not hard to modify the above to match the HTML/CSS you linked to in your question.
I have several components that all do the same thing:
display a form (the form varies though, so the HTML differs for each)
capture the form
validate and send the form using a REST API
There are certain things I'm looking to share among the components. For example, the forms all have a list of RadioButtons with the following values:
#Input() radioList: Object[] = [
{ label: 'Unsatisfactory', value: 1 },
{ label: 'Needs Improvement', value: 2 },
{ label: 'Meets Expectations', value: 3 },
{ label: 'Exceeds Expectations', value: 4 },
{ label: 'Outstanding', value: 5 }
];
Is there any way to share stuff like this so if I want to edit that RadioList, I don't have to do it four times?
Extend class?
//property and property-level annotations (#Input) will be picked up by ancestors
export abstract class RadioListAwareComponent{
#Input() radioList: Object[] = [
{ label: 'Unsatisfactory', value: 1 },
{ label: 'Needs Improvement', value: 2 },
{ label: 'Meets Expectations', value: 3 },
{ label: 'Exceeds Expectations', value: 4 },
{ label: 'Outstanding', value: 5 }
];
}
#Component({
template: `<div>Comp1</div>`
})
export class RadioListImplComponentONE extends RadioListAwareComponent{}
#Component({
template: `<div>Comp2</div>`
})
export class RadioListImplComponentTWO extends RadioListAwareComponent{}
You can make your own factory and inject it into your controller https://docs.angularjs.org/guide/providers