SVG map to navigate to pages (or best way to reach to TS classes/methods from SVG/javascript)


#1

I am trying to use a clickable SVG map to navigate to different pages.

Using the onclick event on the SVG areas works well and I can put a message to the console or give an alert(), for instance.

However, I don’t know how to reach a method on one of my TS classes. Directly trying something like MapsPage.gotoZone(2); fails because “MapsPage is not defined”.

Is there a way to reach Ionic 2 classes/methods from javascript global scope (I believe this is what’s used in the SVG object).?

Or maybe there is a more simple approach to accomplish this task. Any help will be greatly appreciated.


#2

If MapsPage is your template class and gotoZone is a method of this class, I guess you should call gotoZone(2) directly in your template.

Can you post the code of your class and your template’s html?


#3

Thnak-you for looking into it. I just created an empty app, to test more easily, with:

ionic start svgClickTest blank --v2

Here I insert the SVG as follows:

<ion-content padding>
  <object id='mapObject' type="image/svg+xml" data="assets/icon/drawing.svg">
  </object>
</ion-content>

And in my class, the function is a simple console.log:

export class HomePage {
  constructor(public navCtrl: NavController) {  }

  gotoPage(id: number) {
    console.log("Goto page #"+id);
  }
}

The SVG is a single rectangle (drawn with Inkscape and saved as an SVG simple) with the property:

onclick="gotoPage(2);"

Which results on an error in console:

ReferenceError: gotoPage is not defined

Or, if I use:

onclick="HomePage.gotoPage(2);"

The error is:

ReferenceError: HomePage is not defined

It seems to me that it should be a namespace/scope problem, but…


#4

I haven’t played around with Ionic 2 and Angular 2 yet, but I think you need to replace the native js onclick for the Angular 2 event (click). See:

(click)="gotoPage(2)"

Here’s Angular 2 events reference: http://learnangular2.com/events/

Quick addition: for you to know, in ng2 you’ll use (eventname) to bind events and [propertyname] to bind properties:

<img [src]="myPic.src" (click)="zoomPic(myPic)">

#5

Now it produces and SVG analysis error. I don’t believe Angular will look into an SVG object.

The click event could be attached to the full SVG object, but I would like different regions of the SVG to be clickable (like in a map).


#6

What is the error you’re getting and how are you trying to bind click event to SVG regions?

Maybe this exemple would help you: http://dahlström.net/svg/mapcolorizer/countries-withname.htm


#7

@italo-nascimento your example put me on the correct route.

Instead to trying to reach my typescript classes from the SVG, I am attaching now the click event from my class into the SVG regions like this:

  ionViewWillEnter() {
    let map = document.getElementById("mapObject");
    let myPage = this;

    // object will load external SVG asynchronously
    map.addEventListener("load", function() {
      // The cast is to avoid typescript error: "Property 'contentDocument' does not exist on type 'HTMLElement'"
      // Type is probably not correct, but have the desired property... 
      let svgDoc = (<HTMLIFrameElement>map).contentDocument;  // Get the inner SVG DOM
      let zones = svgDoc.querySelectorAll("[id^=MyZone]");    // Query by id pattern
      for (let i = 0, l=zones.length; i < l; ++i) {
        let item = zones[i];

        item.addEventListener("click", function() {           // Attach desired event
                              let myId= this.id.split('_')[1];
                              alert(myId+": Moving to page #"+myId);
                              myPage.gotoPage(myId);
                          }, false);
        }
    },false);                      
  }

By the way, attaching Angular (click) events seems to work when the SVG is directly created in the template, but doesn’t when the SVG comes from an external file, which seems logical to me.


#8

I’ve found a similar exemple here:

But I can’t understand why do you had to parse your object. Couldn’t you just access contentDocument directly?

let svgDoc = map.contentDocument;

#9

Yes, that’s the idea. Javascript will happily execute the sentence:

var map = document.getElementById("mapObject");
var svgDoc = map.contentDocument;

But in typescript the first sentence returns an HTMLElement, which does NOT have the contentDocument property. It will, however, execute it somehow in browser (because the property really exists in the object), but the compiler will fail with a type error and would not generate code for the device.

The correct way is to provide the type for the compiler:

var map = document.getElementById("mapObject") as HTMLObjectElement ;
var svgDoc = map.contentDocument;

#10

Oh, now I got it!

Thanks for sharing your solution. :slight_smile:

I’ll need to implement something just like it real soon.