Reading attributes of SVG view elements

There are a couple of SVG boxes and a button that turns one of them on and off.

https://ellie-app.com/7Vw7fw3fzGBa1

Now I want to control all SVG boxes, changing their color and maybe other attributes for visibility. Is there any way to change their attributes, such as fill without moving them into model?

I want to control SVG attributes directly, because I am afraid that moving them from static HTML into model properties will affect performance. Like when I will need to add d3 style animated transformations to them.

You could use a port and make the change with JavaScript. I don’t think that would mess up Elm’s vdom algorithm as long as you aren’t also controlling that same property in Elm.

I would just put it in the model though and only optimize if you actually see performance problems. Elm is usually very fast.

1 Like

I have to echo the point about worrying about performance before it’s an issue. My project has a gigantic base64 encoded blob (sometimes a 20+ MB file) getting passed around in the model and update function and there’s zero issues whatsoever.

A few properties or values for setting the background of an SVG in your model is probably not going to cause any issues.

3 Likes

What is the best way to move these box properties to the model then? I plan to have about 40x8 grid totaling 320 boxes, and while I can copy-paste the box code like in the example below, such code doesn’t look right.

type alias Model =
      { boxcolor : String
      , box2color : String
      , box3color : String
      }
...
view model = svg
      [ width "400", height "100" ]
      [ rect [ width "10", height "10", fill model.boxcolor ] []
      , rect [ x "10", y "10", width "10", height "10", fill model.box2color] []
      , rect [ x "20", y "10", width "10", height "10", fill model.box3color] []
      ]

Don’t move the boxes colors to the model. That’s an implementation detail. Those boxes colors stand for something. Create a datastructure that holds the state of your app and then use that data structure to generate the SVG.

Here is an example: https://ellie-app.com/7XNY2JkKQpma1

I used the concept of an Item to model the list of boxes. The colors became a custom type to simulate some states that can be toggled. I then used a colorToString to output that state to something that can be used by fill.

The Toggle now happens by index but that’s just for demonstration purposes. The main idea is that you change the state of the app, not the details of the view. Sure, the two things can be connected (for example, I still chose color as the field name) BUT, you don’t have to use the same names in the model and the view. You just need to have a clear correspondence.

Here is the same example with just the names changed: https://ellie-app.com/7XP6jKWc8cKa1

1 Like

If you give us more info about what you’re trying to build (why do you want 320 colored svg boxes?) we might be able to help you find a good structure for your model. Good models in elm rarely have a very direct correspondence to the ui appearance.

I don’t see any event handlers. Does this regenerate HTML on every button click? That doesn’t seem effective.

Create a universal dashboard with boxes. In this specific case for test runners to show people where they can help.

By giving it an URL like Travis CI - Test and Deploy with Confidence it should show green (passed), red (fail), yellow (skipped TODO), gray (skipped), ? (expected failure) boxes. By hovering, it should display specific info about test name or reason why skipped, and maybe a link to test case or contribution docs.

Event handlers that do what? I only added an even handler to the button you had in your original example.

Elm’s html library is backed by a virtual-dom (one of the fastest around). Only changes to the output are ever applied to the actual dom. If performance becomes an issue there are ways (e.g. Html.Lazy) to optimize the code.

No. Elm is very clever about updating the DOM. Every time your model changes your view function is run. Your view function is very fast because it doesn’t have anything to do with HTML or the DOM. It just creates a simple in-memory representation of what your view should look like. JavaScript is very fast at working with objects and plain data, so your view function can get very, very complex without causing any performance issues.

Then Elm finds the differences between the DOM as it is right now, and the description produced by your view function and only updates exactly the elements needed to synchronize them. So even though your entire view function is run when a box changes color, only the one box that changes color is actually mutated by Elm. This entire process is very fast.

This is called the virtual-dom, or v-dom algorithm. It’s used in React and other technologies as well. There’s lots of information available online about how it works if you’re interested.

TL;DR: I very much doubt you’ll run into any performance issues with what you described. Elm is quite smart about how it optimizes DOM mutation. I’d just roll with it and come back if you have performance issues.

I’d suggest something like this:

type alias Model =
   { testRuns: List TestRun
   }

type alias TestRun =
   { status : RunStatus
   , name : String
   , url : Url
   }

type RunStatus
   = Passed
   | Failed String
   | Todo
   | Skipped String

The Failed and Skipped states store their description of what went wrong. If you aren’t familiar with enum/sum types here are the docs.

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.