This is a migrated thread and some comments may be shown as answers.

I need help with some requirements for a TreeView configuration

5 Answers 398 Views
TreeView
This is a migrated thread and some comments may be shown as answers.
Richard
Top achievements
Rank 1
Richard asked on 05 Jun 2019, 03:50 PM

Hi there,

I've been searching and reading for 2 days now and I've some doubts about the TreeView that I need to clarify.

1. The tree will have three levels of deep

    + Contenedor "zst"
    |
    | --- Bulto 1. Largo: 2.6 Ancho: 3.6 Alto: 5.3
    |     |
    |     --- Producto: "Table"    Cantidad: 32
    |     --- Producto: "Computer" Cantidad: 25
    |
    | --- Bulto 2. Largo: 1.3 Ancho: 8.3 Alto: 2.2
    |     |
    |     --- Producto: "Mouse"    Cantidad: 8
    |     --- Producto: "Keyboard" Cantidad: 7
    |
    + Contenedor "trx"
      |
      |
      --- Bulto 1. Largo: 4.2 Ancho: 13.1 Alto: 15.7
          |
          --- Producto: "Display"    Cantidad: 7.7
          --- Producto: "Battery"    Cantidad: 2.3


What follows are questions that I don't have answers so far and I don't know whether it's possible to achieve using this widget

1. I need to apply a different template to every level: 
   a) In the first level (Contenedor) I need to show the word "Contenedor" plus a string specified by the user
   b) In the second level (Bulto) I need to show the word "Bulto" plus "Largo" followed by a number then the word "Ancho" followed by a number and the word "Alto" followed by a number
   c) In the third level (Producto) I need to show the word "Producto" plus a string then the word "Cantidad" followed by a number

2. In every level of the tree the user can edit the values:
   a) In the first level (Contenedor) the user can change the description (a string)
   b) In the second level (Bulto) the user can change the "Largo" (decimal), "Ancho" (decimal) and "Alto" (decimal)
   c) In the third level (Producto) the user can change "Cantidad" (decimal).

3. The TreeView must support drag & drop between it and a Grid widget so the user can drop the items from the tree in the grid and items from the grid in the Tree.

4. How do I get the model associated to a node in the Tree? If possible I would like to keep in the datasource the same members of the classes defined in the backend for each node in the Tree. I.e.

When I get the model of a node in the second level (Bulto) I would like to get this in the JavaScript object:

{
   ID: 1,
   NumeroBulto: 2,
   Largo: 43,
   Ancho: 23.3,
   Alto: 2.1,
   Volumen: 4.2,
}

 

Instead of the TreeViewItem object.

 

Here's my model

01.public class TreeModel
02.    {
03.        public List<ContenedorModel> Contenedores { get; set; }
04.    }
05. 
06.    public class ContenedorModel
07.    {
08.        public Guid ID { get; set; }
09.        public string Descripcion { get; set; }
10.        public List<BultoModel> Bultos { get; set; }
11.    }
12. 
13.    public class BultoModel
14.    {
15.        public Guid ID { get; set; }
16.        public int NumeroBulto { get; set; }
17.        public decimal Largo { get; set; }
18.        public decimal Ancho { get; set; }
19.        public decimal Alto { get; set; }
20.        public decimal Volumen { get; set; }
21.        public List<ProductoTreeModel> Productos { get; set; }
22.    }
23. 
24.    public class ProductoTreeModel
25.    {
26.        public Guid ID { get; set; }
27.        public string Descripcion { get; set; }
28.        public decimal Cantidad { get; set; }
29.    }

 

Here's my view

01.@(Html.Kendo().TreeView()
02.    .Name("treeView")
03.    .TemplateId("treeview-template-contenedor")
04.    .BindTo((IEnumerable<ContenedorModel>)Model.TreeModel.Contenedores, (NavigationBindingFactory<TreeViewItem> mappings) =>
05.    {
06.        mappings.For<ContenedorModel>(binding => binding.ItemDataBound((item, contenedor) =>
07.        {
08.            item.Text = $"Contenedor: {contenedor.Descripcion}";
09.        })
10.        .Children(x => x.Bultos));
11. 
12.        mappings.For<BultoModel>(binding => binding.ItemDataBound((item, bulto) =>
13.        {
14.            item.Text = $"Bulto: {bulto.NumeroBulto}";
15.        })
16.        .Children(x => x.Productos));
17. 
18.        mappings.For<ProductoTreeModel>(binding => binding.ItemDataBound((item, producto) =>
19.        {
20.            item.Text = producto.Descripcion;
21.        }));
22.    })
23.    .DragAndDrop(true)
24.)

 

Here's the template

 

1.<script id="treeview-template-contenedor" type="text/kendo-ui-template">
2.    <span style="color:blue">#: item.Text #</span>
3.    <button><span class='k-icon k-edit' /></button>
4.</script>

 

However the way I use the template here is applied to all levels.

 

Can anyone please help me with this?

 

Thank you.

5 Answers, 1 is accepted

Sort by
0
Petar
Telerik team
answered on 07 Jun 2019, 02:45 PM
Hi Richard,

Thank you for the detailed information and the provided code! 

Talking straight to the point, here are the answers to the asked questions:
Q1: The different text templates on every level could be set using this definition of a TreeView:
@(Html.Kendo().TreeView()
            .Name("treeView")
            .TemplateId("treeview-template-contenedor")
            .BindTo((IEnumerable<ContenedorModel>)ViewBag.inline, (NavigationBindingFactory<TreeViewItem> mappings) =>
            {
                mappings.For<ContenedorModel>(binding => binding.ItemDataBound((item, contenedor) =>
                {
                    item.Text = "Contenedor: " + contenedor.Descripcion;
                })
                .Children(x => x.Bultos));
 
                mappings.For<BultoModel>(binding => binding.ItemDataBound((item, bulto) =>
                {
                    item.Text = "Bulto: " + bulto.NumeroBulto + " Largo: " + bulto.Largo + " Ancho: " + bulto.Ancho + " Alto: " + bulto.Ancho;
                })
                .Children(x => x.Productos));
 
                mappings.For<ProductoTreeModel>(binding => binding.ItemDataBound((item, producto) =>
                {
                    item.Text = "Producto: " + producto.Descripcion + " Candidat: " + producto.Cantidad;
                }));
            })
            .DragAndDrop(true)
)

Q2: One of the possible approaches we could use for TreeView node editing is using a ContextMenu which opens a Kendo Window inside which we can edit the desired values. 
The definition of the ContextMenu could look like this:
@(Html.Kendo().ContextMenu()
                  .Name("menu")
                  .Target("#treeView")
                  .Filter(".k-in")
                  .Items(items => { items.Add().Text("Edit"); })
                  .Events(e => { e.Select("onSelect"); })
)

Then we will need to define a template for the popup window which will open on element node edit. As we are going to edit three different levels of the TreeView with different elements in it, then we will have to define three different popup Window templates. Here is the template for editing the Contenedor: value:
<script id="contenedorTemplate" type="text/x-kendo-template">
    <label id="popupHeader">
        <strong>
            Edit #= node.text.split(" ")[0]# #= node.text.split(" ")[1]#
        </strong>
    </label>
    <br />
    <label>
        Contenedor:
        <input id="contenedorValue" value="#= node.text.replace('Contenedor:', '').trim() #" />
    </label>
    <button class="k-button k-primary">Save</button>
</script>

And here is how our JavaScript should look like in order to edit and save the value of the first level nodes in the TreeView:
<script>
    var contenedorTemplate = kendo.template($("#contenedorTemplate").html());
 
    function onSelect(e) {
        var node = $("#treeView").getKendoTreeView().dataItem($(e.target).closest(".k-item"));
 
        var template;
        template = contenedorTemplate({
                node: node
        });
 
        // create and open Window
        $("<div />")
            .html(template)
            .appendTo("body")
            .kendoWindow({
                modal: true,
                visible: false,
                deactivate: function () {
                    this.destroy();
                }
            })
            // bind the Save button's click handler
            .on("click", ".k-primary", function (e) {
                e.preventDefault();
 
                var dialog = $(e.currentTarget).closest("[data-role=window]").getKendoWindow();
 
                    var contenedorValue = dialog.element.find("#contenedorValue").val();
                    node.set("text", "Contenedor: " + contenedorValue);
                     dialog.close();
                     console.log(node);
            })
            .getKendoWindow().center().open();
    }
</script>

The above works with a right click on a selected node and then "Edit". Attached to my reply you will find a runnable project demonstrating the described above functionalities + templates and editing functionality for all levels of the TreeView

Q3: Please check this Drag and Drop between Kendo UI Grid and Scheduler article. As the name of the article talks, it is demonstrating how we can drag and drop between the Grid and Scheduler components. Similar JavaScript/jQuery logic could be used for implementing the needed drag and drop functionality between the TreeView and the Grid

Q4: In the attached project it could be seen that after saving the data entered in the Window component, all properties of the current node are being printed in the console. Is this what you mean under "model associated to a node in the Tree"? If not, please specify. 
About the second question, the backend structure cannot be used out of the box the way it is defined in the backend. Custom functions which go through node's properties and generate the desired output could be implemented, but it is not a build-in functionality of the TreeView
 
Please, check the attached project and the description above and let me know if you have any questions. 

Regards,
Petar
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Richard
Top achievements
Rank 1
answered on 19 Jun 2019, 01:55 PM

Hi Petar,

Thank you for your answer it helped me a lot.

Currently I only have one more requirement and it's about the third question in my first post:

3. The TreeView must support drag & drop between it and a Grid widget so the user can drop the items from the tree in the grid and items from the grid in the Tree.

I saw the example in the link you sent me "https://docs.telerik.com/kendo-ui/controls/scheduling/scheduler/how-to/interaction/drag_and_drop_grid_scheduler" however I need to select multiple rows in the grid using the SHIFT key to select a range of rows and the CTRL key to select/unselect a specific row and in that example it's not possible to do. Can I do that with the Grid widget?

I've configured my grid to allow selecting more than one row (line 49, see de code at the bottom). However when I select more than one row in the hint function (line 12) I get only one row.

And of course when I drop the rows of the grid in the treeView I only get one row.

Why I'm not getting all the rows but only one?


Also I need to select multiple nodes in the tree using the SHIFT key to select a range of nodes and the CTRL key to select/unselect a specific node. Is this possible using the TreeView widget? Could you please give any advice about how to do that?

You can see the treeView configuration at line 71.

Here's the whole view

 

01.<script>
02.    $(window).on("load", () => {
03.        const grid = $("#grid_Productos").data("kendoGrid");
04.        const gridRowOffset = grid.tbody.find("tr:first").offset();
05. 
06.        grid.table.kendoDraggable({
07.            filter: "tbody > tr",
08.            dragstart: function (e) {
09.                //add margin to position correctly the tooltip under the pointer
10.                $("#dragTooltip").css("margin-left", e.clientX - gridRowOffset.left - 50);
11.            },
12.            hint: function (row) {
13.                console.log(row);
14. 
15.                ////remove old selection
16.                //row.parent().find(".k-state-selected").each(function () {
17.                //  $(this).removeClass("k-state-selected")
18.                //})
19. 
20.                //add selected class to the current row
21.                row.addClass("k-state-selected");
22. 
23.                var dataItem = grid.dataItem(row);
24. 
25.                var tooltipHtml = "<div class='k-event' id='dragTooltip'><div class='k-event-template'>" +
26.                    "</div><div class='k-event-template'>" + dataItem.Descripcion +
27.                    "</div></div>";
28. 
29.                return $(tooltipHtml).css("width", 300);
30.            }
31.        });
32.    })
33.</script>
34. 
35. 
36.@(Html.Kendo().Grid(Model.ProductosGridModel)
37.    .Name("grid_Productos")
38.    .Columns(columns =>
39.    {
40.        columns.Bound(c => c.IdExpedienteProducto).Hidden();
41.        columns.Bound(c => c.Descripcion)
42.            .Title("Producto")
43.            .Width(250);
44.        columns.Bound(c => c.Cantidad)
45.            .Format("{0:N2}")
46.            .HtmlAttributes(new { style = "text-align:right" })
47.            .Width(120);
48.    })
49.    .Selectable(x =>
50.    {
51.        x.Enabled(true);
52.        x.Mode(GridSelectionMode.Multiple);
53.        x.Type(GridSelectionType.Row);
54.    })
55.    .HtmlAttributes(new { style = "height: 100%; min-height: 200px;" })
56.    .Scrollable(x => x.Height(100))
57.    .Sortable(x => x.SortMode(GridSortMode.MultipleColumn))
58.    .Filterable()
59.    .DataSource(dataSource => dataSource
60.        .Ajax()
61.        .ServerOperation(false)
62.        .Batch(true)
63.        .Model(model =>
64.        {
65.            model.Id(p => p.IdExpedienteProducto);
66.        })
67.    )
68.)
69. 
70. 
71.@(Html.Kendo().TreeView()
72.    .Name("treeView")
73.    .TemplateId("treeview-template-contenedor")
74.    .BindTo((IEnumerable<ContenedorModel>)Model.TreeModel.Contenedores, (NavigationBindingFactory<TreeViewItem> mappings) =>
75.    {
76.        mappings.For<ContenedorModel>(binding => binding.ItemDataBound((item, contenedor) =>
77.        {
78.            item.Text = contenedor.Descripcion;
79.        })
80.        .Children(x => x.Bultos));
81. 
82.        mappings.For<BultoModel>(binding => binding.ItemDataBound((item, bulto) =>
83.        {
84.            item.Text = $"{bulto.NumeroBulto};{bulto.Largo};{bulto.Ancho};{bulto.Alto};{bulto.Volumen};{bulto.PesoBruto};{bulto.PesoNeto}";
85.        })
86.        .Children(x => x.Productos));
87. 
88.        mappings.For<ProductoTreeModel>(binding => binding.ItemDataBound((item, producto) =>
89.        {
90.            item.Text = $"{producto.IdExpedienteProducto};{producto.Descripcion};{producto.Cantidad}";
91.        }));
92.    })
93.    .DragAndDrop(true)
94.)


Thank you,

Richard

0
Petar
Telerik team
answered on 21 Jun 2019, 01:55 PM

Hi Richard, 

I am happy the provided information has helped you and thank you for the provided code!

Here are my answers to the asked questions: 

  • Q: Using Shift and CTRL keys when working with Grid – here is a demo demonstrating how to do it
  • Q: How to get all selected rows from the Grid and not only one of them – I would suggest using Grid select method for getting the currently selected rows instead of using the argument of the hint() function. 
  • Q: Select multiple nodes in the tree using the SHIFT key to select a range of nodes and the CTRL key to select/unselect a specific node – this functionality is not a built-in one for the TreeView widget. What I could suggest here is to use the select event of the TreeView and every time it is triggered to check if a given button is pressed. Depending on this condition and using the arguments of the select event you could select the desired nodes. If the arguments are defined as args in the select event, then the clicked node could be accessed using args.target. This approach should get the things working for the functionality with the Ctrl key. About the Shift key functionality, I am not sure if it is possible to be realized because of TreeView's architecture. I will discuss this case with my colleagues and will come back to you if there is a possibility to achieve it. 

As the discussed project is a complex one, if you need further assistance it would be easier to send us a runnable project on which we could test a given functionality. 

I hope the above information will help you resolve the pointed issues.

Regards,
Petar
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Richard
Top achievements
Rank 1
answered on 30 Jun 2019, 02:20 PM

Hi Petar,

Thank you for your answer, again it helped me a lot.

Currently I’ve implemented everything but the multi selection of nodes in the Tree widget.

In the tree the user can ONLY select nodes in the third level (Products) using the CTRL and SHIFT keys.

All the nodes selected in the third level must belong to the same parent. Two different parent aren’t allowed.

As you suggested I’ve sent you a runnable project at https://gofile.io/?c=5SXjNs

When you run the project the tree has some nodes so you don’t have to create them.

The only thing I need is that you write the code that allow the user to select the nodes in the tree and once the nodes are selected drop them into the grid.

When dropped in the grid I only need that you print the content of the dropped nodes in the web browser’s console. I don’t need the implementation of dropping them in the grid because I’ve it already implemented.

Thank you again,

Richard


0
Petar
Telerik team
answered on 02 Jul 2019, 02:43 PM
Hi Richard. 

I’ve downloaded the linked project but I am unable to run it. 

While reading the message, I’ve come with the idea that the desired functionality could be easily achieved using the TreeView component with checkboxes turned on. 
Here is an example in which the checkboxes are activated in the TreeView + demo how to get the IDs of the checked items. If you use this approach, you will be able to select multiple nodes no matter of their parents and in the same time you will know which nodes are the selected ones. 

I would recommend you using the approach demonstrated in the above example, because only the TreeView public API will be used with it.

Please let me know if the proposed approach will fit the needs of your application. If it is not appropriate, please isolate further the previously attached sample and remove any external dependencies and database calls from it. For demo purposes use instead sample data created on the server.

Regards,
Petar
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
Tags
TreeView
Asked by
Richard
Top achievements
Rank 1
Answers by
Petar
Telerik team
Richard
Top achievements
Rank 1
Share this question
or