I know for a fact there is something pretty obvious here that I am completely missing, so your help is greatly appreciated.
I have a feature that provides two dropdowns. They contain the same data (the feature allows a trade between two people, the people is the data), but I want them each to get their own copy of said data.
Another part of this feature is that by picking Person A in the first dropdown, I want to disable Person A in the second dropdown, and vice versa, so I have the ng-options
tag paying attention to a disabled
property on the object.
The issue I have is that even with using a method such as Lodash's clone
to properly create a "new" array upon first time assignment, every time I access Person A in ONE array (and specifically do NOT access the other array) invariably I am seeing that when I touch Person A, that object is updated in BOTH arrays, which has me flustered.
This feels like a down-to-the-metal, barebones Javascript issue (standard PEBCAK, I feel like I'm clearly misunderstanding or straight up missing something fundamental), maybe with a bit of AngularJS rendering-related fun-ness involved, but... What gives?
angular.module('myApp', [])
.controller('weirdDataController', function($scope) {
$scope.$watch('manager1_id', () => {
if (angular.isDefined($scope.manager1_id) && parseInt($scope.manager1_id, 10) > 0) {
$scope._disableManagerInOtherDropdown(false, $scope.manager1_id);
}
});
$scope.$watch('manager2_id', () => {
if (angular.isDefined($scope.manager2_id) && parseInt($scope.manager2_id, 10) > 0) {
$scope._disableManagerInOtherDropdown(true, $scope.manager2_id);
}
});
$scope._gimmeFakeData = () => {
return [{
manager_id: 1,
manager_name: 'Bill',
disabled: false
},
{
manager_id: 2,
manager_name: 'Bob',
disabled: false
},
{
manager_id: 3,
manager_name: 'Beano',
disabled: false
},
{
manager_id: 4,
manager_name: 'Barf',
disabled: false
},
{
manager_id: 5,
manager_name: 'Biff',
disabled: false
},
];
};
const data = $scope._gimmeFakeData();
$scope.firstManagers = _.clone(data);
$scope.secondManagers = _.clone(data);
$scope._disableManagerInOtherDropdown = (otherIsFirstArray, managerId) => {
const disableManagers = manager => {
manager.disabled = manager.manager_id === managerId;
};
if (otherIsFirstArray) {
$scope.firstManagers.forEach(disableManagers);
} else {
$scope.secondManagers.forEach(disableManagers);
}
console.log('Is the first item the same??', $scope.firstManagers[0].disabled === $scope.secondManagers[0].disabled);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="myApp" ng-controller="weirdDataController">
<div class="col-xs-12 col-sm-6">
<select class="form-control" ng-model="manager1_id" ng-options="manager.manager_id as manager.manager_name disable when manager.disabled for manager in firstManagers track by manager.manager_id">
<option value="" disabled="disabled">Choose one manager</option>
</select>
<select class="form-control" ng-model="manager2_id" ng-options="manager.manager_id as manager.manager_name disable when manager.disabled for manager in secondManagers track by manager.manager_id">
<option value="" disabled="disabled">Choose another manager</option>
</select>
</div>
</div>
<br /><br />
I threw everything relevant on $scope
just for the sake of getting it working and illustrating the issue, but here's how it goes:
disabled
value of true
, and the opposite object to have false
. What I see: they both have true
(assuming you select the first "real" item in the dropdown)What's the deal? How big of an idiot am I being?
The answer to my question was: clone()
does not perform a "deep" clone by default, therefore I was dealing with the same array despite making the flawed attempt that I was not. Using Lodash's cloneDeep()
method solved my issue, but as Patrick suggested I reevaluated how I wrote the method in question and refactored it, which I removed the need to use any cloning at all.