In Facebook react.js, you can compose component within component, or maybe mix and match.
I'm wondering if twitter flight can do the same thing. if so, can anyone gives me an example?
this is what I have so far:
define(function (require) {
var defineComponent = require('flight/lib/component'),
infoInput = require('component/info_input');
return defineComponent(inputSection, infoInput);
function inputSection () {
this.after('initialize', function() {
infoInput.doSomehting();
});
};
});
and my info_input.js is defined below:
define(function (require) {
var defineComponent = require('flight/lib/component');
return defineComponent(infoInput);
function infoInput() {
this.after('initialize', function() {
});
this.doSomething = function() {
alert('I will do something');
};
};
});
This is what mixins are for.
Flight Components are enriched mixins.
From doc/component_api.md
It comes with a set of basic functionality such as event handling and Component registration. Each Component definition mixes in a set of custom properties which describe its behavior.
Read more about Components.
So the answer to your question is Yes.
I guess that what you are doing is legit, although I've never done it before.
I'd rather move the shared logic to a Mixin or attach the two components to the same element and let them talk via events:
component/input_section.js
this.after('initialize', function () {
this.trigger('uiSomethingRequired');
});
component/info_input.js
this.after('initialize', function () {
this.on('uiSomethingRequired', this.doSomething);
});
Solution mentioned by G.G above works!
We may go a step ahead to trigger events on restricted scope instead of document:
component/input_section.js
this.after('initialize', function () {
this.$node.closest(this.attr.parentClass).trigger('uiSomethingRequired');
});
component/info_input.js
this.after('initialize', function () {
this.on(this.$node.closest(this.attr.parentClass), 'uiSomethingRequired', this.doSomething);
});
Related
A component I'm making needs the default camera, but this.el.sceneEl.camera returns undefined.
Simplified example:
AFRAME.registerComponent('test', {
init: function () {
console.log(this.el.sceneEl.camera)
}
});
How can I retrieve the camera?
Might have to wait for the camera to be set? Need to document this, but there's an event:
this.el.sceneEl.addEventListener('camera-set-active', function (evt) {
console.log(evt.detail.cameraEl);
});
When is it safe to call the find(...) method on a ractive instance and be guaranteed that the template has been rendered and the DOM elements are available?
Background:
I'm new to RactiveJS, building my first application with it. I've been instantiating Ractive instances and then calling methods like find() on those instances to access elements rendered from my templates. Something like this:
var ractive = new Ractive({ el: ..., template: ..., data: ..., etc });
var element = ractive.find('.some-template-element');
This has been working fine so far, but I'm wondering whether I might have a race condition here because of the fact that ractive seems to render templates asynchronously. Is it safe to write code like the above or do I need to instead move everything into callbacks like this?
ractive.on('complete', function() {
var element = ractive.find('.some-template-element');
});
Too Much Information:
In practice, of course, what I'm doing is more complicated than this simple pseudo-code. I'm creating 'widgets' that use Ractive as an internal implementation detail. Right now, I create those widgets and then start calling methods on them. But if the Ractive methods aren't ready to call right away, I'll need to restructure my widgets to expose callbacks/promises that get called once I know my ractives have been created and are ready for use.
I haven't been able to find details in the RactiveJS documentation that explain when it's safe to call the various functions on Ractive, but I'm hoping I've just missed something.
Assuming you've provided an el option, rendering happens synchronously with the new Ractive instantiation.
While you can subscribe via ractive.on( 'event', ... ), it is often handy and cognitively easier to use the onevent options:
var log = [];
var r = new Ractive({
el: document.body,
template: '#template',
data: {
log: log
},
components: {
'child-component': Ractive.extend({
template: '<span>child</span>',
oninit: function() {
log.push('child component init');
},
onrender: function() {
var span = this.find('span');
log.push('child component render find span:' + !!span);
},
oncomplete: function() {
log.push('child component complete');
}
})
},
oninit: function() {
log.push('parent view init');
},
onrender: function() {
var div = this.find('div'),
span = this.find('span');
log.push('parent component render, find div: ' + !!div + ' find span: ' + !!span);
},
oncomplete: function() {
log.push('parent component complete');
}
});
// these won't fire becasue they already happened!
r.on('init', function() {
log.push('view on("init"...)');
});
r.on('render', function() {
log.push('view on("render"...)');
});
// this will fire because complete is always async.
r.on('complete', function() {
log.push('view on("complete"...)');
});
<script src='//cdn.jsdelivr.net/ractive/0.7.3/ractive-legacy.min.js'></script>
<script src='//cdn.jsdelivr.net/ractive.transitions-fade/0.2.1/ractive-transitions-fade.min.js'></script>
<script id='template' type='text/ractive'>
<div intro='fade'>main view</div>
<child-component/>{{#log}}
<li>{{.}} {{/}}
</script>
This is a bit puzzling to me. I set data in the router (which I'm using very simply intentionally at this stage of my project), as follows :
Router.route('/groups/:_id',function() {
this.render('groupPage', {
data : function() {
return Groups.findOne({_id : this.params._id});
}
}, { sort : {time: -1} } );
});
The data you would expect, is now available in the template helpers, but if I have a look at 'this' in the rendered function its null
Template.groupPage.rendered = function() {
console.log(this);
};
I'd love to understand why (presuming its an expected result), or If its something I'm doing / not doing that causes this?
From my experience, this isn't uncommon. Below is how I handle it in my routes.
From what I understand, the template gets rendered client-side while the client is subscribing, so the null is actually what data is available.
Once the client recieves data from the subscription (server), it is added to the collection which causes the template to re-render.
Below is the pattern I use for routes. Notice the if(!this.ready()) return;
which handles the no data situation.
Router.route('landing', {
path: '/b/:b/:brandId/:template',
onAfterAction: function() {
if (this.title) document.title = this.title;
},
data: function() {
if(!this.ready()) return;
var brand = Brands.findOne(this.params.brandId);
if (!brand) return false;
this.title = brand.title;
return brand;
},
waitOn: function() {
return [
Meteor.subscribe('landingPageByBrandId', this.params.brandId),
Meteor.subscribe('myProfile'), // For verification
];
},
});
Issue
I was experiencing this myself today. I believe that there is a race condition between the Template.rendered callback and the iron router data function. I have since raised a question as an IronRouter issue on github to deal with the core issue.
In the meantime, workarounds:
Option 1: Wrap your code in a window.setTimeout()
Template.groupPage.rendered = function() {
var data_context = this.data;
window.setTimeout(function() {
console.log(data_context);
}, 100);
};
Option 2: Wrap your code in a this.autorun()
Template.groupPage.rendered = function() {
var data_context = this.data;
this.autorun(function() {
console.log(data_context);
});
};
Note: in this option, the function will run every time that the template's data context changes! The autorun will be destroyed along with the template though, unlike Tracker.autorun calls.
I have a strategy question.
I want to change data in my website using signalR and display changed data using react. My question would be: How to perform data binding between signalR and react?
My first clue is the following:
signalR:
chat.client.addMessage = function (name, message) {
chatHistory.push({ Author: name, Text: message }); //here I change global variable chatHistory
};
react:
var CommentList = React.createClass({some class here});
var CommentBox = React.createClass({
componentRefresh: function () {
this.setState({ data: chatHistory });
},
getInitialState: function () {
return { data: chatHistory };
},
componentDidMount: function () {
this.componentRefresh();
setInterval(this.componentRefresh, this.props.interval);
},
render: function () {
return (
React.DOM.div(null,
CommentList({ data: this.state.data })
)
);
}
});
React.renderComponent(
CommentBox({ interval: 2000 }),
document.getElementById('content')
);
in react commentBox component I feed global chatHistory and ask for a new value every 2 seconds.
Is there more elegant way of doing it?
and how to avoid redrawing of CommentBox if chatHistory variable wasn't changed?
Your approach of maintaining state in CommentBox is fine. As your component base grows, it might become complicated to maintain self-updating components though. I recommend investigating the Flux architecture the React team designed and their Todo MVC Flux example in particular.
You could implement shouldComponentUpdate to prevent React from re-rendering the CommentBox if you know state hasn't changed. Also, you should keep a reference to the interval so you can clear it when the CommentBox is unmounted otherwise it will go on polling after the component is removed.
var CommentBox = React.createClass({
...
componentDidMount: function() {
this.componentRefresh();
this._interval = setInterval(this.componentRefresh, this.props.interval);
},
componentWillUnmount: function() {
clearInterval(this._interval);
this._interval = null;
},
shouldComponentUpdate: function(nextProps, nextState) {
// Do a deep comparison of `chatHistory`. For example, use
// Underscore's `isEqual` function.
return !_.isEqual(this.state.chatHistory, nextState.chatHistory);
},
...
});
I don't know why but this code is not working ? Why would it not ? I guess it is because scope problem I am having here :
function washAway(obj) {
alert($(obj)); // says HTML Object which is fine
setTimeout(function() {
alert($(obj)); // says undefined
$(obj).fadeOut("slow", function() {
$(this).remove();
});
}, 2000);
};
At the point where the function in the timeout executes, it has no way to know what obj is - it was a parameter passed into the method where the timeout was set up, but the function inside has no reference to it.
An alternative approach is to write a jQuery plugin to wait before it acts like this:
function($){ //to protect $ from noConflict()
$.fn.pause = function(duration) {
$(this).animate({ dummy: 1 }, duration);
return this;
};
}
Then you can use:
$(obj).pause(2000).fadeOut("slow", function() { $(this).remove(); });
Any ways, I've found my answer after a couple of try/wrong. But I am still wondering why it didn't work out.
Here is the code :
function washAway(obj) {
alert($(obj).attr("id"));
var a = function() {
var _obj = obj;
return function() {
$(_obj).fadeOut("slow", function() {
$(this).remove();
});
};
};
setTimeout(a(), 2000);
};
Edit : I think I understood the problem here :
Because we are talking about closures here, when washAway execution finishes, the variable obj is destroyed so setTimeOut function callback function can't use that resource because it is no more available on the stack and it is not also a global variable.