0

I'm an experienced C#/ASP.NET developer and my company is pushing toward AngularJS for all future web front-end development.

I have a specific situation I can't quite figure out. I have a SELECT bound to an array of "email" objects (with properties "id" and "name") and I am using ng-options instead of ng-repeat to do the data binding. This populates the SELECT for me so what I want to do is to select an item from the list, click a button and then remove the item from the list and add it to a secondary list. I was originally going to use checkboxes but the array has a large number of items in it and my project manager didn't like that.

I'm using this as a reference to all the available array functions: http://www.w3schools.com/jsref/jsref_obj_array.asp

Since I'm so used to C#, I don't have the same methods available in JavaScript and I'm having difficulty trying to find the right technique to do this. What's the best way to identify the item in the source array, remove it, and insert it into the target array while keeping it sorted alphabetically by "name"?

This begs the second question. I don't know if I'm binding my SELECT efficiently because when I select an item and click the button, the ng-model attribute is associated with the "id" of the item which is then used to scan the array the SELECT is bound to, to get the item. Since I'm fairly new to AngularJS, and their documentation isn't quite ideal, I was wondering if it is possible to bind to the object instead of it's "id" then access the selected item directly without having to scan the array. Here's my current code which binds to the "id":

JS:

$scope.addEmailToGroup = function(emailID) {
  var email = getEmailByID($scope.emails, emailID);

  if (email !== null) {
    // TODO: Move item from one array to the other
  }
};

function getEmailByID(arr, id) {
  var email = null;
  var length = arr.length;

  for (var i = 0; i < length; i++) {
    if (arr[i].id === id) {
      email = angular.copy(arr[i]);
      break;
    }
  }

  return email;
}

HTML:

<select id="emails" class="form-control" data-ng-model="selectedEmailID" name="emails" data-ng-options="email.id as email.name  for email in emails"></select>
<button class="btn btn-default" data-ng-click="addEmailToGroup(selectedEmailID)" type="button">
  <span class="glyphicon glyphicon-plus"></span>
</button>
developer033
  • 24,267
  • 8
  • 82
  • 108
DesertFoxAZ
  • 439
  • 1
  • 4
  • 14
  • Your snippet isn't working. – developer033 Jul 05 '16 at 21:14
  • @developer033 it's clearly just a part of the app, but yeah, it probably shouldn't be written as a snippet. – Shomz Jul 05 '16 at 21:15
  • Simpler to bind whole object in ng-model. Then no need to look for the object...you already have it and can use indexOf() and splice() to remove from current array and push it to other array – charlietfl Jul 05 '16 at 21:55
  • @charlietfl, can you provide a sample on how to do set up my SELECT element? The documentation for JavaScript arrays shows examples where the arrays contain simple types (strings or integers). How do they work with objects where you need to inspect property values? – DesertFoxAZ Jul 06 '16 at 15:41
  • set `select` as the object see http://plnkr.co/edit/SxIvt4KThWLtWvh3PnOh?p=preview – charlietfl Jul 06 '16 at 15:48
  • More details. http://stackoverflow.com/a/13049740/1175966 There are lots of posts here about `ng-options` – charlietfl Jul 06 '16 at 15:49
  • I've changed the SELECT to be this: `code``code` This gives me an object now instead of an integer when I use $scope.selectedEmail but I can't figure out how to remove that object from the list. – DesertFoxAZ Jul 06 '16 at 16:45
  • Sorry, ignore this part: ' (' + getEmailTypeNameByID(email.emailTypeID) + ')' – DesertFoxAZ Jul 06 '16 at 17:08
  • to remove from array see http://stackoverflow.com/questions/15453979/how-do-i-delete-an-item-or-object-from-an-array-using-ng-click/15454424#15454424 – charlietfl Jul 06 '16 at 17:41

2 Answers2

0

Assuming that your email IDs are numeric, you can use the IDs as array indexes, and the simply access them like:

$scope.emails[emailID];

If that's too much overhead, you can use a dictionary array to map $scope.emails IDs to their array keys.


Here's what I meant by using dictionary. Note that now you don't have to loop all the objects in the emails array, but you can simply use indexOf on the dictionary variable to fetch the full email object. In your app, you loop over the emails only once in order to build the dictionary.

var emails = [ // dummy emails object
  {id: 3,  email: 'shomz@example.com'},
  {id: 5,  email: 'DesertFoxAZ@example.com'},
  {id: 12, email: 'SomeoneElse@example.com'}
];

var dict = []; // init the dictionary here

for(var i = 0; i < emails.length; i++){ // build the dictionary
  dict.push(emails[i].id);
}

function getEmailById(id){
  return emails[dict.indexOf(parseInt(id, 10))];
}


document.getElementById('b').addEventListener('click', function(){
  var id = document.getElementById('n').value;
  document.getElementById('res').textContent = 
    JSON.stringify(getEmailById(id)) || "Not found";
});
<input type="number" id="n" placeholder="Enter email ID">
<button id="b">Get Email</button>
<p id="res">Enter email ID in the box above, then click the button</p>
Shomz
  • 37,421
  • 4
  • 57
  • 85
  • The ID's are numeric but not necessarily sequential (meaning they don't start at zero and there may be gaps in the sequence). Would that still work? What type of dictionary is available in JavaScript because that would definitely be handy. – DesertFoxAZ Jul 06 '16 at 15:38
  • It will work either way... By dictionary I meant a simple array to translate IDs to keys. I'll update my answer to show you how. – Shomz Jul 06 '16 at 17:31
0

I've come up with a hybrid solution. First, I was able to bind my SELECT to the object itself and that removed need to search the array.

<select id="emails" class="form-control" data-ng-model="selectedEmail" name="emails" data-ng-options="email.name for email in emails track by email.id">
</select>
<button class="btn btn-default" data-ng-click="addEmailToGroup()" type="button">
    <span class="glyphicon glyphicon-plus"></span>
</button>

As for the moving of the item from one array to the other, I used this code:

$scope.addEmailToGroup = function () {
    if ($scope.selectedEmail) {
        var pos = -1;
        var length = $scope.emails.length;

        for (var i = 0; i < length; i++) {
            if ($scope.emails[i].id === $scope.selectedEmail.id) {
                pos = i;
                break;
            }
        }

        $scope.emails.splice(pos, 1);
        $scope.selectedEmails.push($scope.selectedEmail);
    }
};

While I certainly could have used the solution @Shomz proposed as well as what @charlietfl suggested, I think this works fine for my needs.

Thanks for the feedback.

Oh, and sorry for the snipped above not working. That was my first post on StackOverflow...

DesertFoxAZ
  • 439
  • 1
  • 4
  • 14