How to check duplicate value in recursive array structure during text field blur event?

I am trying to have a unique header for my node in a recursive tree.

Therefore, when I give a header to my nodes, he must check that this header is already occupied by some other nodes or not. If it is accepted, it must warn the user, and he must reset that node value to the previous value.

Two nodes should not have the same name.

But here, since the structure is recursive, so I donโ€™t understand how to do it.

Note. I want to do this as soon as the text field loses 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> 
+7
javascript angularjs
source share
2 answers

my decision

  • store a confirmed copy of the name when blurring (that is, there are 3 names, but I do not know the point myObj.name, so I left it as is, and you can clear it)
  • find duplicates recursively, stop at first true . if there is an error, use the last valid name, otherwise update the last valid name.

Whys

  • you want to check the name only when blurring. angular provides ways to test ng-model every time it is changed using parsers and formatters, which completely restrict the user from repeating the name of the hype. There are many ways to solve this problem (validators, editing without saving), and all of them require different solutions. Explore and go with what suits you best.
  • Using hashMap (my first thought) will require cleanup logic when renaming and deleting, which would ultimately complicate the code

If you want to search for duplicates only in the node tree, you need to save the link to the parent node and use the getTree method to determine the root node to search for.

 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> 
+3
source share

You can 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 the cost of memory), you can save a list of names:

 $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 what you mean by

should reset that node value to the previous value.

if you add new objects, you will not have the โ€œprevious valueโ€, but I will leave this bit to you. Anyway, this should get you started.

+3
source share

All Articles