bug(slider): Slider doesn't render values in SSR (Universal)
Is this a regression?
- [ ] Yes, this behavior used to work in the previous version
The previous version in which this bug was not present was
No response
Description
- When rendered on the server with Angular Universal, the slider thumb always renders at the left-most position, regardless on the actual value
- What's more, a range slider only shows the "start" thumb, but not the "end" thumb.
Reproduction
(don't know where to host a Universal app)
Expected Behavior
- Two thumbs are rendered in SSR for a range slider
- Slider thumbs positions in SSR reflect the actual slider values
Actual Behavior
- Only the "start" thumb is rendered in SSR
- The the start thumb is rendered in the left-most position in SSR, regardless on the actual value
Environment
- Angular: 15
- CDK/Material: 15
- Browser(s):
- Operating System (e.g. Windows, macOS, Ubuntu):
@amakhrov I believe this should be fixed in v17. I created this example: https://github.com/wagnermaciel/demo
Let me know if things appear fixed for you as well
Nope, the demo looks as if nothing changed:
- Only the "start" thumb is rendered in SSR
- The the start thumb is rendered in the left-most position in SSR, regardless on the actual value
It's easier to repro if you block main.js in the browser network console, to make sure you're only looking at the server-rendered content
Ah I see, I was getting tricked with main.js quickly hiding the issue. I'll take a closer look at this and see where things are going wrong
A quick side note on the bug description: Both thumbs are being rendered, but they are just displayed overlapping at a broken/default position. This is likely because we're using something like getBoundingClientRect, etc.
Update
The root problem is that we cannot call getBoundingClientRect() on the server.
Slider thumbs behavior explained
The slider thumbs are positioned using transform: translateX(${someValue}px). In order to calculate someValue we need to know the size of the whole mat-slider which depends on us calling getBoundingClientRect(). Since we are on the server, getBoundingClientRect() does not exist and therefore we cannot determine someValue. That is why both thumbs are just overlapping at the 0th position - they both just have transform: translateX(0px)
Theoretical fix?
We'd need to do a lot of targeted overrides on mdc's styles so that we use percentages instead of px. From what I can see, there are 3 main places we'd need to tweak: track, thumb, and tick marks. This change would be very hacky but it is theoretically possible
Is it possible to define all positions relatively in percents instead of absolutely in px? This way, we wouldn't need getBoundingClientRect()
Yeah, that's the part that's theoretically possible, but if we did it on our end it'd require a bunch of hacks due to how material design has structured the html/css for these components. We'd need to override a bunch of their css in order to get things working
Ideally this would be changed on their end, but that would add a ton of churn to getting this fixed
Update: I brought this issue up in our latest team meeting to see what options we have available for getting this issue resolved.
We concluded that just providing a basic loading state would be sufficient as long as it looks good & intentional.
One way this might look is setting the initial position of the slider thumbs to 0 and 100 and then animating the repositioning of thumbs to match the true initial state.