0

I have a datatable in Razor Project with rows containing id, name, title, parent, and other fields. I need to bind a JavaScript array dynamically, but I tried this code and got an error. Please help.

It works fine with static data like below, and my organization chart is shown with no error.

var testData = [
    { id: 1, name: "John", title: "CEO", parent: 0 }, 
    { id: 2, name: "Tom", title: "SellManager", parent: 1 }, 
    { id: 3, name: "Jerry", title: "SupportManager", parent: 1 }, 
    { id: 4, name: "Robert", title: "SellExpert", parent: 2 },
];

With the generated data from JSON doesn't work and shows an error:

Uncaught TypeError: Cannot read properties of undefined (reading 'render') jquery-3.7.1.min.js:2 jQuery.Deferred exception: Cannot read properties of undefined (reading 'render') TypeError: Cannot read properties of undefined (reading 'render')

<script type="text/javascript">        
/////////////////////   Chart Script Start   /////////////////////
    var jq = jQuery.noConflict();

    (function (jq) {
        jq.fn.orgChart = function (options) {
            var opts = jq.extend({}, jq.fn.orgChart.defaults, options);
            return new OrgChart(jq(this), opts);
        }

        jq.fn.orgChart.defaults = {
            data: [{ id: 1, name: 'Root', title: 'Amount', parent: 0, hierarchy: 0 }],
            showControls: false,
            allowEdit: false,
            onAddNode: null,
            onDeleteNode: null,
            onClickNode: null,
            newNodeText: 'Add',
            delNodeText: 'del'
        };

        function OrgChart(jqcontainer, opts) {
            var data = opts.data;
            var nodes = {};
            var rootNodes = [];
            this.opts = opts;
            this.jqcontainer = jqcontainer;
            var self = this;

            this.draw = function () {
                jqcontainer.empty().append(rootNodes[0].render(opts));
                //document.getElementsByClassName('org-del-button')[0].style.display = 'none';
                jqcontainer.find('.node').click(function () {
                    if (self.opts.onClickNode !== null) {
                        self.opts.onClickNode(nodes[jq(this).attr('node-id')]);
                    }
                });

                if (opts.allowEdit) {
                    jqcontainer.find('.node h2').click(function (e) {
                        var thisId = jq(this).parent().attr('node-id');
                        self.startEdit(thisId);
                        e.stopPropagation();
                    });
                    jqcontainer.find('.node h3').click(function (e) {
                        var thisId = jq(this).parent().attr('node-id');
                        self.startEdith3(thisId);
                        e.stopPropagation();
                    });
                }

                // add "add button" listener
                jqcontainer.find('.org-add-button').click(function (e) {
                    var thisId = jq(this).parent().attr('node-id');

                    if (self.opts.onAddNode !== null) {
                        self.opts.onAddNode(nodes[thisId]);
                    }
                    else {
                        self.newNode(thisId);
                    }
                    e.stopPropagation();
                });

                jqcontainer.find('.org-del-button').click(function (e) {
                    var thisId = jq(this).parent().attr('node-id');

                    if (self.opts.onDeleteNode !== null) {
                        self.opts.onDeleteNode(nodes[thisId]);
                    }
                    else {
                        self.deleteNode(thisId);
                    }
                    e.stopPropagation();
                });
            }

            this.startEdit = function (id) {
                var inputElement = jq('<input class="org-input" type="text" value="'+nodes[id].data.name+'"/>');
                jqcontainer.find('div[node-id=' + id + '] h2').replaceWith(inputElement);
                var commitChange = function () {
                    var h2Element = jq('<h2>' + nodes[id].data.name + '</h2>');
                    if (opts.allowEdit) {
                        h2Element.click(function () {
                            self.startEdit(id);
                        })
                    }
                    inputElement.replaceWith(h2Element);
                }
                inputElement.focus();
                inputElement.keyup(function (event) {
                    if (event.which == 13) {
                        commitChange();
                    }
                    else {
                        nodes[id].data.name = inputElement.val();
                    }
                });
                inputElement.blur(function (event) {
                    commitChange();
                })
            }

            //////////////////////title field//////////////////////////////
            this.startEdith3 = function (id) {
                var inputElement = jq('<input class="org-input" type="text" value="' + nodes[id].data.title + '"/>');
                jqcontainer.find('div[node-id=' + id + '] h3').replaceWith(inputElement);
                var commitChange = function () {
                    var h3Element = jq('<h3>' + nodes[id].data.title + '</h3>');
                    if (opts.allowEdit) {
                        h3Element.click(function () {
                            self.startEdith3(id);
                        })
                    }
                    inputElement.replaceWith(h3Element);
                }
                inputElement.focus();
                inputElement.keyup(function (event) {
                    if (event.which == 13) {
                        commitChange();
                    }
                    else {
                        nodes[id].data.title = inputElement.val();
                    }
                });
                inputElement.blur(function (event) {
                    commitChange();
                })
            }
            //////////////////////title field ends////////////////////////
            this.newNode = function (parentId) {
                var nextId = Object.keys(nodes).length;
                while (nextId in nodes) {
                    nextId++;
                }

                self.addNode({ id: nextId, name: '(New name)', title: '0.0', hierarchy: parentId, parent: parentId });
            }

            this.addNode = function (data) {
                var newNode = new Node(data);
                nodes[data.id] = newNode;
                nodes[data.parent].addChild(newNode);
                self.draw();
                self.startEdit(data.id);
                self.startEdith3(data.id);
            }

            this.deleteNode = function (id) {
                for (var i = 0; i < nodes[id].children.length; i++) {
                    self.deleteNode(nodes[id].children[i].data.id);
                }
                nodes[nodes[id].data.parent].removeChild(id);
                delete nodes[id];
                self.draw();
            }

            this.getData = function () {
                var outData = [];
                for (var i in nodes) {
                    outData.push(nodes[i].data);
                }
                return outData;
            }

            // constructor
            for (var i in data) {
                var node = new Node(data[i]);
                nodes[data[i].id] = node;
            }

            // generate parent child tree
            for (var i in nodes) {
                if (nodes[i].data.parent == 0) {
                    rootNodes.push(nodes[i]);
                }
                else {
                    nodes[nodes[i].data.parent].addChild(nodes[i]);
                }
            }

            // draw org chart
            jqcontainer.addClass('orgChart');
            self.draw();
        }

        function Node(data) {
            this.data = data;
            this.children = [];
            var self = this;

            this.addChild = function (childNode) {
                this.children.push(childNode);
            }

            this.removeChild = function (id) {
                for (var i = 0; i < self.children.length; i++) {
                    if (self.children[i].data.id == id) {
                        self.children.splice(i, 1);
                        return;
                    }
                }
            }

            this.render = function (opts) {
                var childLength = self.children.length,
                    mainTable;

                mainTable = "<table cellpadding='0' cellspacing='0' border='0'>";
                var nodeColspan = childLength > 0 ? 2 * childLength : 2;
                mainTable += "<tr><td colspan='" + nodeColspan + "'>" + self.formatNode(opts) + "</td></tr>";

                if (childLength > 0) {
                    var downLineTable = "<table cellpadding='0' cellspacing='0' border='0'><tr class='lines x'><td class='line left half'></td><td class='line right half'></td></table>";
                    mainTable += "<tr class='lines'><td colspan='" + childLength * 2 + "'>" + downLineTable + '</td></tr>';

                    var linesCols = '';
                    for (var i = 0; i < childLength; i++) {
                        if (childLength == 1) {
                            linesCols += "<td class='line left half'></td>";    // keep vertical lines aligned if there's only 1 child
                        }
                        else if (i == 0) {
                            linesCols += "<td class='line left'></td>";     // the first cell doesn't have a line in the top
                        }
                        else {
                            linesCols += "<td class='line left top'></td>";
                        }

                        if (childLength == 1) {
                            linesCols += "<td class='line right half'></td>";
                        }
                        else if (i == childLength - 1) {
                            linesCols += "<td class='line right'></td>";
                        }
                        else {
                            linesCols += "<td class='line right top'></td>";
                        }
                    }
                    mainTable += "<tr class='lines v'>" + linesCols + "</tr>";

                    mainTable += "<tr>";
                    for (var i in self.children) {
                        mainTable += "<td colspan='2'>" + self.children[i].render(opts) + "</td>";
                    }
                    mainTable += "</tr>";
                }
                mainTable += '</table>';
                return mainTable;
            }

            this.formatNode = function (opts) {
                var nameString = '',
                    descString = '';
                titleString = '';
                if (typeof data.name !== 'undefined') {
                    nameString = '<h2>' + self.data.name + '</h2>';
                }
                if (typeof data.title !== 'undefined') {
                    titleString = '<h3>' + self.data.title + '</h3>';
                }
                if (typeof data.description !== 'undefined') {
                    descString = '<p>' + self.data.description + '</p>';
                }
                if (opts.showControls) {
                    var buttonsHtml = "<div class='org-add-button'>" + opts.newNodeText + "</div><div class='org-del-button'>" + opts.delNodeText + "</div>";
                }
                else {
                    buttonsHtml = '';
                }
                return "<div class='node' node-id='" + this.data.id + "'>" + nameString + titleString + descString + buttonsHtml + "</div>";
            }
        }

    })(jQuery);
    var data1 = @Html.Raw(JsonConvert.DeserializeObject(JsonConvert.SerializeObject(Model.Table_Public)));
    testData = JSON.stringify(data1);
    alert(testData);

    jq(function () {
        org_chart = jq('#orgChart').orgChart({
            data: testData,
            showControls: false,
            allowEdit: false,
            onAddNode: function (node) {
                log('Created new node on node ' + 
                   node.data.id);
                org_chart.newNode(node.data.id);
            },
            onDeleteNode: function (node) {
                log('Deleted node ' + node.data.id);
                org_chart.deleteNode(node.data.id);
            },
            onClickNode: function (node) {
                log('Clicked node ' + node.data.id);
            }        
        });
    });
</script>

1 Answer 1

0

You can access the Model in JavaScript directly without iterating over each DataRow in the @foreach statement. Then, you deserialise from the DataTable into a list of objects.

Aside, it would be better to return the data as an array instead of a DataTable in the model.

In your model class:

public class GetDataTableInJsModel
{
    public DataTable Table_Public { get; set; }
    // Optional: Define the model class instead of dynamic
    public List<dynamic> Table_Public_List { get; set; }
}

While in your controller action:

var dt = <Your DataTable instance>;

return View(new GetDataTableInJsModel
{
    Table_Public = dt,
    Table_Public_List = JsonConvert.DeserializeObject(
        JsonConvert.SerializeObject(dt)
    )
});

To display the JSON string in the alert dialog, you should serialize the data with JSON.stringfy().

@using Newtonsoft.Json

<script type="text/javascript">
  
    var data = @Html.Raw(
        JsonConvert.DeserializeObject(JsonConvert.SerializeObject(Model.Table_Public))
    );
    // For using Model.Table_Public_List
    // var data = @Html.Raw(Model.Table_Public_List);
    alert(JSON.stringify(data));
</script>
Sign up to request clarification or add additional context in comments.

6 Comments

When i use this static value everything work fine and my Organization Chart is Draw via javascript method: var testData = [ { id: 1, name: "John", title: "CEO", parent: 0 }, { id: 2, name: "Tom", title: "SellManager", parent: 1 }, { id: 3, name: "Jerry", title: "SupportManager", parent: 1 }, { id: 4, name: "Robert", title: "SellExpert", parent: 2 }, ];
But your code generate this(Nothing Work, .. i think because field name is in double quotation too ): [ { "id": 1, "name": "John", "title": "CEO", "parent": "0" }, { "id": 2, "name": "Tom", "title": "SellManager", "parent": "1" }, { "id": 3, "name": "Jerry", "title": "SupportManager", "parent": "1" }, { "id": 4, "name": "Robert", "title": "SellExpert", "parent": "2" }, ];
You need to show your code that generates the organisation chart, so we can know what is wrong. I don't think the field name with double quotation marks is wrong, as it is a valid JSON (serialization/deserialization). When declaring the object/array in JavaScript, you don't need it as they are valid identifiers.
Meanwhile, it seems like something is wrong with deserialization on your side, as I saw that the values for parent and id are treated as strings, instead of what you expect is that both fields are integers. If you define both columns in DataTable are integers/longs, by right it won't deserialize as string. Again we need the minimal, reproducible example.
thanks for replying, i edit my question and add my Javascript code that generate organization chart with Json string.also i add error that i have got... so if you can help me please
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.