Generate a new computed property value on window resize


#1

I have a component which displays a list of posts bounded by a border. The initial listing of posts has variable text length, and hence the height of the containers is different. The component uses computed properties to find the maximum height of the containers and sets this height across all of them. This works fine for the initial render, but I’m stuck when I want to respond to window resizing events. The code below includes a function which needs to run on the resize, but how do I recalcuate the computed properties from that function? Or I am going about this in the wrong way!

Many thanks

import Component from '@ember/component';  
import { computed } from '@ember/object';
import $ from 'jquery';
import { run } from '@ember/runloop';
import { A } from '@ember/array';

export default Component.extend({

tagName: 'div',
height: null,
maxHeightArray: computed('height', function () {
	let array = A();
	$('.post-listing div.column-inner').each(function () {
		let height = $(this).innerHeight();
		array.push(height);
	});
	return array;
}),
maxHeight: computed.max('maxHeightArray'),
didRender() {
	let maxHeight = parseInt(this.get('maxHeight') + 50);
	$('.post-listing div.column-inner').innerHeight(maxHeight);
},
init() {
	this._super(...arguments);
	$(window).on('resize', run.bind(this, this.resizeWindow));
},
resizeWindow() {
	.... how to reset the computed properties from this function ? .....
}

#2

you can manually notify the observer system of property changes by using notifyPropertyChange. Maybe something like:

resizeWindow() {
  this.notifyPropertyChange('height');
}

You may need to play around with it a little bit to get the behavior that you want.

You could also look into “volatile” (non-cached) computed properties.


#3

Many thanks - I’ll take a look


#4

I concur with notifyPropertyChange However, I would add that the resize window event can be triggered many times in a single run loop. If this property would kick off a complex re-rendering I would suggest debouncing it.

import Component from '@ember/component';
import { computed } from '@ember/object';
import { debounce } from '@ember/runloop';

const RESIZE_DEBOUNCE_DELY = 250;

export default Component.extend({
  width: computed(function() {
    return this.element.scrollWidth;
  }),
  
  resizeWindow() {
    this.notifyPropertyChange('width');
  },
  
  didInsertElement() {
    this._super(...arguments);
    this._boundResize = () => debounce(this, 'resizeWindow', RESIZE_DEBOUNCE_DELY);
    window.addEventListener('resize', this._boundResize, false);
  },
  
  willDestroyElement() {
    this._super(...arguments);
    window.removeEventListener('resize', this._boundResize, false);
    this._boundResize = null;
  }
});