1

I have a scrollview i'm inserting divs into.

however, the content of these divs are being rendered by javascript templating engine.

when famo.us initially creates this page, it seems it calculate the height of the div content to be zero, so the divs in the scrollview all end up being on top of each other. I'm guessing this is because template rendering happens a couple of ticks later..

is there a way to force famo.us to recalculate/reflow its layout?

dcsan
  • 11,333
  • 15
  • 77
  • 118

3 Answers3

2

I too owe johntraver a beer for his excellent answers on famo.us questions here. This is work derived from one of his previous answers, I use it for surfaces on scrollviews with dynamic html content (and height):

/* AutoSurface.js */

define(function(require, exports, module) {
    var Surface = require('famous/core/Surface');
    var Entity = require('famous/core/Entity');

    function AutoSurface(options) {
        Surface.apply(this, arguments);
        this.hasTrueSize = false;
        this._superCommit = Surface.prototype.commit;
    }

    AutoSurface.prototype = Object.create(Surface.prototype);

    AutoSurface.prototype.constructor = AutoSurface;

    AutoSurface.prototype.commit = function commit(context) {
        this._superCommit(context);
        if (!this.hasTrueSize) {
            this.trueHeight = Entity.get(this.id)._currTarget.clientHeight;
            this.setSize([undefined,this.trueHeight]);
            this.hasTrueSize = true;
        }
    }

    module.exports = AutoSurface;
});

Commit is called every tick so you should be able to wait until you have a height > 0 or what you need before you set hasTrueSize = true.

M_rivermount
  • 511
  • 3
  • 11
  • Thanks for the acknowledgement! The previous answer you may have been referring to was my first attempt to deal with true-sizing, but does not handle resizing the surface. Using modifier I can resize the surface, without hard setting any values to the surface itself. – johntraver Jun 28 '14 at 23:04
1

This is very similar to the answer I just posted here.

how best to create a single scrollable view in famo.us?

You can wrap your true-sized surfaces in a sized modifier. This allows for scrollview to know the actual height of the surfaces within, and thus properly scroll. I am using the _currTarget of surface in the Modifiers sizeFrom method. The ternary operation is to prevent errors pre-deploy of the surface.

Here is that example revised for multiple cells in scrollview.. Hope it helps!

var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface');
var RenderNode = require('famous/core/RenderNode');
var Modifier = require('famous/core/Modifier');
var Scrollview  = require('famous/views/Scrollview');

var context = Engine.createContext();

var content = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod \
                tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, \
                quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo \
                consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse \
                cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non \
                proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";

var scrollview = new Scrollview();

var surfaces = [];
scrollview.sequenceFrom(surfaces);

for (var i = 0; i < 10; i++) {
    var surface = new Surface({
        size:[undefined,true],
        content: content,
        properties:{
            fontSize:'24px',
            backgroundColor:'hsl('+(i*360/20)+',100%,50%)'
        }
    })

    surface.pipe(scrollview);

    surface.node = new RenderNode();
    surface.mod = new Modifier();

    surface.mod.sizeFrom(function(){
        target = this._currTarget;
        return target ? [undefined,target.offsetHeight] : [undefined,true];
    }.bind(surface));

    surface.node.add(surface.mod).add(surface);

    surfaces.push(surface.node);
};

context.add(scrollview);
Community
  • 1
  • 1
johntraver
  • 3,612
  • 18
  • 17
  • interesting. so sizeFrom() - how does this know to wait until the surface is deployed? or, how does its return value change? I would think it would evaluate at first run, and therefore still be pre-deploy. – dcsan Jun 28 '14 at 09:46
  • You are thinking the right way. That is why I check to see if the target exists before I try to access offsetHeight! – johntraver Jun 28 '14 at 22:58
  • right but afai can tell, that code should only run once - its not part of a reactive template or anything. So then it will only take the one code path to return the [undefined, true] always. The 'this._currTarget' is bound to the surface, so not sure what that would return exactly either. the surface._currTarget seems to be related to the rendering loop, but not sure what it is exactly: `if (!this._currTarget) this.setup(context.allocator); var target = allocator.allocate(this.elementType);` << i haven't peeled the layers on that onion yet... – dcsan Jun 29 '14 at 03:46
1

Instead of hacking deploy of using a sizeFrom-modifier, there is a much easier hack:

Let a surface return it's actual calculated height instead of it's specified height.

Override the default getSize function with this:

surface.getSize = function() { return this._size || this.size }

Full example in a JSFiddle

Warning: This breaks setting the Size with a modifier higher up the rendering tree. (But since you set size explictly anyway, this should not be a problem)

markmarijnissen
  • 5,569
  • 2
  • 28
  • 34
  • This seemed to work properly with setting a surface to size [undefined,true]. Can you elaborate on how this breaks setting the size with a modifier? – holmesal Sep 14 '14 at 05:59