1

I've got a two-part form, where one page lets a user select via checkbox from several lists. Having made those selections, the user can see a summary of selections, on another page. The user can then return to the checkbox list and check/uncheck as needed.

I can get it to work so the user's selections persist, but then I can't get the output to show up properly on the main page (the summary list). Or, if I get the output to show up nicely with item.name, then I can't get the selections to persist when the user returns to the secondary page's list of checkboxes. Sharing across controllers is NOT the problem, really. The problem here is getting the checkboxes to retain their checked-state when the user returns to the checkbox list. When I output {{checkedCity}}, for instance, I can see everything's in there. I just can't get the checkboxes to reflect this.

This seems to revolve around how the data is entered in the factory: if I use value="item.name" in the checkbox input, then the name string is all that's added to the factory. But I need the id, as well, since that's what I'll be actually sending the api. So if I change to value="item", then I can get the output to look nice, and I get the id -- but then I don't get the persistence if the user revisits the page.

My instinct (which has gotten me nowhere, sadly) is the factory can't see that its contained objects are the same as what's being spit out in the checkbox ng-repeat. I've tried ng-click, ng-checked, and now I'm trying using the webStorage plugin, instead. I've also attempted to cycle through the factory and compare it to the checkbox list.

Here the plunk: http://plnkr.co/edit/akvFJrPP7YtOoNBDS0Hx?p=preview

What's the best way to tackle this? Regardless of how I store the info, the underlying issue remains that I can't seem to get the checkbox list to reconcile with what's being stored, to know that something is checked.

Many many thanks in advance for any help!

kl02
  • 580
  • 6
  • 24
  • I might be totally missing something, but it looks like you're doing a ton of work that angular could take care of for you.. I'll try to recreate the whole thing in a simpler fashion. – m59 Jan 02 '14 at 18:23
  • I had a feeling that was so! Everything else I've done in angular so far has been elegant and sleek. This has seemed so much more complicated, but I couldn't tell if I was coding against angular's grain in some way, or it was just my inexperience. – kl02 Jan 02 '14 at 18:27
  • possible duplicate of [Sharing data between AngularJS controllers?](http://stackoverflow.com/questions/16880340/sharing-data-between-angularjs-controllers) – Ben Lesh Jan 02 '14 at 21:09
  • [This question has been asked to death](https://www.google.com/search?q=site:stackoverflow.com+share+data+across+controllers+angularjs) – Ben Lesh Jan 02 '14 at 21:11
  • @blesh That isn't the problem. The issue is with checkboxes using ng-model. – m59 Jan 02 '14 at 22:08
  • I'd looked at those other questions, but they didn't really answer what was going on. Sharing between controllers was the easy part. Where I was stumped was getting checkboxes to reflect the values in that shared scope. Those other questions didn't answer, so I asked a new one. Sorry if it seems so similar as to overlap. – kl02 Jan 02 '14 at 22:10

1 Answers1

3

The real solution is to make a directive like ngTrueValue that supports expressions so that ngModel can register with an object or array when the checkbox is checked. Unfortunately, it currently only supports strings. It is very likely that ngTrueValue will be updated in the near future, so using ngTrueValue will be the correct approach soon.

Currently, you can make an additional object/array to act as the model for the checkboxes and use that set of data to determine what you want to display.

Basic and Generic Example:

Live Demo (click).

Markup:

<div ng-controller="myCtrl1">
  <div ng-repeat="item in shared.data track by $index">
    <input type="checkbox" ng-model="shared.checks[$index]"/>
    <span>{{item.name}}</span>
  </div>
</div>
<div ng-controller="myCtrl2">
  <h2>Checked:</h2>
  <div ng-repeat="item in shared.checks track by $index" ng-show="item===true">{{shared.data[$index].name}}</div>
</div>

JavaScript:

app.controller('myCtrl1', function($scope, myService) {
  $scope.shared = myService;
});

app.controller('myCtrl2', function($scope, myService) {
  $scope.shared = myService;
});

app.factory('myService', function() {
  var myService = {
    data: [
      {name:'Foo'},
      {name:'Bar'},
      {name:'Baz'},
      {name:'Qux'}
    ],
    checks: []
  };
  return myService;
});

Your code adapted:

Live demo (click).

Note that I have changed checkboxFactory to checkboxService. Factories return (create) services, so calling the service itself a factory is odd. Also, I got rid of a lot of stuff that either isn't needed or doesn't seem relevant.

JavaScript:

app.controller('Ctrl', function($scope, checkboxService) {
  $scope.items = ['city', 'state'];
  $scope.selection = $scope.items[0];
  $scope.shared = checkboxService;
});

app.controller('AllCtrl', function($scope, checkboxService) {
    $scope.shared = checkboxService;
});

app.factory('checkboxService', function() {
  var checkboxService = {
    city: [
      {"id" : 1, "name" : "Firstplace"},
      {"id" : 2, "name" : "Second place"},
      {"id" : 3, "name" : "Thirdplace"},
      {"id" : 4, "name" : "Fourthplace"},
      {"id" : 5, "name" : "Fifth place"}
    ],
    state: [
      {"id" : 6, "name" : "yellow dog"},
      {"id" : 7, "name" : "bluedog"},
      {"id" : 8, "name" : "cobalt dog"},
      {"id" : 9, "name" : "purple dog"},
      {"id" : 10, "name" : "greendog"}  
    ],
    checks: {
      city: [],
      state: []
    }
  };
  return checkboxService;
});

Markup:

  <div ng-controller="Ctrl">
FIRST PAGE: make choices<br/>
  <select ng-model="selection" ng-options="item for item in items">
  </select>
  <hr/>
  <div ng-switch="" on="selection">
      <div ng-switch-when="city">
        <input ng-model="searchText">
        <div ng-repeat="item in shared.city | filter:searchText track by $index">
          <div class="checkbox-box">
            <label>
              <input type="checkbox" ng-model="shared.checks.city[$index]"/>
              <span class="labelText">{{item.name}} {{item.id}}</span>
            </label>
          </div>
        </div>
      </div>
      <div ng-switch-when="state">
        <input ng-model="searchText">
        <div ng-repeat="item in shared.state | filter:searchText track by $index">
          <div class="checkbox-box">
            <label>
              <input type="checkbox" ng-model="shared.checks.state[$index]"/>
              <span class="labelText">{{item.name}}</span>
            </label>
          </div>
        </div>
      </div>
      <div ng-switch-default></div>
  </div>
</div>

<div ng-controller="AllCtrl">
   <hr/>  
   SECOND PAGE: show choices made<br/>
        <div ng-repeat="item in shared.checks.city track by $index" ng-show="item===true">
          <div class="selected-box1">{{shared.city[$index].name}} {{item.id}}<i class="fa fa-times-circle fa-lg pull-right"></i></div>
        </div>  
        <div ng-repeat="item in shared.checks.state track by $index" ng-show="item===true">
          <div class="selected-box2">{{shared.state[$index].name}}<i class="fa fa-times-circle fa-lg pull-right"></i></div>
        </div>
</div>
m59
  • 43,214
  • 14
  • 119
  • 136
  • omg it WORKS and it's so elegant. I need a way to do more than one upvote. One is not enough! Thank you! – kl02 Jan 02 '14 at 22:06