I have the below json data and the react code to populate the data dynamically
var DATA = [{"processList":
[{"processId":"1","processName":"Process1","htmlControlType":"radio","cssClassName":"radio"},
{"processId":"2","processName":"Process2","htmlControlType":"radio","cssClassName":"radio"}],
"processIndexList":
[{"processId":"1","indexId":"1","indexDesc":"First Name","htmlControlType":"textbox","cssClassName":"form-control"},{"indexId":"2","indexDesc":"Last Name","htmlControlType":"textbox","cssClassName":"form-control"}]}];
renderProcessList: function () {
const data = DATA;
return data[0].processList.map(group => {
return <div className={group.cssClassName}>
<label><input type={group.htmlControlType} name="processOptions"/>{group.processName}</label>
</div>
});
},
renderProcessData: function () {
const data = DATA;
return data[0].processIndexList.map(group => {
return <div>
<label>{group.indexDesc}</label>
<input type={group.htmlControlType} className={group.cssClassName} placeholder=""/>
<br/>
</div>
});
},
As of now the form is getting displayed based on the json data, but i want to display the form based on the user selection in the process list
ex: If the user select the Process1 radio the the First Name text box needs to be displayed below the radio and the user selects the process2 then Last Name text box needs to be displyed.
Can anyone tell me how to do it in reactjs?
To achieve the task, you have to implement the next three steps:
Handle radio input click.
Store the selected process ID in the state.
Display process list items, based on the selected ID (a.k.a. filtering).
Please note that you missed to add processId property in processIndexList[1] object.
Also please consider that in the example below I'm using basic filtering in renderProcessData().
I implemented the example in ES5, ES6 because of question's author request. Keep in mind that in the both examples I'm using JSX, so you need a compiler tool (Babel for example). Once you use a compiler, then you can use latest JS features. Please revise your choise of using ES5.
ES6
var DATA = [{
"processList": [{
"processId": "1",
"processName": "Process1",
"htmlControlType": "radio",
"cssClassName": "radio"
},
{
"processId": "2",
"processName": "Process2",
"htmlControlType": "radio",
"cssClassName": "radio"
}
],
"processIndexList": [{
"processId": "1",
"indexId": "1",
"indexDesc": "First Name",
"htmlControlType": "textbox",
"cssClassName": "form-control"
}, {
"processId": "2",
"indexId": "2",
"indexDesc": "Last Name",
"htmlControlType": "textbox",
"cssClassName": "form-control"
}]
}];
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedProcessID: null
};
}
setProcessID(e) {
this.setState({
selectedProcessID: e.target.value
});
}
renderProcessList() {
const data = DATA;
return data[0].processList.map( group => {
return <div className={group.cssClassName}>
<label><input
value={group.processId}
onClick={this.setProcessID.bind(this)}
type={group.htmlControlType}
name="processOptions"/>{group.processName}</label>
</div>
});
}
renderProcessData() {
// Display process data, only if there is already
// selected process ID
if ( ! this.state.selectedProcessID) return;
const data = DATA;
return data[0].processIndexList.map( group => {
// Display process list items for the selected process ID.
// The filtering can be implemented performance better with a library (lodash for example).
// Current implementation is enough for the SO demo.
if (group.processId !== this.state.selectedProcessID) return;
return <div>
<label>{group.indexDesc}</label>
<input type={group.htmlControlType} className={group.cssClassName} placeholder=""/>
<br/>
</div>
});
}
render() {
return <div>
{this.renderProcessList()}
{this.renderProcessData()}
</div>
}
}
ReactDOM.render(<App />, document.getElementById('container'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container"></div>
ES5
var DATA = [{
"processList": [{
"processId": "1",
"processName": "Process1",
"htmlControlType": "radio",
"cssClassName": "radio"
},
{
"processId": "2",
"processName": "Process2",
"htmlControlType": "radio",
"cssClassName": "radio"
}
],
"processIndexList": [{
"processId": "1",
"indexId": "1",
"indexDesc": "First Name",
"htmlControlType": "textbox",
"cssClassName": "form-control"
}, {
"processId": "2",
"indexId": "2",
"indexDesc": "Last Name",
"htmlControlType": "textbox",
"cssClassName": "form-control"
}]
}];
var App = React.createClass({
getInitialState() {
return {
selectedProcessID: null
}
},
setProcessID: function(e) {
this.setState({
selectedProcessID: e.target.value
});
},
renderProcessList: function() {
const data = DATA;
return data[0].processList.map( group => {
return <div className={group.cssClassName}>
<label><input
value={group.processId}
onClick={this.setProcessID.bind(this)}
type={group.htmlControlType}
name="processOptions"/>{group.processName}</label>
</div>
});
},
renderProcessData: function() {
// Display process data, only if there is already
// selected process ID
if ( ! this.state.selectedProcessID) return;
const data = DATA;
return data[0].processIndexList.map( group => {
// Display process list items for the selected process ID.
// The filtering can be implemented performance better with a library (lodash for example).
// Current implementation is enough for the SO demo.
if (group.processId !== this.state.selectedProcessID) return;
return <div>
<label>{group.indexDesc}</label>
<input type={group.htmlControlType} className={group.cssClassName} placeholder=""/>
<br/>
</div>
});
},
render: function() {
return <div>
{this.renderProcessList()}
{this.renderProcessData()}
</div>
}
});
ReactDOM.render(<App />, document.getElementById('container'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container"></div>
Related
I am quite new to React and working with JSON structures. I am trying to construct a recursive render of a JSON tree structure that dynamically renders individual HTML elements (e.g. radio buttons, dropdown menus, etc.) from the tree. I have seen other implementations, but they do not have nested HTML elements that differ from li, ul, etc. They also do not typically have different naming conventions further down the tree (e.g. attributes, options).
The tree looks like this:
{
"id": "1",
"name": "Animals",
"color": "#e37939",
"shape": "bounding_box",
"attributes": [
{
"id": "1.1",
"name": "Type",
"type": "radio",
"required": false,
"options": [
{
"id": "1.1.1",
"optionName": "Cats",
"optionValue": "cats",
"options": [.... and so on
};
What I ultimately what to achieve is to get to a format where one clicks the 'Animals button', which then renders the nested radio button, and if one selects the 'cats' option value it'd render the next dropdown menu. I have set up an initial set of methods, but I can't quite figure out how to dynamically render the next set of nested options when an option is clicked. I have created a React fiddle here: https://codesandbox.io/s/vigilant-grothendieck-jknym
The biggest challenge is to get the nested recursive options embedded in an options group. I haven't been able to figure out how to do that yet.
I have created a datastructure for what you want to achieve , altho i have tweaked it a bit as there are parts of it redundant but you can still keep both data structure and convert between them.it goes recursively as deep as you want it to go.
const prodList = [
{
id: "1",
name: "Animals",
options: [
{
id: "1.1",
name: "Type",
inputType: "radio",
options: [
{
id: "1.1.1",
name: "Cats",
value: "Cats",
inputType: "select",
options: [
{ id: "1.1.1.1", name: "Siamese Grey", value: "Siamese Grey" },
{ id: "1.1.1.2", name: "Siamese Black", value: "Siamese Black" },
{ id: "1.1.1.3", name: "Siamese Cute", value: "Siamese Cute" },
{ id: "1.1.1.4", name: "House Cat", value: "House Cat" },
{ id: "1.1.1.5", name: "House Cat", value: "House Cat" }
]
},
{ id: "1.1.2", name: "Dogs", value: "Dogs" },
{ id: "1.1.3", name: "Cows", value: "Cows" }
]
}
]
}
];
above is the data structure where you have "inputType" property that helps determining what component to show. we will have a basic component , a radio component and a select component of each type which can render each other inside them.
export default class ProductsPage extends Component {
render() {
let prodItems = prodList.map(p => {
return <MainContentManager data={p} key={p.id} />;
});
return <div>{prodItems}</div>;
}
}
class MainContentManager extends Component {
render() {
let renderObj = null;
renderObj = basicMethod(renderObj, this.props.data);
return (
<div>
<h6> {this.props.data.name}</h6>
{renderObj}
</div>
);
}
}
class RadioButtonManager extends Component {
constructor(props) {
super(props);
this.state = {
activeOptionIndex: 0
};
this.handleInputClick = this.handleInputClick.bind(this);
}
handleInputClick(index) {
this.setState({
activeOptionIndex: index
});
}
render() {
let renderObj = null;
let renderDat = null;
renderDat = this.props.data.options.map((op, index) => {
return (
<label key={op.id}>
<input
type="radio"
onChange={e => {
this.handleInputClick(index);
}}
checked={index == this.state.activeOptionIndex ? true : false}
/>
{op.name}
</label>
);
});
renderObj = basicMethod(renderObj, {
options: [this.props.data.options[this.state.activeOptionIndex]]
});
return (
<div>
<h6> {this.props.data.name}</h6>
{renderDat}
{renderObj}
</div>
);
}
}
class SelectManager extends Component {
constructor(props) {
super(props);
this.state = { value: "", activeOptionIndex: 0 };
this.handleInputClick = this.handleInputClick.bind(this);
}
handleInputClick(value) {
let activeOptionIndex = this.state.activeOptionIndex;
if (this.props.data.options) {
for (let i = 0; i < this.props.data.options.length; i++) {
if (this.props.data.options[i].value == value) {
activeOptionIndex = i;
}
}
}
this.setState({
value: value,
activeOptionIndex: activeOptionIndex
});
}
render() {
let renderObj = null;
let selectOptions = this.props.data.options.map((op, index) => {
return (
<option key={op.value} value={op.value}>
{op.name}
</option>
);
});
renderObj = basicMethod(renderObj, {
options: [this.props.data.options[this.state.activeOptionIndex]]
});
return (
<div>
<select
onChange={e => {
this.handleInputClick(e.target.value);
}}
>
{selectOptions}
</select>
{renderObj}
</div>
);
}
}
function basicMethod(renderObj, data) {
if (data && data.options) {
renderObj = data.options.map(op => {
!op && console.log(data);
let comp = null;
if (op.inputType == "radio") {
comp = <RadioButtonManager data={op} key={op.id} />;
} else if (op.inputType == "select") {
comp = <SelectManager data={op} key={op.id} />;
} else {
comp = <MainContentManager data={op} key={op.id} />;
}
return comp;
});
}
return renderObj;
}
ask anything if it is unclear or you want it a bit different.
I am tried to get the data from my file using ajax in select2.js. I want to get data according the value which I entered in my textbox and append that value in my dropdown using select2. I tried for that but it didn't give the result according my search keyword how to solve these problem.
Here is my input box on HTML:
<input type="text" id="Address1" name="Address1" >
Javascript Code
<script>
$("#Address1").select2({
tags: [],
ajax: {
url: 'ajaxhandler.php',
dataType: 'json',
type: "POST",
// quietMillis: 50,
data: function (term) {
return {
term: term
};
},
results: function (term) {
}
}
});
</script>
ajaxhandler.php
<?php
$CITIES = array("Ahmedabad", "Mumbai", "USA", "Canada", "Pune");
echo json_encode($CITIES); exit;
?>
Data format for Select2.js (version 4) is:
{
"results": [
{
"id": 1,
"text": "Option 1"
},
{
"id": 2,
"text": "Option 2"
}
]
}
See: https://select2.org/data-sources/formats
So you need to processResults received form server like below:
processResults: function (data) {
return {
results: $.map(data.items, function(obj, index) {
return { id: index, text: obj };
})
};
},
Here is a fiddle: http://jsfiddle.net/beaver71/cwb9r23b/
I'm trying the example in reactjs tutorial https://facebook.github.io/react/docs/tutorial.html to read the json from server(file). But, my "componentDidMount" is not getting called.
Below is the code:
var CommentBox = React.createClass({
loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this),
cache: false
});
},
getInitialState: function() {
return {data: {}};
},
componentDidMount: function() {
this.loadCommentsFromServer();
},
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
</div>
);
}
});
var CommentList = React.createClass({
render: function() {
var commentNodes = this.props.data.content.map(function(comment) {
return (
<Comment pageName={comment.xyz} key={comment.id}>
{comment.abc}
</Comment>
);
});
return (
<div className="commentList">
{commentNodes}
</div>
);
}
});
var Comment = React.createClass({
render: function() {
return (
<div className="comment">
<h2 className="commentpageName">
{this.props.pageName}
</h2>
<p> {this.props.children} </p>
</div>
);
}
});
ReactDOM.render(<CommentBox url="/api/comments" />, document.getElementById('content'));
Please check the way I have initialized the data(data: {})
The "componentDidMount" gets called when the json is of the following type(Just the way in the tutorial):
[ {"id": "1", "xyz": "xyz1", "abc": "abc1"},
{"id": "2", "xyz": "xyz2", "abc": "abc2"} ]
But the json format I want is:
{
content: [
{"id": "1", "xyz": "xyz1", "abc": "abc1"},
{"id": "2", "xyz": "xyz2", "abc": "abc2"},
{"id": "3", "xyz": "xyz3", "abc": "abc3"}],
"totalPages":3,
"totalElements":10,
"last":true
}
Let me know in what conditions the componentDidMount doesn't get called. Please help.
In the "Networks" console of chrome developer tools, I don't see the call made to my json file.
When I added logs in my program, I see that getInitialState() gets called first, then render, then the error "Uncaught TypeError: Cannot read property 'data' of null" is shown.
I think your componentDidMount() is not called because your render code inside <CommentList> does not work on first render, because data is empty, and this.props.data.content probably does not exist, therefore your render fails.
What you probably should do is change this:
var commentNodes = this.props.data.content.map(function(comment) {
To this:
var commentNodes = [];
if (this.props.data.content) {
commentNodes = this.props.data.content.map(function(comment) {
...
}
componentDidMount() is always called after the first render (and not in subsequent renders)
I'm having json as below
var content = [{
"DocList": [
{
"Events": [
{
"CategoryName": "PressStatements",
"DisplayName": "Press Statements",
"ID": 9
},
{
"CategoryName": "Reports",
"DisplayName": "Reports",
"ID": 10
}
],
"Heading": "News 2",
"PageID": 23,
"Date": "\/Date(1454537792000)\/"
},
{
"Events": [
{
"CategoryName": "Research",
"DisplayName": "Research",
"ID": 6
}
],
"Heading": "Heading",
"PageID": 20,
"Date": "\/Date(1437417792000)\/"
}
]}
];
$scope.data=content;
$scope.filterItems = function(g) {
//console.log('filterItems is run');
var ret = $filter('filter')(g.NewsList,"");
//g.filteredItemCount = ret.length
return ret
};
$scope.abms = [];
//Options Inside Select tag
angular.forEach(news[0].NewsList, function(newsItems, index) {
angular.forEach(newsItems.CategoryList, function(category, index){
$scope.abms.push(category.DisplayName);
});
});
i'm displaying Events in select option depending upon that i have to display the data, for selection i'm using select tag with ng-model
<select ng-model="selected"><option ng-repeat="item1 in abms" >{{item1}}</option></select>
<div id="dataRow{{$index}}" ng-repeat="item1 in (filtered = filterItems(d)) | findobj:selected">
//display data depending on option selection
</div>
If i select any categoryName from Events[0] then it should display data from DocList[0] data and same to other for select option.
//filter - dont knw this the way to do it
myapp.filter('findobj', function() {
return function(dataobj, selected) {
return dataobj.filter(function(news) {
//alert('got something');
return (selected || []).some(function(s) {
alert('got something');
return news.CategoryList.CategoryName === s.CategoryList.CategoryName;
});
});
};
});
How to achieve that?
Thanks in Advance...
you need fix your findobj filter something like this
myapp.filter('findobj', function () {
return function (dataobj, selected) {
if (!selected) return dataobj;//if nothing selected - return initial array
return dataobj.filter(function (news) {
//select only news where in category list places selected category
return news.CategoryList.some(function (category) {
//check display category name
return category.DisplayName === selected;
});
});
};
});
working sample on JSFIDDLE
I trying to render all objects names from my json file /api/tables.json into ? <li>there</li>'s
var Table = Backbone.Model.extend({
defaults: {
name: 'table',
id: 1
}
});
var Tables = Backbone.Collection.extend({
model: Table,
url: 'api/table.json'
});
var TablesView = Backbone.View.extend({
el: '#mydiv',
template: _.template($("#table-template").html()),
initialize : function() {
this.coll = new Tables()
this.listenTo(this.coll, 'reset', this.render);
this.coll.fetch();
},
render : function() {
this.$el.html(this.template({ table: this.coll.toJSON() }));
return this;
}
});
This is my template in index.html :
<div id="mydiv"></div>
<script type="text/template" id="table-template">
<ul>
<% _.each(table, function(table) { %>
<li><%= table.name %></li>
<% }); %>
</ul>
</script>
data from json file:
[
{
"name": "Table 1",
"id": 1
},
{
"name": "Table 2",
"id": 2
},
{
"name": "Table 3",
"id": 3
},
{
"name": "Table 4",
"id": 4
}
]
Please help me.... I don't know where is fault or what is missing.
I highly recommend using Backbone.Marionette plugin, which supports rendering of lists out of the box. You don't have to write boilerplate code for it. Just use CollectionView or CompositeView with the collection given as constructor arguments and define an ItemView for them (li element)