Treeview consuming three different endpoints with different results

Hello,
I have a case where I want to use the tree view component in the following way:
I have 3 levels of data that are being acquired by three different endpoints. Only Level 3 has checkboxes.
When I click the checkboxes - api calls will be made.

Models:
- Account: { id, name, ... }
- Directory: { id, name, ... }
- Subdirectory: { id, name, ... }

API routes:
(1) Accounts: /api/GetUserAccounts?userId=1
(2) Directories: /api/GetUserDirectories?userId=1&accountId=1
(3) Subdirectories: /api/GetUserSubdirectories?userId=1&accountId=1&directoryId=1

Desired behavior:
NOTE: UserID is already available.
- On init it should load Accounts (Level1)
- On Level1 expand - Ajax is fired and fetching Directory (Level2) Data (this ajax uses Account.id)
- On Level2 expand - Ajax is fired and fetching Subdirectory (Level3) Data (Api Call uses Account.id and Directory.id)

What would be the correct approach for me tackling this problem?

I would like to leave creating new endpoints as last resort, but if there's no other way ... I would like some points on what format the data should be returned.

I tried creating two DataManagers (aiming to show at least level2), but when I 'expanded' the row - it never made the secondary api call.

3 Replies

SP Sowmiya Padmanaban Syncfusion Team September 10, 2020 01:54 PM UTC

Hi Martin Majedov,  
 
Greetings from Syncfusion support. 
 
We have checked your requirement with TreeView component. TreeView has load on demand (Lazy load) functionality which is enabled in default. It reduces the bandwidth size when consuming huge data. It loads first level nodes initially, and when parent node is expanded, loads the child nodes based on the parentID/child member. 
 
When rendering the TreeView datasource using our DataManager component, it send the request to the server side ( for example: corresponding parent nodeId ) when expanding each parent node of TreeView component to fetch its corresponding child nodes.  
 
Based on your requirement , you can filter the data in server side and return the datasource to the TreeView component. 
 
Refer the below code snippet for defining the TreeView component to the client side. 
 
<div class="control_wrapper"> 
        <ejs-treeview id="main-menubar" :fields="fields" ref="treeviewInstance"></ejs-treeview> 
    </div> 
  data () {   
    var data = new DataManager({ 
            url: 'https://localhost:44356/Home/Get', 
            adaptor: new WebApiAdaptor, 
            crossDomain: true, 
             }); 
    return { 
      fields: { 
        dataSource: data,  id: 'prog_id', text: 'prog_name', parentID: 'pid', hasChildren: 'hasChild' 
      } 
    } 
}, 
 
Server side to fetch the data based on the corresponding parent id. 
 
  [HttpGet] 
        public IEnumerable<OrdersDetails> Get() 
        { 
            var data = OrdersDetails.GetAllRecords().ToList(); 
            var queryString = Request.Query; 
            if (queryString.Keys.Contains("$filter")) 
            { 
 
                string filter = string.Join("", queryString["$filter"].ToString().Split(' ').Skip(2)); // get filter from querystring 
                // filter the data based on the expand node id. 
                data = data.Where(d => d.pid.ToString() == filter).ToList(); 
                return data; 
            } 
            else 
            { 
                // if the parent id is null. 
                data = data.Where(d => d.pid == null).ToList(); 
                return data; 
            } 
        } 
 
Please, refer the below video footage for the work flow of TreeView component. 
 
 
Refer the below sample and Server side. 
 
 
Server side to fetch the datasource for TreeView component: https://www.syncfusion.com/downloads/support/forum/157746/ze/Controller_data1582780537.zip 
 
Note: Run the service side and refer the localhost URL in sample. 
 
Please, refer the following links to know more about the TreeView component. 
  
UG Documentation 
 
Demo link 
 
Remote data  
 
API reference 
 
 
Please let us know, if you need any further assistance.  
  
Regards,   
Sowmiya.P  



MM Martin Majedov September 10, 2020 03:28 PM UTC

Thanks for your answer, but it covers my question only partially.

It works flawlessy when I want to load the initial data.
So far so good.

When I start to dig down further (Read: Expand to Level 2) I need additional data from the object that's being expanded.

Example:
Level 2 has - id and parentID.
To get it's children I need to pass to the data manager additional parameters - id (Level2 ID).


I will try to be as specific as possible in the following example:
Data Structure: 
Level1: Account
{
"id":"5f3d0cfff089140001103e69",
"accountId":"5f3d093ef089140001103d28",
"name":"Martin"
}

Level2: Directory
{
"id": "2b5e1ffff051230805642a77",
"name": "Storage",
}

Level3: Subdirectory
{
"id": "23d093eff0f0891482642f089",
"name": "Misc",
}

CODE:
const getToken = () => localStorage.getItem('secret_key');
const getAccountsManager = (customerId: string) => new DataManager({
url: `https://localhost:44356/api/Home/GetAccounts?customerId=${customerId}`,
adaptor: new WebApiAdaptor(),
headers: [
{ Authorization: getToken() ? 'Bearer ' + getToken() : null }
]
});

const getDirectoriesManager = (customerId: string) => new DataManager({
url: `https://localhost:44356/api/Home/GetDirectories?customerId=${customerId}`,
adaptor: new WebApiAdaptor(),
headers: [
{ Authorization: getToken() ? 'Bearer ' + getToken() : null }
]
});

const getSubdirectoriesManager = (customerId: string) => new DataManager({
url: `https://localhost:44356/api/Home/GetSubdirectories?customerId=${customerId}`,
adaptor: new WebApiAdaptor(),
headers: [
{ Authorization: getToken() ? 'Bearer ' + getToken() : null }
]
});

let customerId = 'customer-id-fetched-from-vuex';
get fields() {
return {
dataSource: getAccountsManager(this.customerId),
id: 'id',
text: 'name',
hasChildren: 'accountId'
child: {
dataSource: getDirectoriesManager(this.customerId),  // I have customerId and Account.accountId in $filter, but I need Account.id as well
id: 'id',
text: 'name',
parentID: 'accountId'
child: {
dataSource: getSubdirectoriesManager(this.customerId),
id: 'id',
text: 'name',
parentID: "id"
},
}
}
};

I think bottom line is: I need to pass additional properties from nodes' origin object.
Is this achievable? And if so - how :)


SA Shameer Ali Baig Sulaiman Ali Baig Syncfusion Team September 14, 2020 10:14 AM UTC

Hi Martin Majedov,  
 
From your provided information, we understood that you are expecting to pass the additional value when expanding the TreeView node. You can achieve this requirement y using nodeExpanding event of TreeView component. 
 
When you set offline as true in DataManager, it does not trigger the sever side on expanding the TreeView node.  
 
  data () {   
    var remotedata = new DataManager({ 
            url: 'https://localhost:44376/Home/Get', 
            adaptor: new WebApiAdaptor, 
            offline: true 
             }); 
    return { 
        fields: { dataSource: remotedata, id: 'prog_id', text: 'prog_name', hasChildren: 'hasChild', parentId:'pid'},  
    } 
}, 
 
Based on your scenario, you need to return only the parent node on initial loading using WebApiAdaptor. Refer the below screenshot. 
 
 
 
 
On expanding the node, trigger the ajax method for server side and send the additional parameter to the server side and return the data based on your requirement and add the returned data to the TreeView node using addNodes method.  
 
nodeExpanding : function(args) { 
  if (args.isInteracted) { 
    // send the account id only on second level. 
    if(args.node.classList.contains("e-level-1")) { 
        var obj = { user: "1", account:args.nodeData.id }; 
    } 
    // send the account id and subdirectory id on third level. 
    else if(args.node.classList.contains("e-level-2")) { 
      var obj = { user: "1", account: args.nodeData.id,subDirectory :args.nodeData.parentID }; 
    } 
     $.ajax({ 
        type: "POST", 
        contentType: "application/json; charset=utf-8", 
        // server side method to fetch the data. 
        url: "https://localhost:44376/Home/Update", 
                data: JSON.stringify(obj), 
                dataType: "json", 
        success: function(data) { 
          var tree = document.getElementById("treeview").ej2_instances[0]; 
          // Add the child data to the corresponding parent node. 
          tree.addNodes(data, args.nodeData.id); 
        }, 
        error: function(xhr, err) {} 
      }); 
  } 
} 
 
Refer the below code for server side method. 
 
  [HttpPost] 
        public IActionResult Update([FromBody] customclass details) 
        { 
            var data1 = OrdersDetails.GetAllRecords().ToList(); 
            // Fetch the data based on corresponding id. 
            data1 = data1.Where(d => d.user == details.user && d.account == details.account && d.subDirectory == details.subDirectory).ToList(); 
            return Json(data1); 
        } 
 
Please, download the sample and servers side operations in following link. 
 
 
 
Please let us know, if you need any further assistance. 
 
Regards,  
Shameer Ali Baig S. 


Loader.
Up arrow icon