What is the best way to create a Snap.svg map using AngularJS?

I am in the process of creating a web interface for an online board game. My goal is to load a Snap.svg map using Snap.load asynchronously.

Once the map is loaded, I intend to attach a watch to a scope property and apply colors to the map based on that property (which is an object). Here's the code snippet:

.controller('GameCtrl', [
    '$scope'
    '$routeParams'
    'GameService'
    (
      $scope
      $routeParams
      GameService
    ) ->
      $scope.game = GameService.get($routeParams.gameId)

      $scope.map = Snap("#map")
      Snap.load("img/classical.svg", (data) ->
        console.log "Loaded map!"
        data.select("#provinces").attr
          style: ""
        provinces = data.selectAll("#provinces path")
        for province in provinces
          province.attr
            style: ""
            fill: "#FFFFFF"
            "fill-opacity": "0"
        $scope.map.append(data)

        deregisterWatch = $scope.$watch('game', ->
          console.debug "Game loaded!", $scope.game.data.Id

          for provinceName,unit of $scope.game.data.Phase.Units
            provinceName = provinceName.replace '/', '-'

            province = $scope.map.select("##{provinceName}")

            province.attr
              style: ""
              fill: powers[unit.Nation].colour
              "fill-opacity": "0.8"

            deregisterWatch()
        )
      )

  ])
  

My current challenge is how to separate the map into its own "class" or file while still allowing it to access the $scope for setting the watch after loading.

My initial thought is to extract the map and pass the scope as a parameter:

define([
    'snap'
  ], (
    Snap
  ) ->
    'use strict'

    Map = ($scope, selector, svgPath) ->

      that = {}
      that.provinces = {}

      ...

      that.snap = Snap(selector)
      Snap.load(svgPath, (data) ->
        console.log "Loaded map in Map!"

        data.select("#provinces").attr
          style: ""
        provinces = data.selectAll("#provinces path")
        for province in provinces

          that.provinces[province.attr("id")] = province

          province.attr
            style: ""
            fill: "#FFFFFF"
            "fill-opacity": "0"
        that.snap.append(data)

        deregisterWatch = $scope.$watch('game', ->
          console.debug "Game loaded!", $scope.game.data.Id

          for provinceName,unit of $scope.game.data.Phase.Units
            provinceName = provinceName.replace '/', '-'

            that.colourProvince(provinceName, that.powerColors[unit.Nation].colour)

            deregisterWatch()
        )
      )

      that.colourProvince = (abbr, colour) ->
        ...

      return that

    return Map
  )
  

I believe there may be a better way to handle this within AngularJS. Should I consider using a directive, or do you have any other suggestions?

Answer №1

It would be beneficial for you to create a directive. Directives are designed to encapsulate reusable components, particularly graphic elements.

define [
  'app'  # or utilize any alternative method to access your Angular module.
  'snap'
], (app, Snap) ->

  app.directive 'awesomeMap', ->
    # directive template: will be injected into controller ($element variable)
    template: "<svg></svg>"
    # will replace hosting element
    replace: true
    # can be used as an element and attribute
    restrict: 'EA'
    # defining the "parameters" of your directive: can be parsed from HTML, or bound to parent scope (as shown in this example).
    scope:
      svgPath: '=?path'
    # controller
    controller: AwesomeMap

  class AwesomeMap

    # Controller dependencies: scope and HTML element
    @$inject: ['$scope', '$element']

    # Controller constructor: bind methods and attributes to current scope
    #
    # @param scope [Object] directive scope
    # @param element [Object] root element's HTML (angular.element)
    constructor: (@scope, element) -
      @snap = Snap(element[0])
      Snap.load(@scope.svgPath, (data) ->
        console.log "Loaded map in Map!">
        # do your stuff here !

Your directive should be loaded after Angular's module initialization (make sure to require it somewhere), and then you can simply use it in your HTML code.

<div data-ng-controller="AParentScope">
  My awesome map <awsome-map data-path="scopeAttribute"></awsome-map>

(Implementing CoffeeScript classes with Angular can pose a challenge, so it's great to see others giving it a try :))

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Designate the preferred location for requesting the desktop site

Is there a way to specify the location of the 'Request desktop site' option? Here is the code I am currently using: var detector = new MobileDetect(window.navigator.userAgent); if(detector.mobile() != null || detector.phone() != null || det ...

Get every possible combination of a specified length without any repeated elements

Here is the input I am working with: interface Option{ name:string travelMode:string } const options:Option[] = [ { name:"john", travelMode:"bus" }, { name:"john", travelMode:"car" }, { name:"kevin", travelMode:"bus" ...

What are the necessary steps for incorporating Payment/Bank transactions into an e-commerce platform?

Right now, I'm utilizing HTML, CSS, jQuery, and some JavaScript to develop a website. As I work on creating the store section, I find myself unsure of how to incorporate payment methods and all related features. Are there any free "pre-built" systems ...

Disabling caching for Angular AJAX requests in Internet Explorer - a step-by-step guide

My Angular application successfully makes HTTP GET requests to the server when accessed on Chrome and Firefox. However, I recently encountered an issue with Internet Explorer where it caches the GET response. This causes a problem when I need to retrieve u ...

Encountering an error with $mdDialog.alert

I am attempting to activate an Alert Dialog using Angular Material Design. Within my controller, I have: angular.module('KidHubApp') .controller('ActivityCtrl', ['$scope','$meteor', '$stateParams', &apo ...

Leveraging ngsanitize alongside cascading style sheets

https://i.stack.imgur.com/o0rgS.png When utilizing ngsanitize, only the HTML is displayed without any applied CSS. For instance: The provided image is expected as output, but using ngsanitize results in only the text being visible. What additional steps ...

Exploring Data and Models within AngularJS

I am working on a WebApp that has a unique HTML layout Nav-column-| Center Column---- | Right Column Link1---------|Data corresponding|Data Corresponding to Link1-A Link2---------| to Nav-column------| (ie based oon Center Column Link) Link3----- ...

The icon in Material UI button is missing from the display

The button is functional, but the icon inside it isn't displaying correctly: Here is the code for the button: <Button color="secondary" aria-label="add an alarm"> <AlarmIcon></AlarmIcon> </Button> <But ...

Changing text color based on positive or negative value in JavaScript(React)

Greetings everyone! I'm currently working on a specific feature that requires the color of a value to change dynamically. If the value is above 0, it should be displayed in green; if below 0, in red; and if equal to 0, in orange or yellow. To achieve ...

Using Protractor to Verify Text Inside an Element

Is there a way to verify if a button contains text without specifying the actual text? I want to ensure that the button has text but not necessarily check for specific content as it may vary on different pages. Ideally, I'm looking for a dynamic solut ...

Tips for implementing React Browser Router within Material UI Drawer

I'm currently exploring how to implement Browser Router in React to populate the content section of a Material UI Drawer. While my code successfully links menu options to components displayed within the drawer's content section, a problem arises ...

When the textfield mui is set to shrink, an additional space appears in the label when using a font size of 12px

Struggling to customize the appearance of the textField component, particularly with the label when it is minimized: import * as React from "react"; import TextField from "@mui/material/TextField"; import { createTheme } from "@mui ...

What mechanism does angularjs employ to detect the $dirty state in the absence of a form tag?

I have implemented $window.addEventListener('beforeunload' to detect changes on a page successfully. Additionally, I am using $transitions.onStart... to monitor navigation with the browser buttons. I find it puzzling though, as my HTML template l ...

I'm curious if anyone has experimented with implementing TypeScript enums within AngularJS HTML pages

During my Typescript project, I defined an enum like this: enum Action { None = 0, Registering = 1, Authenticating = 2 }; In the controller, I declared a property named action as follows: class AuthService implements IAuthService { action: number; ...

The accordion seems to be stuck in the open position

Working on a website, I encountered a frustrating bug with an accordion feature. When clicking on the arrow, the accordion opens and closes smoothly. However, when attempting to close it by clicking on the title, the accordion bounces instead of closing p ...

Timeout set to run indefinitely

Looking at the code snippet below, it seems to be running at a frame rate of 60 frames per second. If any button is clicked, the variable isjump will be set to true and the following code will start executing. The goal here is to exit the jump() function a ...

The prop HandleClick is not being identified

Whenever I click on the sidebar, my menu should appear, but instead I'm encountering an error message saying "react-dom.development.js:86 Warning: React does not recognize the handleClick prop on a DOM." import { useState } from "react"; imp ...

Sending files in Express causes issues

I keep receiving the following message: express deprecated res.sendfile: Use res.sendFile instead Here is the code snippet in question: app.get('/*', function (req, res) { var myUrl = req.url.split('/')[1]; myownfunction(myUrl ...

User update function is not being triggered by Ionic 2 Express call

I am currently working on implementing a feature in my Ionic 2 program that updates a user field when triggered. The code snippet below is from my user-service.ts file: // Update a user update(user: User): Observable<User> { let url = `${this.u ...

Adjust the dimensions of the thead, tbody, and td elements on the fly

I've implemented this property in a .css file on the table shown below and there are 9 records. .fixed tbody td, thead th { width: 5.2%; float: left; } In my scenario, when there are 4 columns, the width should be 23.2%; for 5 columns ...