Reputation: 14161
I'm trying to implement functionality for checking multiple items in a list of checkboxes. I am using a Map to store the data and its selected/unselected state.
List<String> fruits = [
{'fruit': 'apple', 'selected': true},
{'fruit': 'banana', 'selected': true},
{'fruit': 'kiwi', 'selected': false}
];
The checkboxes display initially with the checked/unchecked status correctly rendered, but when I toggle a checkbox's checked status, the fruits
map does not update. How can I correctly create a two-way binding here?
Here is my .dart file:
import 'package:angular/angular.dart';
@NgDirective(
selector: '[my-controller]',
publishAs: 'ctrl'
)
class MyController {
List<String> fruits = [
{'fruit': 'apple', 'selected': true},
{'fruit': 'banana', 'selected': true},
{'fruit': 'kiwi', 'selected': false}
];
}
main() {
ngBootstrap(module: new Module()..type(MyController));
}
And here is the view markup:
<!DOCTYPE html>
<html ng-app>
<body>
<div my-controller>
<ul>
<li ng-repeat="item in ctrl.fruits">
{{item['fruit']}}
<input type="checkbox" ng-model="item['selected']" />
</li>
</ul>
</div>
<!-- THIS DOES NOT UPDATE WHEN THE CHECKBOXES ARE TOGGLED -->
{{ctrl.fruits}}
<script type="application/dart" src="main.dart"></script>
<script type="text/javascript" src="packages/browser/dart.js"></script>
</body>
</html>
Upvotes: 3
Views: 2901
Reputation: 47946
The reason why it does not update is because ctrl.fruits
is being watch on reference. Since the fruits
list reference never changes the binding never changes either.
Here is a simple fix:
{{ctrl.fruits | json}}
By running it through a filter it forces the object to be JSONified in each digest.
Unfortunately there is no easy way to deep watch an object, since the computational complexity would be potentially unbounded.
Upvotes: 4
Reputation: 657048
Edit 2 I found 2 IMHO usable solutions
1
{{ctrl.fruits.toString()}}
2
<div ng-repeat="item in ctrl.fruits">{{item['fruit']}} - {{item['selected']}}
Edit
small improvement, but still an ugly workaround
void update() {
var tmp = fruits;
fruits = null;
Timer.run(() => fruits = tmp);
}
<input type="checkbox" ng-model="item['selected']" ng-click="ctrl.update()"/>
First I added a timer that periodically calls
new Timer.periodic(new Duration(milliseconds: 2000), (t) => print(fruits = new List.from(fruits)));
with this the output gets updated.
I have not yet figured out how to do without the timer.
I guess only changes of the fruits member are recognized but not changes of an element inside the list.
May be related to How do I watch elements of an AngularDart collection?
Upvotes: 2
Reputation: 1561
My guess is to try a $watch
... I'm not familiar with dart, but your problem sounds exactly like your object is not being deep watched. Without a deep watch, angular sees fruits
as an array with 3 objects, and won't look any further than that. With a deep watch, it will see an array with 3 objects, and ALSO the properties of those objects, and it should update when they change.
EDIT
Here's a JSFiddle written in raw JS... it works like you would expect, with this JS
function TodoCtrl($scope) {
$scope.fruits = [
{'fruit': 'apple', 'selected': true},
{'fruit': 'banana', 'selected': true},
{'fruit': 'kiwi', 'selected': false}
];
$scope.printFruits = function(){return JSON.stringify($scope.fruits);}
}
and some real dumb HTML
<div ng-app>
<div ng-controller="TodoCtrl">
<ul>
<li ng-repeat="item in fruits">
{{item['fruit']}}
<input type="checkbox" ng-model="item['selected']" />
</li>
</ul>
<span>{{printFruits()}}</span> <!-- Works correctly as items are checked -->
</div>
</div>
You might try moving your access to ctrl.fruits
into the same div
that's running as the controller; you might not be getting the fre $watch
because your access isn't in the same scope as the controller that runs the checkboxes.
Upvotes: 1