2

I'm looking for an event that tells me when a surface has been rendered so I can call methods like surface.focus().

If I call focus immediately after I create the surface it doesn't work. If I call it in a timer after some arbitrary time I expect it to be rendered, it works. So there must be an event I can use.

For example if I create a widget that builds a bunch of surfaces inside a view, how do I know when that widget has been fully built and more importantly, when is it being rendered so I can set focus on an input surface?

Thanks

agconti
  • 17,780
  • 15
  • 80
  • 114
sday
  • 1,041
  • 14
  • 22

3 Answers3

3

I marked johntraver's response as the answer, but I also wanted to include a complete working example for the InputSurface for people like me just learning famous. This code subclasses InputSurface so that the focus method will work.

Once the InputSurface is rendered it gains focus.

TextBox.js

define(function(require, exports, module) {
    var InputSurface      = require('famous/surfaces/InputSurface');
    var EventHandler      = require('famous/core/EventHandler');
    function TextBox(options) {
        InputSurface.apply(this, arguments);
        this._superDeploy = InputSurface.prototype.deploy;
    }
    TextBox.prototype = Object.create(InputSurface.prototype);
    TextBox.prototype.constructor = TextBox;
    TextBox.prototype.deploy = function deploy(target) {
        this.eventHandler.trigger('surface-has-rendered', this);
        this._superDeploy(target);
    };
    module.exports = TextBox;
});

implementation

this.email = new TextBox({
    size: [300, 40],
    placeholder:'email'
});

var event_handler = new EventHandler();

event_handler.on('surface-has-rendered', function(control){
    control.focus();
});

this.email.pipe(event_handler);
skb
  • 30,624
  • 33
  • 94
  • 146
sday
  • 1,041
  • 14
  • 22
2

This is another case of when subclassing may be your easiest and most straight forward approach. In this example, Surface is subclassed and I am sure to grab the deploy function of the Surface and bind it to the MySurface instance for later use. Then when we override deploy later on, we can call super for the surface and not have to worry about altering core code. eventHandler is a property built into Surface, so that is used to send the render event.

An interesting test happened while making this example. If you refresh the code, and grunt pushes the changes to an unopened tab.. You event will not be fired until you open the tab again. Makes sense, but it was nice to see!

Here is what I did..

Good Luck!

var Engine            = require('famous/core/Engine');
var Surface           = require('famous/core/Surface');
var StateModifier     = require('famous/modifiers/StateModifier');
var EventHandler      = require('famous/core/EventHandler')


function MySurface(options) {
    Surface.apply(this, arguments);
    this._superDeploy = Surface.prototype.deploy
}

MySurface.prototype = Object.create(Surface.prototype);
MySurface.prototype.constructor = MySurface;

MySurface.prototype.elementType = 'div';
MySurface.prototype.elementClass = 'famous-surface';


MySurface.prototype.deploy = function deploy(target) {
  this._superDeploy(target);
  this.eventHandler.trigger('surface-has-rendered', this);
};


var context = Engine.createContext();

var event_handler = new EventHandler();

event_handler.on('surface-has-rendered', function(data){
  console.log("Hello Render!");
  console.log(data);
})

var surface = new MySurface({
  size: [200,200],
  content: "Hello",
  properties: {
    color: 'white',
    textAlign: 'center',
    lineHeight: '200px',
    backgroundColor: 'green'
  }
});

surface.pipe(event_handler);

context.add(new StateModifier({origin:[0.5,0.5]})).add(surface);
johntraver
  • 3,612
  • 18
  • 17
  • Thanks, your example is leading me to a solution. I edited the question with a side effect, do you have any idea why I am no longer able to set the placeholder? – sday Apr 27 '14 at 16:29
  • Yes, make sure you are inheriting from the InputSurface class, mine inherits only from the Surface class – johntraver Apr 27 '14 at 16:51
  • If you look at the edit to the original question I posted the code I used to test, and yes, I did inherit InputSurface. – sday Apr 27 '14 at 17:06
  • Sorry trying to answer from my phone.. You need to keep a reference to the original deploy function before you redefine deploy. You then call the original deploy in the new deploy function – johntraver Apr 27 '14 at 17:17
  • Awesome, I now understand. Thanks a bunch! I added a complete working example of this concept for InputSurface in an answer below. – sday Apr 28 '14 at 02:23
  • I just noticed that setPlaceholder isn't changing the placeholder value. Any ideas? – sday Apr 28 '14 at 02:26
  • @sday Hey.. Yea looks like for some reason the inheritance is not working correctly.. I added a call to superDeploy after the change in placeHolder because _placeholder is actually changing.. so right after control.setPlaceholder('test') call.. control._superDeploy(control._currTarget), kind of weird but there is no reason standard subclassing should not work.. and calling the new deploy function would get you into an infinite loop – johntraver Apr 28 '14 at 02:51
  • Interesting, this appears true with setValue() also. In attempting to understand this behavior, I also noticed that if I call set value from a response to an onkey event from a different Input Surface, it does set the value, but it also forces that TextBox to now have focus. – sday Apr 28 '14 at 12:26
  • It looks like we just needed to switch the order of the statements in the subclassed deploy. First we fire the event to say it is rendered, then call the _superDeploy(). – sday Apr 28 '14 at 12:37
  • A little late but while looking through the code i found out that after the deploy function is called the deploy event is emitted. https://github.com/Famous/core/blob/88f7ab3ae324790d38485e67cb31a230569f3797/Surface.js#L496 – Marc vd M May 08 '14 at 18:32
1

I know this has been answered already, all be it a few months ago. I just thought that I would add that currently you can do this without subclassing as follows:

var textbox = new InputSurface({
    size: [true,true],
    placeholder: 'Text'
});

textbox.on('deploy', function() {
    textbox.focus();
});
Tim Daley
  • 145
  • 3
  • 10