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

Experiencing difficulty in choosing an array in Javascript

Currently learning Javascript and working on a project that involves retrieving data from a weather API. Encountered a simple issue that I need help with. Here is the data I managed to fetch so far: {coord: {…}, weather: Array(1), base: "stations", mai ...

I'm looking to retrieve the selected value from my autocomplete box in React using the Material UI library. How can I

Here is a snippet of code that utilizes an external library called material ui to create a search box with autocomplete functionality. When a value is selected, an input tag is generated with the value "selected value". How can I retrieve this value in ord ...

Directive experiencing issues with Angular attribute expressions not being evaluated properly

Why is there a difference in how the expression inside the directive is evaluated using the link attribute compared to the template? Note that 'link' is only used here for illustrative purposes. I aim to pass data into a directive through its at ...

What is the best way to change a string that represents an array into an actual array?

Experimenting with something new... Imagine I have the following HTML code: <div id="helloworld" call="chart" width="350" height="200" value="[[4, 8, 1, 88, 21],[45, 2, 67, 11, 9],[33, 4, 63, 4, 1]]" label="['test1', ...

Tips for center-aligning layouts in Angular Material

I am struggling with the Angular Material flex layout, so I took this directly from the Angular Material website. Based on the documentation for layout container, items are arranged in a row with a max-height of 100% and max-width matching the width of th ...

Want to know how to center the value of a slider range input in your React project using NextJS and Tailwind

Can someone help me with the issue I'm having with the offset? Is there a way to link a value to the thumb? I've tried two methods, but one gives a significant offset and the other gives less. However, it seems more like a workaround than a solu ...

Enhancing infinite scroll functionality with ajax integration

After implementing a method to enable infinite scroll on my website using the following function: window.onscroll = yHandler; function yHandler(){ var wrap = document.getElementById('wrap'); var contentHeight = wrap.offsetHeight; var yOffset = w ...

Firebase and Angular 7 encountered a CORS policy block while trying to access an image from the origin

I am attempting to convert images that are stored in Firebase storage into base64 strings in order to use them in offline mode with my Angular 7/Ionic 4 application! (I am using AngularFire2) However, I encountered an error message stating: Access to i ...

The focus and blur events for the document in a content editable iframe are not activated by Chrome

As I modify the content of an iframe while it is in focus, I have noticed that this seems to function properly in Firefox. However, I am facing issues as the focus and blur events do not seem to trigger in Google Chrome! var iframe = $('#iframe') ...

What could be causing my Javascript prompts to not appear in my Express.IO .ejs file?

I am fairly new to JavaScript and exploring Node with Express.IO. I'm currently working on a project that involves tracking real-time connections made by different 'users' to the server. However, I've encountered an issue where my promp ...

Interactive Content Swapping: How to Use Javascript for Dynamic Link Updates

http://jsfiddle.net/bUjx7/42/ <script type='text/javascript' src='http://code.jquery.com/jquery-1.9.1.js'> </script> <script type='text/javascript'> $(document).ready(function () { $('.fieldreplace ...

Is there a way to prevent a web page from automatically refreshing using JavaScript?

I would like my webpage to automatically refresh at regular intervals. However, if a user remains inactive for more than 5 minutes, I want the refreshing to stop. There is an example of this on http://codepen.io/tetonhiker/pen/gLeRmw. Currently, the page ...

What are some strategies I can implement to effectively manage different errors and ensure the app does not crash

I came across a variety of solutions for error handling. The key concept revolves around this explanation: https://angular.io/api/core/ErrorHandler Attempts were made to implement it in order to capture TypeError, however, the outcome was not successful: ...

How can I make the lines of my drawer thicker in Material UI?

Currently, I'm in the process of implementing a left-side navigation bar for a web application I'm developing. The main challenge I'm facing is customizing the styles as desired when using the Drawer component. Specifically, I'm aiming ...

Only trigger the function for a single bootstrap modal out of three on the current page

I'm facing a challenge on a page where I have 3 bootstrap modals and I need to trigger a function only for one modal while excluding the other two. $("body").on("shown.bs.modal", function() { alert('modal Open'); //this alert should ...

Multipart form data processing without specifying files

I need help with uploading an image using node.js, express, and multiparty. My code is as follows: HTML <!DOCTYPE html> <html> <body> <form method="post" action="/img"> Select image to upload: <input type="file" name= ...

What is the hostname that npm install directs to?

My task is to set up multiple Node.js packages using npm on a standalone server that does not have direct internet access. I am able to specify an IP Address and port for access. When executing 'npm install' from the command line, where can I f ...

The user interface design transforms as a PDF file is being generated through html2pdf

I am experiencing an unusual problem while using html2pdf to convert an HTML page to a PDF file and download it. The conversion process is successful and the PDF file is downloaded without any issues. However, when I click on a button to generate the file, ...

Exploring vuelidate: demonstrating personalized validation messages alongside pre-built validators

I'm currently utilizing the vuelidate library to validate my forms. I've been attempting to use the built-in validators along with a custom message, as shown below. However, I have encountered issues with it not functioning properly. For referenc ...

What is the process for sending a POST request from a React app to a Node.js Express server?

I watched a tutorial video on resolving the CORS issue, which worked well for GET requests. However, I encountered an issue when attempting to send a POST request. Unfortunately, I do not have control over the third-party API and cannot make any changes to ...