Nearly forgot to post something this week. It feels stubbornly like a Saturday for some reason, which doesn’t bode well for me remembering that tomorrow I’m actually supposed to be going into the office for the first time in ages. It’s not that I’ve particularly avoided it up to now, but I’ve got into such a routine that it doesn’t usually cross my mind that I could go in until I’ve already made a coffee and started work. Anyway, my boss’s boss is in the country at the moment and in our office tomorrow (and Tuesday I think), so it’s probably a good idea for us to actually be there to meet him.
I’m not really sure much has happened since last time. I spent a couple of hours working out how to make Chartkick.js play nicely with Phoenix LiveView, and because I was feeling public-spirited and there wasn’t much detail around of how to do it (I think it’s one of those things that’s just simple enough that if you look on forums you see people asking, being given a hint then coming back saying “thanks, that made sense” without explaining exactly what they did) I decided to write it up.
Other than that, I think everything’s just running. We’ve got a trail of giant painted owls in Ipswich at the moment, so I went out to run round them on Wednesday. I missed a few, and still managed to go a couple of miles further than the planned 10. Then Stowmarket Friday 5 (much less roasting this week, but ended up slower than it felt), parkrun yesterday, and a nice chatty 14 mile trail run today on the Stour Valley Path, including a few wrong turns and a stop for an ice cream and a little paddle in the river. My Garmin is now recommending 3.5 days’ rest, which seems a bit excessive (especially as I have another 5k race on Tuesday).
I was converting some “old-fashioned” Phoenix code to a LiveView today, and got stuck for a while trying to get a Chartkick graph to render properly. I found various hints online about how to do it, and I assumed it was a case of phx-update="ignore" and some kind of Javascript hooks, but it took a bit of time to figure out the details. I thought I’d be helpful and write up an example, as it turns out it’s not that hard.
First let’s create the application. I skipped Ecto because we don’t need a database for this example.
So far, so good. Time to try it with a live view! Replace the root route in lib/my_app_web/router.ex with live "/", PageLive, and create the module in lib/my_app_web/live/page_live.ex, rendering exactly the same html as we had before:
defmodule MyAppWeb.PageLive do
use MyAppWeb, :live_view
@impl true
def render(assigns) do
~H"""
<%= "{foo: 1, bar: 4, baz: 2, qux: 3}"
|> Chartkick.bar_chart()
|> Phoenix.HTML.raw() %>
"""
end
end
When this renders, we see the chart flash up briefly, then … hmm.
Perpetually loading
The reason we see the chart appear then disappear is that LiveView renders the page twice when it’s loaded – once statically, for search engines etc, then a second time, when it swaps in its dynamic Dom. Here are the elements that Chartkick initially creates (a placeholder div and a bit of javascript that will call the library and swap in the generated graph):
<div id="b5b7b558-1220-4546-a3e8-15e2e607b312" style="...">
Loading...
</div>
<script type="text/javascript">
new Chartkick.BarChart(
'b5b7b558-1220-4546-a3e8-15e2e607b312',
{foo: 1, bar: 4, baz: 2, qux: 3}, {});
</script>
The problem is that a script tag inserted dynamically into the Dom, unlike one in the original page source, doesn’t get executed. There’s also a potential issue with LiveView and Chartkick both trying to manipulate the same elements, leading to unpredictable behaviour when the page data is updated. We can address the latter issue by telling LiveView to ignore the chart when updating the page:
defmodule MyAppWeb.PageLive do
use MyAppWeb, :live_view
@impl true
def render(assigns) do
~H"""
<div id="chart" phx-update="ignore">
<%= "{foo: 1, bar: 4, baz: 2, qux: 3}"
|> Chartkick.bar_chart()
|> Phoenix.HTML.raw() %>
</div>
"""
end
end
This actually works in our simple case, but what if the chart wasn’t always shown, or was part of a component that was live patched in? As the simplest possible illustration of this, let’s add some show/hide buttons and only render the element when we toggle @show to true:
defmodule MyAppWeb.PageLive do
use MyAppWeb, :live_view
@impl true
def mount(_params, _session, socket) do
{:ok, assign(socket, show: false)}
end
@impl true
def render(assigns) do
~H"""
<input type="button" phx-click="hide" value="Hide"
disabled={not @show} />
<input type="button" phx-click="show" value="Show"
disabled={@show} />
<%= if @show do %>
<div id="chart" phx-update="ignore">
<%= "{foo: 1, bar: 4, baz: 2, qux: 3}"
|> Chartkick.bar_chart()
|> Phoenix.HTML.raw() %>
</div>
<% end %>
"""
end
@impl true
def handle_event("hide", _params, socket) do
{:noreply, assign(socket, show: false)}
end
def handle_event("show", _params, socket) do
{:noreply, assign(socket, show: true)}
end
end
Initially the chart isn’t there, as expected:
Hidden …
But when we click “show”, we’re back to our perpetual loading indicator:
… and still kind of hidden
We need a way of triggering the Chartkick script when the view’s updated, and that’s where hooks come in. First define a hook in assets/js/app.js (I’m not entirely sure this is the best way of running the javascript, but it works!):
let Hooks = {
RenderChart: {
mounted() {
console.log("RenderChart")
console.log(this.el)
eval(this.el.getElementsByTagName("script")[0].innerHTML)
}
}
}
let liveSocket = new LiveSocket("/live", Socket,
{hooks: Hooks, params: {_csrf_token: csrfToken}})
There are other hooks as well as mounted – If we were dynamically updating the chart element itself we’d probably want to also execute the script on updated, for example.
And that’s it! The code’s here, and I hope it helps someone avoid a bit of head scratching.
This week’s main news is that Shadow cat has gone missing again. She didn’t turn up for any food on Wednesday, and I haven’t seen her since. She did disappear for a week a year ago, and turned out to have been hiding in my garage the whole time, but I’ve checked several times and she’s definitely not there this time. Still holding out hope that she might reappear again, but obvious the longer she’s missing the less likely it gets.
I put together a cryptic crossword for the first time in a while, and people seemed to largely enjoy it (the kind of people that have any interest in such things anyway), so I should probably do this more often. This one had a theme, which I usually seem to end up doing – you’d think it would be easier without, but it feels a bit weird for some reason.
I spent Monday afternoon grappling with a failing build caused by race conditions as some tests tore down database sessions after doing things which published messages to a queue, which caused things listening to those messages to terminate unexpectedly. There’s probably a lesson there about decoupling unit tests from the database, but in the meantime I tried all sorts of tweaks without any success. Then just after I’d given up for the day José Valim retweeted a tweet from German Velasco linking to his blog post about how Wojtek Mach had fixed a similar issue in LiveView. It wasn’t exactly the same situation as I had, but it was enough of a nudge in the right direction for me to finally fix the issue the next morning.
I did a quick TDD demo for some colleagues. It’s always hard to pitch this kind of thing so it’s neither too simplistic nor so complex as to be impenetrable or boring – this time I prepared a simple to-do web app, then test-drove a made-up feature involving a button which popped up a random item out of all those with the highest priority which weren’t yet marked as done. This meant I could throw in a bit of UI-based acceptance testing, then drive the domain layer with unit tests, including injecting the “random” algorithm to make the tests repeatable. Seemed to go OK, but it’s always hard to tell with these things. As usual, my main aim was to at least show people what a short “red-green-refactor” loop actually looks like, to dispel any misconceptions that TDD means “write all the acceptance tests to match the spec, then keep coding until they all pass”.
I marked Friday’s heatwave by running the Bury Friday 5. Not altogether the most pleasant experience, especially on top of a 40 minute delay getting there thanks to the A14 being closed because apparently it had melted. My slowest time of the series so far, but it turns out each of them has been slower than the last. Surely I can buck that trend at Stowmarket next week?
I failed to finish the hedge cutting that I started last weekend, blaming a combination of heat and lack of brown bin space (although it was mostly laziness), but I did at least spend a bit of time trying to pull up of cut down the worst of the brambles in what I’m stubbornly referring to as my “wildlife lawn”.
I encountered this chap on my long run today. Fortunately he seemed quite chilled and didn’t decide to charge at me, but just being stared at as I passed through his field was a little unnerving.
The fox has continued to hang around a fair bit, and I’m starting to think it is the same one after all.
A fox …
On Wednesday I finally got round to replacing the rat-chewed rubber seal between the sink waste pipe and the drain. But not before the rat made an escape through the gap under the open dishwasher, and spent Tuesday afternoon alternately hiding under things and chasing/being chased by the cats (I’m not really sure who had the upper hand). I had to take the (fitted) dishwasher out to get to the pipe, which was a pain, but at least when I put it back I managed to do a better job of lining it up than when I originally fitted it, so after five years the door’s finally no longer wonky!
… and a rat
A fairly uneventful running week, apart from a warm Framlingham Friday 5, and some mild embarassment at Tuesday’s club session involving losing the rest of the group, assuming they’d finished, heading back to the sports centre where we meet, then discovering that they’d actually just gone round the corner for a time trial. At least there were three of us, so I don’t have to shoulder all the blame!
My Buffy rewatch has now gone past S5E16, The Body – a strong candidate for the most emotionally battering 40 minutes of TV ever made. And because I’ve been listening to the Buffering podcast at a much-accelerated rate compared to their original release schedule, it feels like they’re in a whirlwind of coping with that episode, the beginning of lockdown, Black Lives Matter and various accusations against Joss Whedon.
I’ve been waiting for the final series of Brooklyn Nine-Nine to appear in the UK, thinking I might reactivate my Netflix account for a month to watch it when it arrived. I just discovered that (a) it’s moved to E4, and (b) as far as I can tell they’ve already removed the first few episodes from All 4. Harumph.
Today I actually managed to do a bit of gardening, strimming down the weeds in the driveway and making a start on cutting the hedges (I’d been putting it off mainly due to laziness, but also because I didn’t want to accidentally disturb any nesting birds. I did find an old nest, but fortunately nothing current). To be honest, this is still only a small subset of the bare minimum that needs doing, but at least it’s a start!
Well that’s the four day weekend pretty much over, and I still didn’t manage to summon up enough enthusiasm to cut my hedges (or hack down the weeds in the driveway, or any of the myriad other jobs that need doing). And because of the weird Thursday/Friday nonsense, we now get tipped straight back into a full week of work! Incidentally, can anyone [he writes in the pretence that more than one or two people are actually reading this] explain why the Jubilee celebrations were in the year that’s 70 years after the accession (which happened in January), but around the date of the coronation (which happened in 1953)? I guess it’s just about getting decent weather.
As you would expect, I largely paid no notice at all to the various celebrations and the like, although I did go to what was technically a jubilee garden party on Saturday (but was really just a bunch of friends drinking beer in someone’s garden). I also dug out some clippings of when seven-year-old me won a local radio competition for the silver jubilee. I didn’t look particularly happy about it, or more likely I just hated having my photo taken.
Fame at last (1977)
On Friday I ran in the long-delayed Run for A Rose 10k, on the flat but windy (and not terribly exciting) RAF Woodbridge runway. This was a charity event in memory of Angela Rose (hence the capitalisation of the name), a friend and local runner who died of cancer at a tragically young age, and was originally supposed to happen in early 2020. Ironically, it was originally postponed because of Storm Dennis, but that pushed it into the void of 2020–21. It was a shame it hadn’t been publicised a bit more, as it felt a bit sparse (I finished in 44 minutes something, and the people in front of and behind me were on 43 something and 45 something respectively. Ozzy, who won it, was miles clear of second place).
In rat-up-a-drainpipe news, I noticed a bit of a smell returning to the kitchen today, and discovered that the tea-towel I’d wedged in the pipe gap has mysteriously vanished. Not sure whether it was removed by reprobate rodents or just got washed away, but hopefully it hasn’t caused a blockage elsewhere (I guess I should poke around under the inspection cover and try to find it).
My new coffee grinder, to complement my new espresso machine, unexpectedly arrived earlier in the week. I say unexpectedly because the manufacturer’s website that orders placed when mine was would be delivered in July (which had just changed from June while I was procrastinating). Not sure why they’d say that then ship it out in May, but I’m not complaining.
I chose today’s run route to go through a massive local field of poppies – apparently they’re there every year, but this year will be the last because the field is about to be turned into a housing estate. Progress, eh?
Poppies
Finally, I found a fox sleeping in my garden again. I don’t think it’s the same one that has visited on and off for the past few years, or if it is it’s got a lot more nervous all of a sudden (apart from the whole casually sleeping in people’s gardens thing).