Aside from my work project in AngularJS, I've also got a private project in 
Angular, and the stuff I'm doing there is actually a bit more complicated:
I'm working on a game (who isn't?) that has a map showing the current positions 
of a number of (space) fleets and planets. Fleets can have a number of 
waypoints (places they move to over the next couple of turns). All of this is 
drawn in SVG.

I'm currently using Raphael as my SVG library because it's popular and probably 
good. Handling interaction between Angular and Raphael was originally quite 
easy to do with the use of $watch and $apply. For example, you could select a 
fleet or a planet either on the (SVG) map or in a list outside it, and the data 
for it would be shown, and it would be highlighted on the map. This worked well.

The latest feature I added turned it into quite a mess, though. I wanted the 
ability to add new waypoints to a fleet. First press a button outside the map 
area to switch to the "add waypoint" mode. Then click on the map where you want 
the new waypoint. The way point gets added to the data, and is immediately 
shown in the fleet data outside the map, as well as detected by a $watch and 
drawn on the map.

This has led to the following hideous piece of code:

      $scope.$watch('selected', function(selected, oldselected) {
        console.log("selected changed");
        if (selected !== oldselected && selected !== null) {
          if (selected.fleet) {
            $scope.$watchCollection('selected.waypoints', function() {
              $scope.selected.shape.remove();
              $scope.drawFleet(selected);
            });
            selected.shape.attr({"fill": $scope.mapconfig.selectedfleetcolour, 
"stroke": $scope.mapconfig.selectedfleetcolour});
          }
          else if (selected.planet) {
            selected.shape.attr({"fill": $scope.mapconfig.selectedplanetcolour, 
"stroke": $scope.mapconfig.selectedplanetcolour});
          }
          if (oldselected !== null) {
            if (oldselected.fleet) {
              oldselected.shape.attr({"fill": $scope.mapconfig.fleetcolour, 
"stroke": $scope.mapconfig.fleetcolour});
            }
            else if (oldselected.planet) {
              oldselected.shape.attr({"fill": $scope.mapconfig.planetcolour, 
"stroke": $scope.mapconfig.planetcolour});
            }
          }
        }
      })

      $scope.$watch('mode', function(mode, oldmode) {
        console.log("mode changed");
        if (mode == 'addWaypoint') {
          console.log("addWaypoint");
          iElement.on('mousemove', function($event) {
            $scope.mousecoords.x = $event.offsetX;
            $scope.mousecoords.y = $event.offsetY;
            $scope.$apply();
          })
          iElement.on("click", function(event) {
            $scope.addWaypoint({x: event.offsetX, y: event.offsetY});
            $scope.setMode(oldmode);
            $scope.$apply();
          });
        }
        else if (oldmode == 'addWaypoint') {
          iElement.off("mousemove click");
        }
      })

I'm setting watchers and even handlers inside watchers. I'm pretty sure that's 
needlessly complicated. I don't doubt that there's already a dozen bugs in 
this. But most of all, by doing it this way, I'm working in a very non-Angular 
way. To combine Angular with this library (and I suspect the same would be true 
for any non-Angular library), I need to switch from the declarative Angular way 
to the more procedural traditional javascript way. It almost feels like I'm 
using $watch to do my DOM manipulation explicitly in JQuery.

Fortunately, the cool thing about SVG is that, unlike Canvas, it's XML, and I 
think it's part of the DOM of the page. I suspect I could manipulate that DOM 
through Angular directly, instead of through some library. Originally I chose 
the library because I'm totally new to SVG, and it feels nice to have a nice 
library to do complex things for me, but now I think it's getting in my way. Or 
in Angular's way. Maybe I should drop it and manipulate the SVG directly with 
ngRepeats, Angular's data bindings and event handlers.

Another option would be to use an SVG library designed to work with Angular, if 
one exists. Does one exist? Would that be better than the direct approach?

So what does everyone here think the direct approach? Would it work? One thing 
I fear is that it would create a lot of data binding that would all be checked 
on every $digest, whereas I know that only the currently selected fleet can 
actually have waypoints added. So watching them all seems like it might grow 
into a serious performance overhead. Particarly if this game might eventually 
end up with hundreds of fleets and other objects. Or more. Who knows?

So I've got a ton of data, but most of the time, most of it is pretty static. 
Is dynamically adding and removing $watches still a better way to go, despite 
the uglier code?


(I'm also thinking this might be an interesting topic for a blog post. I've 
been thinking of starting a blog, and this might be a good topic to start with.)


mcv.

-- 
You received this message because you are subscribed to the Google Groups 
"AngularJS" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to angular+unsubscr...@googlegroups.com.
To post to this group, send email to angular@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to