How to Create Custom Charts in Angular, Using SVG Elements and RxJS

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Applause] [Music] so i really really really really dislike chart libraries and it's not because they're bad it's because i have really bad experiences with them my first job in the industry i worked at army game studio and i had to build these like ridiculous charts with lots of animations and styles and i apologize brad he was the designer who designed these but you made my life so difficult by demanding that they look and work a certain way at my current job i inherited a chart that looks like this it's just a simple line chart like it really shouldn't be that bad but every time we upgrade that app's dependencies this thing breaks and it's just a struggle so what's going wrong for me well getting the design just right with a charting library can be very very painful you can spend a lot of time trying to get it to look the way your designer wants you to it's hard to keep them looking and behaving right across upgrades and i think that's partly because these libraries tend to be very configuration heavy you pass in this massive config object to most these charting libraries and hope you get the right result and it's hard to keep track of what changes on that api when you're updating those dependencies so my current job we had this big task to build basically like google trends but for energy data and it was wild design custom tool tips really interactive and i'm like oh no i really don't want to build this using another charting library but i don't want to build it myself either because in my opinion making my own chart seems terrifying there must be a reason why we don't build charts ourselves and always lean on libraries to get a good visualization so i decided you know what let me just spend a week or two and explore it was time to face my own fears to see what it was really like to build a custom chart myself but first my name is mike ryan i'm a software architect at a company called synapse where i'm working on making north american manufacturing more sustainable i'm also a gde and angular and web technologies and as mentioned i am one of the co-creators of ngrx a collection of open source libraries for reactive angular so i want to make building visualizations in my apps a little better than what my current experience is and if i'm going to do this i want to build on top of the existing skill sets that my teammates already have i also want it to be endlessly customizable i want to have full and final say over how this chart looks and feels and most importantly when i upgrade my app's dependencies it would be great if it kept working across those updates so how are we going to do this what big challenge do we have to overcome well a new foe has appeared for you all to learn with me today and that foe is svg so svg is an xml based vector image format it allows you to describe the way a vector image looks using xml like language and what's kind of neat is that html is an xml based language and most modern browsers actually have support for you authoring svg directly in the browser so if we take a look at an example i can use an html element the svg tag and then inside of that svg tag i can use svg elements it's a whole new world of elements but this one draws a circle with a radius of 200 pixels whose center x is at 250 and center wise at 250 producing the black circle on the right but my mind was blown further when i realized that you can actually style svg elements just using css custom svg properties like fill or stroke things i never even heard of before you can specify in your applications css and even further svg elements are part of the dom which means i can use things like query selector to get a hold of one and add some interactivity directly to an svg document so i'm like okay this seems pretty magic like i can i have this full language at my disposal for drawing interesting visualizations to the screen i can style them with css and i can make them interactive with javascript could i use them with angular and it turns out the answer was yes if we take a quick look at one of an angular component we can use svg directly in this component's template there's one small trick you have to tell angular that it's an svg element so every time you see me use an svg element today i have to put this svg prefix in front of the tag name that's just letting angular know that hey this isn't an html element don't try and make an html element here this tag name make an svg element then you can bind to properties of this element just using template property binding syntax so i can have some piece of data like the radius and i can control the radius of the circle using angular's template syntax i can also use view encapsulation to style just this circle using the components css it's like okay i think i'm onto something i think i'm starting to get the pieces together for how i build my own visualizations but if i want to build something complex i kind of want to do it the angular way i would want to encapsulate behaviors inside of components so i started thinking through well could i build custom components that run an svg instead of html the answer was yes but there was a caveat svg unlike html does not support custom element names so if i were to try and do something fancy like this and throw in a custom element inside of an svg document nothing's going to work for me browser's going to complain that's not legal but svg does have this g element and the g element stands for group and it's basically like the div of svg you can put whatever you want inside of it and what you can do is you can have custom attributes on elements so i said okay here's what i'll do instead of using custom tag names i'm going to author angular components that target the g element with a specific attribute name so let's take a look at how this might work so i've got a component it's just an angular component but there'll be a few things that are different here first is the selector instead of selecting a custom tag name i'm going to select a g element with my custom circle as the attribute on it and i'm going to put that svg prefix in front of it to let angular know that hey i'm talking about the svg namespace here not html now my template is all svg i've completely left html this is an svg component and i can use other svg elements directly in this component's template i can have inputs and outputs we're just talking about angular components now and i can receive data and use that to control the way my visualization renders i can also use the component's view encapsulation to style the way that these elements work so this is feeling pretty great i can make full angular components that work in svg instead of html now i have to do is take this component use it in an svg document and it'll render as i'd expect so i think i've got enough pieces put together i'm like okay let's take a stab let's actually try to build a visualization with this so how do we go about going from that circle to a full-blown chart with svg plus angular well i asked my designer friend hey throw together a challenging chart and he did not disappoint it's a little card and it's got a sparkline in it there's two things i want you to note i don't know if you can tell on the projector but the first is there's a custom tool tip animation the little dots expand out at you and there's a gradient underneath the sparkline to give the visualization i guess depth and so this is like one of those charts that should be simple to do in a charting library but ends up being pretty complex to put together and have it work correctly so let's try to build this with angular instead first i like to think about the component hierarchy what components do i have to create so i have my applications root component and it's going to consume what i'm calling the sparkline chart component and it's going to pass down whatever data we want to actually plot using this sparkline the sparkline chart component is going to create an svg context and it's going to start using svg components inside of it it's going to have one called area to show that gradient is going to have one called line to actually show the line that the user sees to visualize the data and it's going to have a dots component which is going to handle mousing over one of those dots it appearing and showing a tool tip the data flow is pretty simple but what does the data actually look like well we're just going to have the root component pass down a list of points to the chart component and a points is going to have an x and a y what value do i want to plot on the x axis and what value do i want to plot on the y axis so let's get the data wired together let's go to the root component and the first thing we're going to do is we're going to inject a service and this service is going to get us the data that i want to plot it's going to get me the number of movies that are released per year in the north american movie market pretty straightforward data then what i'm going to do is i'm going to take that list of movies by year and i'm going to map them into a list of points i'm going to say that the x-axis on my chart is the year and the y-axis is the number of movies released for that year now that i have my observable of a list of points i can take those points and i can bind them to a points input on a sparkline chart component using the async pipe so now that our root component is set up and we're passing points down to be plotted to the chart component it's time to talk about the really tricky part here which is actually plotting the data and this is the part that's like okay this is where things must get complex this is why we must use charting libraries and it is complex there's a good bit of math here but it really was not as bad as i was thinking so if we look at our card that we're actually plotting onto let's imagine we're going to display some data on this and first let's establish a coordinate system so we can talk about where to place things in the top left we'll have position zero zero x is zero here and y is zero there and the bottom right we've got the width and height of our card 500 by 120. what we need to do is we need to take some data point so here i have the number of movies released in the year 2007 is 538 movies and i need some way some bit of math to take that year and say okay 2007 belongs here 538 belongs here therefore i need to draw a point at the intersection of those two lines which belongs here how can i actually do this math and before we talk about it i need to give you some two new terms to learn which is domain and range domain is the data that we want to plot so for the data set that i'm talking about i've got a list of years 2002 to 2017 and the range is what i'm plotting that onto which is in this case since we're talking about the year and the x-axis it's just the width of the chart 0-500 i always get these terms confused i like to think that the d in domain means data the data that i want to plot and the r in range must mean the result the thing that i actually want to turn that data into and so translate these values from the top from our domain into our range we're going to need what's called a scale function what a scale function will do is it's going to take a year and it's going to tell you which position to put that year in so what does it look like to actually create a scale function well we're going to have to create a scale function factory that given a domain and a range my data and the result i want to project that domain onto i can give you a function back that can plot that data this is where the math is this is all the math it's going to take to actually plot out this entire chart so let's go through this line by line to understand we're going to ask the user to give me two values what's your minimum value in the range the lowest value here it's me 2002 and what's the maximum value 2017 for the range what's my minimum value well my coordinate system starts at zero and my maximum value is 500 that's the width of the chart then i'm going to take my minimum domain and my maximum domain and figure out what's the size of it i'm also going to do the same thing for the range what's the size of the range and now i'm ready to return a scale function that can take a year and actually figure out where it belongs and what it's going to do that's going to take whatever value you pass in subtract out the minimum domain divide divided by the size of domain using those values we can now calculate which pixel to actually plot this on so i know that was a lot of math and i want to go through it one more time and actually evaluate this expression by expression to figure out what's going on here so if i call this function with a domain of 2002 to 2017 in a range of 0 to 500 what's this gonna actually compute well the size of my domain is 15 because 2017 minus 2002 is 15. there are 15 total years in my data set the size of my range is pretty easy to calculate 500 minus 0 is just 500 it's 500 pixels wide that's how many pixels we have to play with now if i want to plot some year like 2006 what i'm going to do is i'm going to take 2006 and i'm going to subtract 2002 from it that gives me a result of 4. if i divide four by 15 which is the total size of my domain that gives me basically like a percentage what percentage point in my domain am i talking about once i have a percentage i can just multiply that position or that percentage by the size of the range and tack on the minimum range giving me hey 2006 belongs at pixel point 133 so we've got this ability to actually plot data now that's the math we had to go through to get the plotter let's actually use this now and see if we can get some data to display so first we need to jump into that sparkline chart component this is the first component in the actual visualization and what we're going to do is the users pass us in a set of points right but those points are like year and number of movies released per year what we need to do is we need to translate those into pixels to where we actually want to draw stuff so i'm going to use a little utility function i've written called get bounds it just searches the list of points and finds me the minimum and maximum x and y in the entire list and i can use that to create scale functions for the x-axis and the y-axis for the x-axis my domain is my minimum x value i found and my maximum x value i found 2002 and 2017. for my y my minimum value is going to be what was the lowest number of movies released across all those years and what was the greatest number of movies released across all those years once i've got my scale functions for the x and y axis i can map over each of those points and actually figure out hey where am i supposed to draw something so i've done all the math i need i've created those scale functions and i've used them to figure out where to draw stuff now let's actually draw some stuff so first let's just draw some dots to figure out hey did we get even close does this look like we're on the right track so i've got an angular component here this is an svg angular component and it's going to have one input a set of points and this is the actual like computed set of points what are the positions where i need to draw something at and from here it's just angular i can use ng4 of that list of points and draw a circle for every point that i was passed when i get a point i'm going to set the center of that circle to be where the point was drawn once i got the component done i can add a little bit of css and then i can go use this component inside of my sparkline chart now if i run the app i've actually got my data plotted all that math and then a little bit of ng4 and suddenly i'm starting to show data in the right position on this card but we don't just want to show dots we've also got to show a line going through them and we have to do that little gradient business so how do we do paths and polygons with svg well it gets a little magical here svg has a path element and it has a d property on it it stands for dark incantation to draw a shape or at least that's what it looked like to me the first time i saw it it actually turns out that this just means a list of commands to draw a path so the path command syntax is you have a function and then you have some arguments that function svg paths support a number of functions but we only need to learn three today the first function is m and m stands for move almost every path you encounter in svg is going to start with the move function this is going to say where do i want to start drawing so i'm going to start drawing at 72-0 those are the two arguments first is x position second is y position so start at 72 over and 0 down then i'm going to use the l function and the l function stands for line so line takes again two arguments an x and a y where should i draw a line to from the previous position i was at from here we can keep drawing lines to build up a shape but then there's one final command we need which is z i have no idea what z stands for i couldn't find it if you know i'd love to know but it means close the path wherever i am now and wherever i started from the very beginning just draw a final line there so you might recognize the shape we've just drawn the path commands to draw the base of the angular logo so it looks like dark wizardry once you unpack it and understand the syntax it's like oh i'm just moving and drawing lines between coordinates so i could draw i could write a function that does that if i had a function called get path commands for line and it took a set of points then what i would need to do is i would first destructure that list of points into two pieces of data i need the first point and i need all the points after that in an array and the reason for this is because i'm going to use that first point to do that in command move i'm going to start my drawing context that whatever the very first point is that i received then for every other point after that i'm going to use the l function which stands for line to draw a line in order so now given a list of points i'm going to create a command that will draw a path that just draws a line through those points so let's try it out let's write an angular component again this is an svg component and it's going to have really straightforward functionality i'm going to have an input called points it's going to take a list of points this is the data we're trying to draw and it's going to say hey give me the path command to draw a line through these points it's going to start off on a property and i'm going to bind that property to the d attribute on a path element that's all i have to do to now draw a line i'm going to take this component i'm going to slap it into my sparkline chart components template and then i'm going to run the app again and i've got a line passing through my points it's starting to feel like i'm pretty good at this only thing that's left is to really draw that gradient beneath that line so i don't need a line this time i actually need a polygon right i need it to go through all the points and then i need to like draw a box beneath the bottom part to fill it in with a gradient well we saw how to draw a polygon earlier and in fact we can actually start with some of the code we've already written so if we wanted to get a path command that drew a polygon we'd first start by taking in a set of points to start to feel pretty familiar but then i'm going to reuse get path commands for line to get me all the commands i need to draw a line through each of those points then i'm going to use my get pounds utility to find my minimum and my maximum x and y's and i'm going to draw use them to draw two lines the first is so i've drawn my line through all the points now i need to draw a line to my bottom right and then a line to my bottom left that will close off the polygon or get me close enough so now i can use the z command to close it up so i've got my function to draw a polygon now i need to draw write my area component again it turns out it's really not that bad i'm going to do the same thing i did last time i'm going to have an input of points pass into points get my path command to draw that polygon and i'm going to bind that polygon's path to a path element i'm going to take my new component that i've written add some css to do that fancy gradient fill and then i'm going to slap it in my sparkline chart component run the app again and now i've got my fill beneath the line i'm really close now to having built the chart that my designer want me to build but what it's missing is that when you you're not supposed to actually show the marks you're supposed to have them like appear when you hover over it and show a tool tip so it's like okay let's let's figure it out how do i do tool tips well before we do the tool tips let's get the little behavior right to where they only appear when you hover over one let's go into our dots component and let's figure out how to do this with svg and you already know how to do this with svg because you're going to do it with just css so you're going to take the styles for the circle and we're going to throw on some pretty familiar css attributes i can set the opacity of a circle just like any other element and i'm going to start the opacity at zero i'm going to use a transition property so i can transition the opacity so that when the user hovers over a circle i'm going to animate it from an opacity of zero to an opacity of one again i'm just leveraging things i already know i'm using the css skill set that i've built up over this all this time now when i run the app i've got the behavior i was looking for those dots only appear when i actually move my cursor over them now's for the really hard part though that tool tip let me go back to the point interface we started with from the beginning and we said that a point was an x and a y let's expand this ever so slightly and say that a point is an x and a y and some kind of message to show in the tool tip we're going to go back to our root component now where we're mapping over each of those movies we're going to say okay your x is the year your y is the movies but now your tool tip is this message i'm going to want to show the user hey there's this many movies in this year so i've got all the data wired up i know tool tip to show but how do i show the tool tip again we can just leverage angular here i don't have to do anything special i'll just reach for angular material and use matte tooltip why not it's an angular app i can use the things i've already got at my disposal now if i go running the app it just works the way i expect it to you hover over a circle and now we're just going to show the tool tip using all the skills that we've already learned so i'm feeling really great about this because i think i've set out and achieved the goals i wanted to accomplish i have built on top of existing skill sets other than that little bit of complex math this was just some templates some styles and passing data around using inputs it's got endless amounts of customization because since this is an svg visualization and i can use css to style svgs i can endlessly change the way this thing looks and i think that this is built to last well into the future because i didn't use any other libraries i just used my experience with angular and i was just using angular features like template syntax so if you know angular it turns out you can make visualizations on your own with a low amount of code that look any way you want them to by leveraging svg so go play with the slides in a working example you can clone this repository now dig into the code and start playing with your own visualizations thank you [Music]
Info
Channel: Briebug
Views: 1,241
Rating: undefined out of 5
Keywords: Angular, dashboard in angular, dashboard creation in angular, Angular Chart libraries, open source angular chart libraries, Reactive charts, SVG elements in Angular, Angular 12, Angular 11, Angular 10, Angular 9, Web design, Data visualization in Angular, SVG charts, software developer, Angular Colorado, ngColorado, Rocky Mountain Angular, Briebug Software, NGRx, Mike Ryan NGrx, @MikeRyanDev, Reactive Applications, RxJS, RxJS in Angular, material CDK, SVG COMPONENTS
Id: iwjMAf345-8
Channel Id: undefined
Length: 25min 27sec (1527 seconds)
Published: Mon Oct 26 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.