6

I am trying have unique title to my node in recursive tree.

So when I give title to my nodes it should check that this title is already taken by some other nodes or not. If taken then it should alert user and it should reset that node value to previous value.

No two nodes should have same title.

But here as structure is recursive so I am not getting how do to do this.

Note : I want to do this as soon as textbox loses its focus.

var app = angular.module("myApp", []);
        app.controller("TreeController", function ($scope) {
            $scope.delete = function (data) {
                data.nodes = [];
            };
            $scope.add = function (data) {
                var post = data.nodes.length + 1;
                var newName = data.name + '-' + post;
                data.nodes.push({ name: newName, nodes: [],selected : false, myObj: { name: newName} });
            };
            $scope.tree = [{ name: "Node", nodes: [], selected: false }];

            $scope.setActive = function ($event, data) {
            	$event.stopPropagation();
                $scope.selectedData = data;
                clearDivSelection($scope.tree);
                data.selected = true;
            };

            function clearDivSelection(items) {
                items.forEach(function (item) {
                    item.selected = false;
                    if (item.nodes) {
                        clearDivSelection(item.nodes);
                    }
                });
            }
            
            $scope.checkDuplicateNodeName = function () {
             alert()
            }
        });
ul {
    list-style: circle;
}
li {
    margin-left: 20px;
}
 .active { background-color: #ccffcc;}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>


<ul ng-app="myApp" ng-controller="TreeController">
        <li ng-repeat="data in tree" ng-include="'tree_item_renderer.html'"></li>
        <script type="text/ng-template" id="tree_item_renderer.html">
            <div ng-class="{'active': data.selected}" > {{data.myObj.name}}</div>
            <button ng-click="add(data)">Add node</button>
            <button ng-click="delete(data)" ng-show="data.nodes.length > 0">Delete nodes</button>
            <ul>
                <li ng-repeat="data in data.nodes" ng-include="'tree_item_renderer.html'" ng-click="setActive($event, data)"></li>
            </ul>
        </script>
        <div style="margin-left:100px;">
           Title :  <input type="text" ng-model="selectedData.myObj.name" ng-blur="checkDuplicateNodeName()" />
           Location :  <input type="text" ng-model="selectedData.myObj.location" />

        </div>
    </ul>

2 Answers 2

3
+50

my solution

  1. store validated copy of name on blur (meaning there are 3 names but I dont know the point of myObj.name so I left it as is, and you can clean up)
  2. find dupes recursively, stop on first true. if there's a dupe, use last valid name, else update last valid name.

whys

  1. you want to validate the name only on blur. angular provides ways to validate ng-model on every change via, parsers and formatters which would completely restrict the user from persisting a dupe name. there are a ton of ways to handle this problem (validators, editing without persisting) and all require different kinds of solutions. explore and go with what works best for you
  2. using a hashMap (my first thought) would require cleanup logic on renames and deletes that would've ended up complicated the code further

If you wish to search for dupes only in the node's tree, you need to store a reference to the parent node, and use the getTree method to determine the root node to search from.

var app = angular.module("myApp", []);
app.controller("TreeController", function($scope) {
  $scope.delete = deleteNodes;
  $scope.add = add;
  $scope.setActive = setActive;
  $scope.checkDuplicateNodeName = checkDuplicateNodeName;
  $scope.trees = [{
    name: "Node",
    nodes: [],
    selected: false
  },{
    name: "Node2",
    nodes: [],
    selected: false
  }];

  function deleteNodes(data) {
    data.nodes = [];
  }

  function add(data) {
    var post = data.nodes.length + 1;
    var newName = data.name + '-' + post;
    
    data.nodes.push({
      name: newName,
      nodes: [],
      selected: false,
      validatedName: newName
    });
  }

  function setActive($event, data) {
    $event.stopPropagation();
    if($scope.selectedData) {
      $scope.selectedData.selected = false;
    }
    $scope.selectedData = data;
    
    data.selected = true;
  }

  function checkDuplicateNodeName() {
    if(!$scope.selectedData)
      return;
  
    var dupe = false;
    
    for(var idx = 0; idx < $scope.trees.length; idx++) {
      if(isDuplicateName($scope.trees[idx], $scope.selectedData)) {
        dupe = true;
        break;
      }
    }
    
    if(dupe){
      alert('The name "' + $scope.selectedData.name + '" already exists');
      $scope.selectedData.name = $scope.selectedData.validatedName;
    } else {
      $scope.selectedData.validatedName = $scope.selectedData.name;
    }

  }
  
  function getTree(node){
    while(node.parent) {
      node = node.parent;
    }
    
    return node;
  }
  
  function isDuplicateName(node, nodeToCheck) {
    var dupeName = node != nodeToCheck && node.name && nodeToCheck.name && node.name == nodeToCheck.name;
    if(dupeName) return true;
    
    if(node.nodes){
      for(var idx=0; idx< node.nodes.length; idx++) {
        if(isDuplicateName(node.nodes[idx], nodeToCheck)){
          return true;
        }
      }
    }
    return false;
  }
});
ul {
  list-style: circle;
}

li {
  margin-left: 20px;
}

.active {
  background-color: #ccffcc;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>


<ul ng-app="myApp" ng-controller="TreeController">
  <li ng-repeat="data in trees" ng-include="'tree_item_renderer.html'"></li>
  <script type="text/ng-template" id="tree_item_renderer.html">
    <div ng-class="{'active': data.selected}"> {{data.name}}</div>
    <button ng-click="add(data)">Add node</button>
    <button ng-click="delete(data)" ng-show="data.nodes.length > 0">Delete nodes</button>
    <ul>
      <li ng-repeat="data in data.nodes" ng-include="'tree_item_renderer.html'" ng-click="setActive($event, data)"></li>
    </ul>
  </script>
  <div style="margin-left:100px;">
    Title : <input type="text" ng-model="selectedData.name" ng-blur="checkDuplicateNodeName()" /> Location : <input type="text" ng-model="selectedData.myObj.location" />

  </div>
</ul>

Sign up to request clarification or add additional context in comments.

10 Comments

Upvoted for your kind efforts towards helping me but in your code i am not getting whats the purpose of getTree function?
@Learning cause according to you, you only want to make sure there isn't a duplicate per tree. getTree figures out the root node of a node by walking up the parent property
@Learning you make it sound like it doesn't solve your problem
I didnt understand your this point :there can be multiple trees ?? dupe should only matter in the tree node is under. if this isnt the case you can easily modify checkDuplicateNodeName to use the root node.Can you please clarify this?
Sorry this is what i exactly wanted but before i include in my project i need to understand something because i am not that good with client side
|
3

You could use a recursive approach similar to your clearDivSelection method:

function isDuplicated (node, title) {
  var result = false;

  if (node.nodes && node.nodes.length > 0) result = node.nodes.reduce(function (result, node) {
    return isDuplicated(node, title);
  }, false);

  return result && node.name === title;
}

Or (at a cost of memory), you could maintain a list of titles:

$scope.titles = {};

$scope.add = function (data) {
  var post = data.nodes.length + 1;
  var newName = data.name + '-' + post;

  if ($scope.titles[newName]) return; // refuse to add
  $scope.titles[newName] = true;

  data.nodes.push({
    name: newName,
    nodes: [],
    selected : false,
    myObj: {
      name: newName
    }
  });
};

I'm not sure exactly what you mean by

should reset that node value to previous value.

if you are "add"ing new objects, you won't have a "previous value" - but I'll leave that bit up to you. This should get you started, anyway.

2 Comments

Ok upvoted for your kind efforts towards helping me.Let say you have generated 2 nodes with title : Node-1 and Node-1-1.Now if you are trying to give Node-1 title to Node-1-1 which is already there so Node-1-1 title should reset to its original value i.e Node-1-1
canny use of reduce but you dont have to necessarily go through every child node to know if there's a dupe.

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.