How to Render Component Dynamically in React - reactjs

I have developed a Component base application in which I have developed a different Component but at time of rendering I have to render based on user action currently what I have tried is mentioned below.
My Component looks like:
var React = require('react');
var Constants = require('../constants/ActionTypes');
var ItemRelationStore = require('../stores/ItemRelationStore');
var ItemStore=require('../stores/ItemStore');
var TreeView=require('./TreeView.react');
var childNodes={};
function getItemRelationState() {
return {
Items:JSON.parse(JSON.stringify(ItemStore.getItem())),
Items1:JSON.parse(JSON.stringify(ItemStore.getItem1()))
};
}
// Define main Controller View
var ItemPage = React.createClass({
// Get initial state from stores
getInitialState: function() {
return getItemRelationState();
},
// Add change listeners to stores
componentDidMount: function() {
ItemStore.CallItem("TenantId",this.props.MetaItemId,this.props.Key);
ItemStore.addChangeListener(this._onChange);
},
// Remove change listers from stores
componentWillUnmount: function() {
ItemStore.removeChangeListener(this._onChange);
},
// Render our child components, passing state via props
render: function() {
var itemslist;
if(this.props.Key==1)
{
itemslist=this.state.Items;
}
else
{
itemslist=this.state.Items1;
}
var metaid=this.props.MetaItemId;
childNodes= Object.keys(itemslist).map(function(item,index){
return (
{
title:itemslist[item].Name,
Id: itemslist[item].Id,
metaitemId:metaid
}
)
});
var tree= {
title: this.props.MetaItemName,
metaitemId:metaid,
childNodes: [
{childNodes}
]
};
return (
<div className="treeview">
<TreeView node={tree}/>
</div>
);
},
// Method to setState based upon Store changes
_onChange: function() {
this.setState(getItemRelationState());
}
});
module.exports = ItemPage;
Now when I used static in another component like
var Item=require('../../Item');
React.render(<Item MetaItemName={"Users"} MetaItemId={1} Key={1}/>,document.getElementById("firstCol"));
then it's working fine. but I want to render dynamically so I have prepared one CustomeComponent like
var React = require('react');
var Item=require('../Item');
var CustomComponent = React.createClass({
// Get initial state from stores
getInitialState: function() {
return null;
},
// Add change listeners to stores
componentDidMount: function() {
},
componentWillReceiveProps: function(nextProps) {
// Load new data when the dataSource property changes.
if (nextProps.dataSource != this.props.dataSource) {
this.loadData(nextProps.dataSource);
}
},
// Remove change listers from stores
componentWillUnmount: function() {
},
// Render our child components, passing state via props
render: function() {
var InputType=this.props.inputType;
console.log(InputType);
return (
<InputType/>
);
},
// Method to setState based upon Store changes
_onChange: function() {
}
});
module.exports = CustomComponent;
and I am calling this custom component like
React.render(<CustomComponent inputType="Item" />,document.getElementById("secondCol"));
but it's not working i mean to say nothing render on Page.
I just want to know how we can Achive dynamic rendering in React.
Thanks in Advance.

Maybe React.render(<CustomComponent inputType={Item} />,document.getElementById("secondCol"));?

Related

Fullcalendar Custom Views via JS classes destroy throws errors

I have implemented Fullcalendar Custom View by referring document Custom Views via JS classes. It seems to worki fine, but the destroy method throws an error: View.prototype.destroy.apply is not function.
I have used the below code:
var FC = $.fullCalendar;
var View = FC.View;
var BlankView;
BlankView = View.extend({
initialize: function() {
// View.prototype.initialize.apply(this, arguments);
},
setHeight: function(height, isAuto) {
},
render: function () {
this.title = this.title || 'TestView';
if (this.el[0].children.length === 0) this.el.addClass('fc-basic-view').html(this.renderHtml());
},
destroyEvents: function() {
// responsible for undoing everything in renderEvents
},
destroy: function() {
View.prototype.destroy.apply(this, arguments);
},
renderHtml: function () {
this.el.html('<div></div>');
},
});
FC.views.blank = BlankView;
Any clues to achieve the destroy method in custom views?

React js attach click event on every single span element

I have the following react code at: http://codepen.io/AlexanderWeb00/pen/ZOWyNr
I am trying to attach for every span a click event that allows to console.log the correspondent number that you have clicked on:
var one = [],
two = [];
var SingleButton = React.createClass({
getInitialState: function() {
return {
val1: one,
val2: two,
action: ''
}
},
handleClick: function() {
var val1 = this.state.val1,
val2 = this.state.val2;
if(!$('.action').length){
val1.push(this.props.numbers);
console.log(val1[0].join(" "));
}
},
render: function() {
var numbers = [];
var classes = 's-btn large-4 columns';
var ff =this.handleClick;
this.props.numbers.forEach(function(el){
numbers.push(<span onClick={ff} className={classes}>{el}</span>);
});
return (
<div className="row">
{numbers}
</div>
);
}
});
what I am getting instead is 123456789 as opposed to 5 if I only click on number 5.
As stated in the comment, you are pushing the whole numbers arrays into val1, therefore it will always display the whole list. To solve it we need to pinpoint the clicked span, get its number, and add it to the list.
The first method would be using jQuery, and the eventArg that the onClick sends.
Once the span is clicked our handleClick is called, with the click event data as a parameter. We will extract the clicked span from the event arg, ie eventArg.target, and get its text using jQuery (ie $(eventArg.target).text()).
To specifically solve the issue you could:
var one = [],
two = [];
var SingleButton = React.createClass({
getInitialState: function() {
return {
val1: one,
val2: two,
action: ''
}
},
handleClick: function(e) {
var val1 = this.state.val1,
val2 = this.state.val2;
if(!$('.action').length){
val1.push($(e.target).text());
console.log(val1.join(" "));
}
},
render: function() {
var numbers = [];
var classes = 's-btn large-4 columns';
var ff =this.handleClick;
this.props.numbers.forEach(function(el){
numbers.push(<span onClick={ff} className={classes}>{el}</span>);
});
return (
<div className="row">
{numbers}
</div>
);
}
});
https://jsfiddle.net/omerts/zm0bzmwp/
Another option is to wrap the span's number in a colsure. In other words, create an anonymous function, which will have the number as part of its scope object. Since closures are out of the scope of this question, for further reading: Closures
var SingleButton = React.createClass({
getInitialState: function() {
return {
val1: [],
val2: [],
action: ''
}
},
handleClick: function(number) {
if(!$('.action').length){
const updatedList = this.state.val1.slice(); // clone the array, and add the new number
updatedList.push(number);
console.log(updatedList.join(" "));
this.setState({val1: updatedList});
}
},
render: function() {
var numbers = [];
var classes = 's-btn large-4 columns';
this.props.numbers.forEach(function(el){
numbers.push(<span onClick={() => {this.handleClick(el)}} className={classes}>{el}</span>);
}, this);
return (
<div className="row">
{numbers}
</div>
);
}
});
https://jsfiddle.net/omerts/590my903/
Second option is better, since you should not be mutating state, unless you are using this.setState. If you don't really need it as part of your state, just make val1, and val2 data memebers of your SingleButton.

How can a Router talk to a View other than using a global variable?

I can not understand why in my Backbone app (Todo app) after I reload a page (CTRL+F5) a filterTodos method does not get called. When I simply click on links to filter Todos ("Active", "Completed") - it does get called.
You can see this feature in links below. No matter how many times you click Refresh in Browser - correct filtered results are displayed:
http://todomvc.com/architecture-examples/backbone/#/completed
http://todomvc.com/architecture-examples/backbone/#/active
I have a theory that it's because I am triggering a filter event from Router too early - a TodosView is not initialized yet and therefore it does not listenTo filter event yet.
But how a Router can inform a View to re-render itself (based on filter) if this View does not exist yet? Can't it be achieved via triggering some event in Router as I do? One possible option is to have a global variable app.FilterState.
Is there any other methods of communications between a Router and a non-constructed yet View?
For app.FilterState I will set its state in Router and then check it in View and call filterTodos function manually like so and it will work:
views/todos.js
render: function() {
app.Todos.each(function(todo) {
this.renderTodo(todo);
}, this);
if (app.FilterState !== 'all') { // <--- ADDED CODE
this.filterTodos(app.FilterState);
}
return this;
}
Existing source code:
routers/router.js
var app = app || {};
var Router = Backbone.Router.extend({
routes: {
'all': 'all',
'active': 'active',
'completed': 'completed'
},
all: function() {
console.log('all');
app.Todos.trigger('filter', 'all');
},
active: function() {
console.log('active');
app.Todos.trigger('filter', 'active');
},
completed: function() {
console.log('completed');
app.Todos.trigger('filter', 'completed');
}
});
app.Router = new Router();
Backbone.history.start();
views/todos.js
var app = app || {};
app.TodosView = Backbone.View.extend({
el: '#todo-list',
initialize: function(todos) {
console.log('initialize begin');
app.Todos.reset(todos);
this.listenTo(app.Todos, 'add', this.addOneTodo);
this.listenTo(app.Todos, 'filter', this.filterTodos);
this.render();
console.log('initialize end');
},
render: function() {
app.Todos.each(function(todo) {
this.renderTodo(todo);
}, this);
return this;
},
renderTodo: function(todo) {
var todoView = new app.TodoView({model: todo});
this.$el.append(todoView.render().el);
},
addOneTodo: function(todo) {
this.renderTodo(todo);
},
filterTodos: function(filterType) {
console.log('filter'); // <--- CODE DOES NOT REACH THIS LINE WHEN CALLED ON BROWSER'S REFRESH (F5)
var active = app.Todos.active();
var completed = app.Todos.completed();
if (filterType === 'active') {
// hide remaining
_.each(completed, function(todo) {
todo.trigger('hide');
});
//show active
_.each(active, function(todo) {
todo.trigger('show');
});
}
else if (filterType === 'completed') {
_.each(completed, function(todo) {
todo.trigger('show');
});
//show active
_.each(active, function(todo) {
todo.trigger('hide');
});
}
else if (filterType === 'all') {
app.Todos.each(function(todo) {
todo.trigger('show');
});
}
}
});
Have you considered using Backbone Marionette? It comes with a built in pub sub communication system built in that makes it super easy to do this. Overall it gives you a great organization/modularization of your code by utilizing the pub sub system.

Backbone inheritance, merging the render() function

So I have this current situation:
app.Ui.ModalView = Backbone.View.extend({
events: {
},
initialize: function() {
},
render: function() {
var that = this;
var model = this.model.toJSON();
that.$el.html(that.template(_.extend(this.params || {}, {
model: model,
})));
return this;
}
});
and then the inherited view:
app.Views.childView = kf.Ui.ModalView.extend({
template: JST["templates/app/blah/blah-edit.html"],
events: {
},
initialize: function() {
var that = this;
this.events = _.extend({}, app.Ui.ModalView.prototype.events, this.events);
app.Ui.ModalView.prototype.initialize.apply(this, arguments);
},
render: function(){
// add extra logic in this render function, to run as well as the inherited render function?
}
});
So, I don't want to override the parent's render(), but to add extra functionality to it, how would I go about doing that?
Two ways to achieve this: Either you can add explicit support for overriding the behaviour by creating a "render hook" in the base class, or you'll have to call the overridden base method from the superclass method:
Render hook in base class:
app.Ui.ModalView = Backbone.View.extend({
render: function() {
//if this instance (superclass) defines an `onRender` method, call it
if(this.onRender) this.onRender();
//...other view code
}
}
app.Views.childView = kf.Ui.ModalView.extend({
onRender: function() {
//your custom code here
}
});
Call base class method from super class:
app.Views.childView = kf.Ui.ModalView.extend({
render: function() {
//your custom code here
//call the base class `render` method
kf.Ui.ModalView.prototype.render.apply(this, arguments);
}
});

Backbone view not rendering correctly on subsequent calls to route handler

I'm having a problem with rendering a backbone.js view successfully from a route handler (browser application).
My javascript module is currently setup like this:
$(function () { // DOM ready
myModule.Init();
});
var myModule = (function () {
// Models
var DonorCorpModel = Backbone.Model.extend({ });
// Collections
var DonorCorpsCollection = Backbone.Collection.extend({ model : DonorCorpModel });
// Views
var DonorCorpsListView = Backbone.View.extend({
initialize : function () {
_.bindAll(this, 'render');
this.template = Handlebars.compile($('#pre-sort-actions-template').html())
this.collection.bind('reset', this.render);
},
render : function () {
$(this.el).html(this.template({}));
this.collection.each(function (donorCorp) {
var donorCorpBinView = new DonorCorpBinView({
model : donorCorp,
list : this.collection
});
this.$('.donor-corp-bins').append(donorCorpBinView.render().el);
});
return this;
}
});
var DonorCorpBinView = Backbone.View.extend({
tagName : 'li',
className : 'donor-corp-bin',
initialize : function () {
_.bindAll(this, 'render');
this.model.bind('change', this.render);
this.template = Handlebars.compile($('#pre-sort-actions-donor-corp-bin-view-template').html());
},
render : function () {
var content = this.template(this.model.toJSON());
$(this.el).html(content);
return this;
}
})
// Routing
var App = Backbone.Router.extend({
routes : {
'' : 'home',
'pre-sort' : 'preSort'
},
initialize : function () {
// ...
},
home : function () {
// ...
},
preSort : function () {
if (donorCorps.length < 1) {
donorCorps.url = 'http://my/api/donor-corps';
donorCorps.fetch();
}
var donorCorpsList = new DonorCorpsListView({ collection : donorCorps }).render().el;
$('#document-action-panel').empty().append(donorCorpsList);
// ...
}
});
// Private members
var app;
var donorCorps = new DonorCorpsCollection();
// Public operations
return {
Init: function () { return init(); }
};
// Private operations
function init () {
app = new App();
Backbone.history.start({ root: '/myApp/', pushState: true });
docr.navigate('/', { trigger: true, replace: true});
}
}(myModule || {}));
Everything runs just fine when I run the app...it navigates to the home view as expected. I have links setup with handlers to navigate to the different routes appropriately, and when I run app.navigate('pre-sort', { trigger: true, replace: true}) it runs just fine, and renders the expected items in the list as expected.
The problem comes when I try to go back to the home view, and then back to the pre-sort view again...the items in the list don't get displayed on the screen as I am expecting them to. I'm just getting the empty pre-sort-actions-template rendered with no child views appended to it. I've stepped through the code, and can see that the collection is populated and the items are there...but for some reason, my code isn't rendering them to the view properly, and I can't seem to figure out why or what I'm doing wrong. Any ideas?? I'm pretty new to backbone, so I'm sure this code isn't written totally right...constructive feedback is welcome. Thanks.
how is the code rendering it to the view after going back home, then to pre-sort again? could you provide some details on that? duplicate items? empty view?
Also, I like to keep an index of my sub-views when I render them so the parent view can always access them regardless of scope. I found a really nice technique for this here: Backbone.js Tips : Lessons from the trenches
A quick overview of the pattern I'm referring to is as follows:
var ParentView = Backbone.View.extend({
initialize: function () {
this._viewPointers = {};
},
render: function (item) {
this._viewPointers[item.cid] = new SubView ({
model: item
});
var template = $(this._viewPointers[item.cid].render().el);
this.$el.append(template);
}
});
var SubView = Backbone.View.extend({
initialize: function () {
this.model.on("change", this.render, this);
},
render: function () {
this.$el.html( _.template($('#templateId').html(), this.model.attributes) );
return this;
}
});
I realize this answer is rather "broad," but it will be easier to answer with more specifics if I can understand the exact issue with the rendering. Hope its of some help regardless :)

Resources