You are here:   Blog
Register   |  Login

 

Dec 30

Written by: Michael Washington
12/30/2016 4:29 PM  RssIcon

This article covers an application that implements a full CRUD (Create Read Update Delete) Angular 2 Tree application.

image

This makes extensive use of the free open source PrimeNG Angular 2 components.

image

We start with the application created in the article: Tutorial: Creating An Angular 2 CRUD Application Using MVC 5 and OData 4.

The Database

image

When we open the project in Visual Studio and open the database in the App_Data folder…

image

We see the Nodes table that contains the data for the Tree.

image

When we open it, we see the schema for the table that was created using the following script:

 

CREATE TABLE [dbo].[Nodes] (
    [Id]         INT                IDENTITY (1, 1) NOT NULL,
    [NodeName]   NVARCHAR (255)     DEFAULT ('') NOT NULL,
    [CreatedBy]  NVARCHAR (255)     NULL,
    [Created]    DATETIMEOFFSET (7) NULL,
    [ModifiedBy] NVARCHAR (255)     NULL,
    [Modified]   DATETIMEOFFSET (7) NULL,
    [RowVersion] ROWVERSION         NOT NULL,
    [Node_Node]  INT                NULL,
    PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT [Node_Node] FOREIGN KEY ([Node_Node]) REFERENCES [dbo].[Nodes] ([Id])
);

image

When we open the Angular2QuickStartDAL.edmx file we see the data context created to allow us to programmatically interact with the database.

The Generic File Handler (To display Tree Data)

image

The data for a Tree can have several nested nodes. To display the nodes, we require code that uses recursive functions.

The generic file handler will be called by the Angular code.

The code is as follows:

 

using Angular2QuickStart.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Script.Serialization;
namespace LightSwitchApplication.Web
{
    /// <summary>
    /// Get the data and creates a JSON feed for the Tree control
    /// </summary>
    /// 
    public class TreeData : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            // This connects to the database
            using (Entities db = new Entities())
            {
                // Collection to hold final TreeNodes
                List<DTONode> colTreeNodes = new List<DTONode>();
                // This returns all Nodes in the database
                var colNodes = (from objNode in db.Nodes
                                select new DataNode
                                {
                                    Data = objNode.Id.ToString(),
                                    NameName = objNode.NodeName,
                                    NodeParentData = objNode.Node1.Id.ToString()
                                }).ToList();
                // Loop through Parent 'root' nodes
                // (meaning the NodeParentData is blank)
                foreach (DataNode objNode in colNodes
                    .Where(x => x.NodeParentData == ""))
                {
                    // Create a new Node
                    DTONode objNewNode = new DTONode();
                    objNewNode.data = objNode.Data;
                    objNewNode.label = objNode.NameName;
                    objNewNode.expandedIcon = "fa-folder-open";
                    objNewNode.collapsedIcon = "fa-folder";
                    objNewNode.parentId = 0;
                    objNewNode.children = new List<DTONode>();
                    colTreeNodes.Add(objNewNode);
                    // Add Child Nodes
                    AddChildren(colNodes, colTreeNodes, objNewNode);
                }
                // Create JavaScriptSerializer
                JavaScriptSerializer jsonSerializer = new JavaScriptSerializer();
                // Output as JSON
                context.Response.Write(jsonSerializer.Serialize(colTreeNodes));
            }
        }
        #region AddChildren
        private void AddChildren(
            List<DataNode> colNodeItemCollection,
            List<DTONode> colTreeNodeCollection,
            DTONode paramTreeNode)
        {
            // Get the children of the current item
            // This method may be called from the top level 
            // or recursively by one of the child items
            var ChildResults = from objNode in colNodeItemCollection
                               where objNode.NodeParentData == paramTreeNode.data
                               select objNode;
            // Loop thru each Child of the current Node
            foreach (var objChild in ChildResults)
            {
                // Create a new Node
                var objNewNode = new DTONode();
                objNewNode.data = objChild.Data;
                objNewNode.label = objChild.NameName;
                objNewNode.expandedIcon = "fa-folder-open";
                objNewNode.collapsedIcon = "fa-folder";
                objNewNode.parentId = Convert.ToInt32(paramTreeNode.data);
                objNewNode.children = new List<DTONode>();
                // Search for the Node in colTreeNodeCollection
                // By looping through each 'root' Node
                // (meaning the NodeParentData is blank)
                foreach (DataNode objNode in colNodeItemCollection
                    .Where(x => x.NodeParentData == ""))
                {
                    // See if Parent is in the colTreeNodeCollection
                    DTONode objParent =
                        colTreeNodeCollection.Where(x => x.data == objNode.Data).FirstOrDefault();
                    if (objParent != null) // Parent exists in the colTreeNodeCollection
                    {
                        // Get the Parent Node for the current Child Node
                        DTONode objParentTreeNode = objParent.Descendants()
                            .Where(x => x.data == paramTreeNode.data).FirstOrDefault();
                        if (objParentTreeNode != null)
                        {
                            // Add the Child node to the Parent
                            objParentTreeNode.children.Add(objNewNode);
                        }
                    }
                }
                //Recursively call the AddChildren method adding all children
                AddChildren(colNodeItemCollection, colTreeNodeCollection, objNewNode);
            }
        }
        #endregion
        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

image

If we have data in the database, when we run the application, and look at the output in an application like Fiddler, we can see that the code returns the nodes in a nested format.

Install PrimeNG

image

We built the application using the free open source suite of components called PrimeNG.

The setup directions are here:

http://www.primefaces.org/primeng/#/setup

image

In our application, we first added:

"primeng": "^1.0.0"
to the package.json file.

image

Then we added:

'primeng': 'npm:primeng'

and:

      primeng: {
          defaultExtension: 'js'
      }
to the systemjs.config.js file.

 

image

Next, we downloaded and installed Font Awesome.

image

Then we added the following lines to _Layout.cshtml:

    <link rel="stylesheet" 
          type="text/css" 
          href="node_modules/primeng/resources/themes/omega/theme.css" />
    <link rel="stylesheet" 
          type="text/css" 
          href="node_modules/primeng/resources/primeng.min.css" />
    <link href="~/Content/font-awesome.min.css" rel="stylesheet" />

Enable PrimeNG

image

To enable PrimeNG in our application, we open the app.module.ts file and add:

 

import {
    InputTextModule,
    DropdownModule,
    ButtonModule,
    FieldsetModule,
    TreeModule,
    TreeNode,
    SelectItem
} from 'primeng/primeng';

 

and:

 

        InputTextModule,
        TreeModule,
        DropdownModule,
        ButtonModule,
        FieldsetModule 

 

Display The Tree

image

To display the Tree, we create a file, NodeService.ts, that calls the generic file handler we created earlier, using the following code:

 

// Angular Imports
import {
    Injectable
} from '@angular/core';
import {
    Http, Response, RequestOptions,
    Request, RequestMethod, Headers
} from '@angular/http';
import 'rxjs/add/operator/toPromise';
import { TreeNode } from 'primeng/primeng';
@Injectable()
export class NodeService {
    // This is the URL to the end point
    private _treenodeUrl = 'web/TreeData.ashx';
    // Pass the Http object to the class 
    // through the constructor
    constructor(private _http: Http) {
    }
    getFiles() {
        // Call the data end point
        // and return the Tree nodes
        return this._http.get(this._treenodeUrl)
            .toPromise()
            .then(res => <TreeNode[]>res.json())
            .then(data => {
                return data;
            });
    }
}

 

image

We then create a file, tree.component.html using the following code:

 

<!-- Error (if any) -->
<div class="ui-button-danger" *ngIf="errorMessage">
    <p>{{ errorMessage }}</p>
</div>
<!-- Tree Node Control -->
<p-tree [value]="treeNodes" 
        selectionMode="single" 
        (onNodeSelect)="nodeSelect()" 
        [(selection)]="selectedNode"></p-tree>
<br />

image

We create a file, tree.component.ts using the following code:

 

import {
    Component, OnInit, OnDestroy, Input, Output,
    ViewContainerRef, EventEmitter, ViewChild, trigger
} from '@angular/core';
import {
    Router, ActivatedRoute
} from '@angular/router';
import {
    Subscription
} from 'rxjs/Subscription';
import {
    NodeService
} from './NodeService';
import {
    InputTextModule,
    DropdownModule,
    ButtonModule,
    FieldsetModule,
    TreeModule,
    TreeNode,
    SelectItem
} from 'primeng/primeng';
@Component({
    moduleId: module.id,
    selector: 'tree-form',
    templateUrl: 'tree.component.html'
})
export class TreeComponent implements OnInit {
    treeNodes: TreeNode[];
    EditModeLabel: string = "Edit Node";
    // Any error messages
    errorMessage: string;
    selectedNode: TreeNode;
    editNodeName: string;
    nodeParents: TreeNode[];
    selectedNodeParent: SelectItem;
    nodeParentsDropdown: SelectItem[] = [];
    // Contructor is called when the class is created
    constructor(
        private _NodeService: NodeService) { }
    // Handle data related tasks in ngOnInit
    ngOnInit() {
        this.populateTree();
    }
    populateTree() {
        // Call the service to update the Tree
        this._NodeService.getFiles().then(files => {
            // Set the data source for the Tree
            this.treeNodes = files;
        });
    }
}

 

image

Finally, we add the following lines to app.module.ts:

 

import { NodeService } from './tree/NodeService';

 

and:

 

    providers: [
        NodeService
    ],

 

image

If we have data in the database, when we run the application, we will see the Tree.

The Dropdown and the Form

image

There is a form, that contains a dropdown of the nodes, that allows you to create and edit the Tree nodes.

The HTML mark-up for the form and dropdown are as follows:

 

<!-- Edit Form -->
<p-fieldset legend="{{ EditModeLabel }}">
<label for="NodeName">Node Name</label>
<input type="text"
       id="NodeName"
       required
       [(ngModel)]="editNodeName"
       name="NodeName"
       #name="ngModel">
<br />
<label for="NodeParent">Node Parent</label>
<p-dropdown id="NodeParent" 
            [options]="nodeParentsDropdown" 
            [(ngModel)]="selectedNodeParent" 
            [style]="{'width':'150px'}"></p-dropdown>
<br />
<button pButton type="button" 
        (click)="Save()" 
        label="Save"></button>
<button pButton type="button" 
        (click)="NewNode()" 
        label="New" 
        class="ui-button-success"></button>
<button pButton type="button" 
        (click)="DeleteNode()" 
        label="Delete" 
        class="ui-button-danger"></button>
</p-fieldset>

 

The code to fill the dropdown is as follows:

 

   populateDropdown() {
        // Call the service
        this._treeService.getTreeNodes()
            .subscribe((nodes) => {
                // Clear the list
                this.nodeParentsDropdown = [];
                // Loop through the returned Tree Nodes
                for (let node of nodes) {
                    // Create a new SelectedItem
                    let newSelectedItem: SelectItem = {
                        label: node.label,
                        value: node.data
                    }
                    // Add Selected Item to the DropDown
                    this.nodeParentsDropdown.push(newSelectedItem);
                }
                // Set the selected option to the first option
                this.selectedNodeParent = this.nodeParentsDropdown[0];
            },
            error => this.errorMessage = <any>error);
    }

 

This code calls the _treeService class, that communicates with the back-end OData service. That code is as follows:

 

// Angular Imports
import {
    Injectable
} from '@angular/core';
import {
    Http, Response, RequestOptions,
    Request, RequestMethod, Headers
} from '@angular/http';
// OData IDTONode class
import {
    IDTONode
} from './DTONode';
// OData IDTODataNode class
import {
    IDTODataNode
} from './DTODataNode';
// This service uses rxjs Observable
import {
    Observable
} from 'rxjs/Observable';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
// This is marked Injectable because it will be 
// consumed in the Controller class
@Injectable()
export class TreeService {
    // This is the URL to the OData end points
    private _createTreeNodeUrl = 'odata/ODataNodes';
    private _updateTreeNodeUrl = 'odata/DTODataNodes';
    // Pass the Http object to the class 
    // through the constructor
    constructor(private _http: Http) { }
    // ** Get all Tree Nodes **
    getTreeNodes(): Observable<IDTONode[]> {
        // Make the Angular 2 Get
        // Note that we must use .value
        // because the OData response is wrapped in an object
        // and the data we want to map is at .value
        return this._http.get(this._createTreeNodeUrl)
            .map((response: Response) => <IDTONode[]>response.json().value)
            .catch(this.handleError);
    }
    // ** Called when there are any errors **
    private handleError(error: Response) {
        console.error(error);
        return Observable.throw(error.json().error || 'Server error');
    }
}

 

The code for the server-side OData service that communicates with the database (through the .edmx DataContext class) and returns the data for the dropdown is as follows:

 

using System;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web.Http;
using System.Web.OData;
using Angular2QuickStart.Models;
using System.Collections.Generic;
namespace Angular2QuickStart.Controllers
{
    public class ODataNodesController : ODataController
    {
        // This connects to the database
        private Entities db = new Entities();
        #region public IEnumerable<DTONode> GetODataNodes()
        // GET: odata/GetODataNodes
        public IEnumerable<DTONode> GetODataNodes()
        {
            // Collection to hold final TreeNodes
            List<DTONode> colTreeNodes = new List<DTONode>();
            // This returns all Nodes in the database
            var colNodes = (from objNode in db.Nodes
                            select new DataNode
                            {
                                Data = objNode.Id.ToString(),
                                NameName = objNode.NodeName,
                                NodeParentData = objNode.Node1.Id.ToString()
                            }).OrderBy(x => x.NodeParentData).ThenBy(y => y.NameName).ToList();
            // Create a '[None]' Node
            DTONode objNoneNode = new DTONode();
            objNoneNode.data = "0";
            objNoneNode.label = "[None]";
            objNoneNode.parentId = 0;
            colTreeNodes.Add(objNoneNode);
            // Loop through Parent 'root' nodes
            // (meaning the NodeParentData is blank)
            foreach (DataNode objNode in colNodes
                .Where(x => x.NodeParentData == ""))
            {
                // Create a new Node
                DTONode objNewNode = new DTONode();
                objNewNode.data = objNode.Data;
                objNewNode.label = objNode.NameName;
                if (objNode.NodeParentData != "")
                {
                    objNewNode.parentId = Convert.ToInt32(objNode.NodeParentData);
                }
                colTreeNodes.Add(objNewNode);
                // Add Nodes
                AddNodes(colNodes, colTreeNodes, objNewNode);
            }
            // This is not Queryable because we need the list
            // to stay in the correct order (by NodeParentData)
            return colTreeNodes.ToList();
        }
        #endregion
        // Utility
        #region AddNodes
        private void AddNodes(
            List<DataNode> colNodeItemCollection,
            List<DTONode> colTreeNodeCollection,
            DTONode paramTreeNode)
        {
            // Get the children of the current item
            // This method may be called from the top level 
            // or recuresively by one of the child items
            var ChildResults = from objNode in colNodeItemCollection
                               where objNode.NodeParentData == paramTreeNode.data
                               select objNode;
            // Loop thru each Child of the current Node
            foreach (var objChild in ChildResults)
            {
                // Create a new Node
                var objNewNode = new DTONode();
                objNewNode.data = objChild.Data;
                // See if there is a Parent
                if (objChild.NodeParentData != "")
                {
                    // Set the Parent
                    objNewNode.parentId = Convert.ToInt32(objChild.NodeParentData);
                    // Get the Parent
                    DTONode objParent =
                        colTreeNodeCollection.Where(x => x.data == objChild.NodeParentData).FirstOrDefault();
                    // See how many dots the Parent has
                    int CountOfParentDots = objParent.label.Count(x => x == '.');
                    // Update the label to add dots in front of the name
                    objNewNode.label = $"{AddDots(CountOfParentDots + 1)}{objChild.NameName}";
                }
                else
                {
                    // There was no parent so don't add any dots
                    objNewNode.label = objChild.NameName;
                }
                colTreeNodeCollection.Add(objNewNode);
                //Recursively call the AddChildren method adding all children                
                AddNodes(colNodeItemCollection, colTreeNodeCollection, objNewNode);
            }
        }
        #endregion
        #region AddDots
        private static string AddDots(int intDots)
        {
            String strDots = "";
            for (int i = 0; i < intDots; i++)
            {
                strDots += ". ";
            }
            return strDots;
        }
        #endregion
        #region protected override void Dispose(bool disposing)
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                // Dispoase of the database object
                db.Dispose();
            }
            base.Dispose(disposing);
        }
        #endregion
    }
}

 

The Remaining CRUD Methods

image

The form allows users to create new nodes, select and update existing nodes, and to delete nodes.

The methods that provide that functionality are as follows:

 

    nodeSelect() {
        // We are editing
        this.EditModeLabel = "Edit Node";
        // Set the edit node label
        this.editNodeName = this.selectedNode.label
        // Set the Edit Node parent
        var selectedTreeNode: SelectItem;
        if (this.selectedNode.parent !== undefined) {
            selectedTreeNode = this.nodeParentsDropdown.find(x => x.value == this.selectedNode.parent.data);
        } else {
            // Select the '[None]' node
            // It is always the first option in the list
            selectedTreeNode = this.nodeParentsDropdown[0];
        }
        // Set the selected option to update the DropDown
        this.selectedNodeParent = selectedTreeNode.value;
    }
    NewNode() {
        // We are creating a new node
        this.EditModeLabel = "New Node";
        // Set the Edit Node parent
        var selectedTreeNode: SelectItem;
        if (this.selectedNode !== undefined) {
            selectedTreeNode = this.nodeParentsDropdown.find(x => x.value == this.selectedNode.data);
            if (selectedTreeNode !== undefined) {
                // Set the selected option to update the DropDown
                this.selectedNodeParent = selectedTreeNode.value;
            }
        } 
        // Set selectedNode to a new TreeNode
        this.selectedNode = this.createNewTreeNode();
        // Set the edit node label
        this.editNodeName = this.selectedNode.label
    }
    createNewTreeNode() {
        // Create a new TreeNode
        let newTreeNode: TreeNode = {
            label: "",
            data: -1 // So we know it is a new Node
        }
        return newTreeNode;
    }
    Save() {
        this.errorMessage = "";
        // Create an IDTODataNode
        // This will be used to update the database
        let objTreeNode: IDTODataNode = {
            Id: this.selectedNode.data,
            NodeName: this.editNodeName,
            ParentId: 0
        }
        // Is this a new TreeNode?
        if (objTreeNode.Id == -1) {
            // Set the ParentId
            objTreeNode.ParentId =
                Number(this.selectedNodeParent.value
                    ? this.selectedNodeParent.value
                    : this.selectedNodeParent);
            // Call the service to Insert the TreeNode
            this._treeService.createTreeNode(objTreeNode)
                .subscribe(() => {
                    // Refresh 
                    this.populateTree();
                    this.populateDropdown();
                    // Set NewNode Mode
                    this.NewNode();
                },
                error => this.errorMessage = <any>error);
        } else {
            // A Node cannot be set as a parent to itself
            if (this.selectedNodeParent !== this.selectedNode.data) {
                // Set the ParentId
                objTreeNode.ParentId = Number(this.selectedNodeParent);
                // Call the service to update the TreeNode
                this._treeService.updateTreeNode(objTreeNode)
                    .subscribe(() => {
                        // Refresh 
                        this.populateTree();
                        this.populateDropdown();
                        // Set NewNode Mode
                        this.NewNode();
                    },
                    error => this.errorMessage = <any>error);
            }
        }
    }
    DeleteNode() {
        this.errorMessage = "";
        // Get TreeNode
        var NodeId: number = Number(this.selectedNode.data);
        // Only a Node Id other than -1 can be deleted 
        if (NodeId > -1) {
            // Call the service to delete the Node
            this._treeService.deleteTreeNode(NodeId)
                .subscribe(() => {
                    // Refresh 
                    this.populateTree();
                    this.populateDropdown();
                    // Set NewNode Mode
                    this.NewNode();                    
                },
                error => this.errorMessage = <any>error);
        }
    }
    expandAll() {
        this.treeNodes.forEach(node => {
            this.expandRecursive(node, true);
        });
    }
    private expandRecursive(node: TreeNode, isExpand: boolean) {
        node.expanded = isExpand;
        if (node.children) {
            node.children.forEach(childNode => {
                this.expandRecursive(childNode, isExpand);
            });
        }
    }

 

This code calls methods in the _treeService class. Those methods are as follows:

 

    // ** Create a Tree Node **
    createTreeNode(paramtreenode: IDTODataNode): Observable<IDTODataNode> {
        // This is a Post so we have to pass Headers
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });
        // Make the Angular 2 Post
        return this._http.post(this._updateTreeNodeUrl,
            JSON.stringify(paramtreenode), options)
            .map((response: Response) => <IDTODataNode>response.json())
            .catch(this.handleError);
    }
    // ** Update a Tree Node **
    updateTreeNode(paramtreenode: IDTODataNode): Observable<void> {
        // This is a Put so we have to pass Headers
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });
        // Make the Angular 2 Put
        return this._http.put(
            this._updateTreeNodeUrl + "(" + paramtreenode.Id + ")",
            JSON.stringify(paramtreenode), options)
            .catch(this.handleError);
    }
    // ** Delete a Tree Node **
    deleteTreeNode(id: number): Observable<void> {
        // A Delete does not return anything
        return this._http.delete(this._updateTreeNodeUrl + "(" + id + ")")
            .catch(this.handleError);
    }

 

These methods call the server-side OData methods. Those methods are as follows:

 

 

        // This connects to the database
        private Entities db = new Entities();
        #region public IHttpActionResult Post(DTODataNode DTODataNode)
        // POST: odata/ODataNodes
        public IHttpActionResult Post(DTODataNode DTODataNode)
        {
            // Create a new Node
            var NewNode = new Node();
            NewNode.NodeName = DTODataNode.NodeName;
            if (DTODataNode.ParentId > 0)
            {
                NewNode.Node_Node = DTODataNode.ParentId;
            }
            // Save the Node
            db.Nodes.Add(NewNode);
            db.SaveChanges();
            // Populate the ID that was created and pass it back
            DTODataNode.Id = NewNode.Id;
            // Return the Node
            return Created(DTODataNode);
        }
        #endregion
        #region public IHttpActionResult Put([FromODataUri] int key, DTODataNode DTODataNode)
        // PUT: odata/ODataNodes(1)
        public IHttpActionResult Put([FromODataUri] int key, DTODataNode DTODataNode)
        {
            // Get the existing Node using the key that was passed
            Node ExistingNode = db.Nodes.Find(key);
            // Did we find a Node?
            if (ExistingNode == null)
            {
                // If not return NotFound
                return StatusCode(HttpStatusCode.NotFound);
            }
            // Update the Node 
            ExistingNode.NodeName = DTODataNode.NodeName;
            if (DTODataNode.ParentId > 0)
            {
                ExistingNode.Node_Node = DTODataNode.ParentId;
            }
            else
            {
                ExistingNode.Node_Node = null;
            }
            // Save changes
            db.Entry(ExistingNode).State = EntityState.Modified;
            db.SaveChanges();
            // Return the Updated Node
            // Return that the Node was Updated
            return Updated(ExistingNode);
        }
        #endregion
        #region public IHttpActionResult Delete([FromODataUri] int key)
        // DELETE: odata/ODataNodes(1)
        public IHttpActionResult Delete([FromODataUri] int key)
        {
            // Get the existing Node using the key that was passed
            Node ExistingNode = db.Nodes.Find(key);
            // Did we find a Node?
            if (ExistingNode == null)
            {
                // If not return NotFound
                return StatusCode(HttpStatusCode.NotFound);
            }
            int? ParentNodeID = null;
            // Possibly update Child Nodes
            if (ExistingNode.Node_Node.HasValue)
            {
                // Get the Parent Node of the ExistingNode
                ParentNodeID = ExistingNode.Node_Node.Value;
            }
            // Get the children of the current item
            var ChildResults = from objNode in db.Nodes
                               where objNode.Node_Node.Value == ExistingNode.Id
                               where objNode.Node_Node.HasValue == true
                               select objNode;
            // Loop thru each Child of the current Node
            foreach (var objChild in ChildResults)
            {
                // Update the Parent Node
                // for the Child Node
                objChild.Node_Node = ParentNodeID;
            }
            // Delete the Node
            db.Nodes.Remove(ExistingNode);
            // Save changes
            db.SaveChanges();
            // Return a success code
            return StatusCode(HttpStatusCode.NoContent);
        }
        #endregion

 

 

Links

PrimeNG

Font Awesome

Angular 2 Tutorial Series

  1. Hello World! in Angular 2 using Visual Studio 2015 and ASP.NET 4
  2. Implement ASP.NET 4 MVC Application Security in Angular 2 Using OData 4
  3. Tutorial: Creating An Angular 2 CRUD Application Using MVC 5 and OData 4
  4. Tutorial: An End-To-End Angular 2 Application Using MVC 5 and OData 4

Resources to Learn Angular 2

Angular 2: Getting Started (Pluralsight – Paid)

Introduction to Angular 2.0 (Microsoft Virtual Academy – Free)

Getting Started with npm in Visual Studio

Using EcmaScript 2015 Modules in TypeScript with SystemJS

Download

The project is available at http://lightswitchhelpwebsite.com/Downloads.aspx

You must have Visual Studio 2015 Update 3 (or higher) and TypeScript 2.0 (or higher) installed to run the code.

Microsoft Visual Studio is a registered trademark of Microsoft Corporation / LightSwitch is a registered trademark of Microsoft Corporation