Unable to Append new Child Components in react - reactjs

I am new to react.
I want to add another SingleButtonComponent when a SingleButtonComponent is clicked.
I am able to increment the number of children using a state variable numChildren.
AddChild: function() {
var numChildren = (this.state.numChildren) +1;
this.setState({numChildren :numChildren})
},
But I am getting error whenever I am trying to loop inside
render: function () {
return (
for (var i = 0; i < this.state.numChildren; i += 1) {
<SingleButtonComponent AddChild={this.AddChild}/>
};
)
}
. Here is the PLUNKER to it
I have temporarily kept the for loop out of render..
Please suggest me a way to add a child-component every time it is clicked.
Thanks

Your render function doesn't return anything.
If you're using React 16, try:
render: function() {
const arr = [];
for (var i = 0; i < this.state.numChildren; i += 1) {
arr.push(<SingleButtonComponent key={i} AddChild={this.AddChild}/>);
}
return arr;
}
Otherwise, you must return a single element:
render: function() {
const arr = [];
for (var i = 0; i < this.state.numChildren; i += 1) {
arr.push(<SingleButtonComponent key={i} AddChild={this.AddChild}/>);
}
return (<div>{arr}</div>);
}
Regarding your second question:
React.createClass is deprecated in favor of ES6 classes. Your component should be defined as follows:
class MainBody extends React.Component {
constructor() {
this.state = { numChildren: 0 };
}
AddChild() { ... }
render() { ... }
}

Related

Vue: Array is only updated on console, not on screen

I'm still learning the basics of vue. I have different elements in one component, that have to be assigned in a second component. When I use console.log the array is shown correctly, but when i want to show the array in dynamically in template it is still the default value. What am I doing wrong?
Component one:
<template>
<div>
{{nr}}
{{containertyp}}
<button #click="click(0)"></button>
</div>
</template>
I have many more buttons with different parameters, just to make it shorter here.
export default: {
data: function {
return {
nr: [],
containertyp: [],
}
}
methods: {
click(number) {
for (var i = 0; i < 27; i++) {
this.nr[i] = false;
if (number == i) {
this.nr[i] = true;
}
};
EventBus.$emit('containerclicked');
}
},
created: function() {
let i;
//default
for (i = 0; i < 27; i++) {
this.nr[i] = false;
}
for (var index = 0; index < 27; index++) {
this.containertyp[index] = 'bs';
}
},
mounted() {
const self = this
EventBus.$on('containertypchosen', function (containertyp) {
for (let j = 0; j < 27; j++) {
if (self.nr[j] == true) {
self.containertyp[j] = containertyp
}
}
})
Component two:
<template>
<div>
<button :disabled = "disabled == true" v-on:click="chosetype('bs')" "> bs </button> <br />
</div>
</template>
export default: {
data: function() {
return {
disabled: true
}
}
mounted () {
const self = this
EventBus.$on('containerclicked', function (){
self.disabled = false
})
},
methods: {
chosetype: function (containertyp) {
this.containertyp = containertyp
EventBus.$emit('containertypchosen', containertyp)
}
}
}
You can't update arrays using indexes, the changes won't be detected by the reactivity system.
https://v2.vuejs.org/v2/guide/list.html#Caveats
So, for example, this won't work:
this.nr[i] = true;
Instead you'd need to use Vue.set, or the alias vm.$set:
this.$set(this.nr, i, true);
An alternative would be to create a new array and then replace this.nr entirely, i.e. this.nr = newArray.
You'll need to make a similar change everywhere that you're updating an array by index. There are updates to both nr and containertyp that currently have this problem.
It's not immediately clear from your code whether nr even needs to be an array. It seems that all the values are false apart from one, so you might be better off just holding the index of the true value instead of using an array.

How do I create an ag-Grid cell editor using React and TypeScript?

I see that the ag-grid-react repo has types, and I also see that the ag-grid-react-example repo has examples. But how do I put the two together and create a cell editor with React and Types?
I'm guessing it's something like this but I can't make TypeScript happy:
class MyCellEditor implements ICellEditorReactComp {
public getValue() {
// return something
}
public render() {
const { value } = this.props
// return something rendering value
}
}
I implemented ICellEditor and used ICellEditorParams for prop definitions. For example, this MyCellEditor example from their documentation:
// function to act as a class
function MyCellEditor () {}
// gets called once before the renderer is used
MyCellEditor.prototype.init = function(params) {
// create the cell
this.eInput = document.createElement('input');
this.eInput.value = params.value;
};
// gets called once when grid ready to insert the element
MyCellEditor.prototype.getGui = function() {
return this.eInput;
};
// focus and select can be done after the gui is attached
MyCellEditor.prototype.afterGuiAttached = function() {
this.eInput.focus();
this.eInput.select();
};
// returns the new value after editing
MyCellEditor.prototype.getValue = function() {
return this.eInput.value;
};
// any cleanup we need to be done here
MyCellEditor.prototype.destroy = function() {
// but this example is simple, no cleanup, we could
// even leave this method out as it's optional
};
// if true, then this editor will appear in a popup
MyCellEditor.prototype.isPopup = function() {
// and we could leave this method out also, false is the default
return false;
};
became this:
class MyCellEditor extends Component<ICellEditorParams,MyCellEditorState> implements ICellEditor {
constructor(props: ICellEditorParams) {
super(props);
this.state = {
value: this.props.eGridCell.innerText
};
}
// returns the new value after editing
getValue() {
// Ag-Grid will display this array as a string, with elements separated by commas, by default
return this.state.value;
};
// Not sure how to do afterGuiAttached()
// if true, then this editor will appear in a popup
isPopup() {
return true;
};
render() {
return (
<div>
hello
</div>
);
}
}

Typescript: How to access and update class variable from public function

I am trying to access value of a class variable in function setRating() but the console print is "undefined".
export class UserFeedbackComponent implements OnInit {
rating: number;
constructor() {
this.rating = 3;
}
ngOnInit() {
//initial setup
console.log("Rating " + this.rating);
document.addEventListener('DOMContentLoaded', function() {
let stars = document.querySelectorAll('.star');
stars.forEach(function(star) {
star.addEventListener('click', setRating);
});
let temps = parseInt(document.querySelector('.stars').getAttribute('data-rating'));
console.log("Rating 2: " + this.rating);
let target = stars[temps - 1];
target.dispatchEvent(new MouseEvent('click'));
});
}
function setRating(ev) {
//Printing 'undefined' in console.log
console.log('At top: ' + this.rating);
let span = ev.currentTarget;
let stars = document.querySelectorAll('.star');
let match = false;
let num = 0;
stars.forEach(function(star, index) {
if (match) {
star.classList.remove('rated');
} else {
star.classList.add('rated');
}
//are we currently looking at the span that was clicked
if (star === span) {
match = true;
num = index + 1;
}
});
this.rating = num;
console.log("value after update: " + this.rating)
document.querySelector('.stars').setAttribute('data-rating', num.toString());
}
}
the "value after update: " console log prints "undefined" unless this.rating is assigned to num. Can someone please help me with how to access the value of rating variable in setRating() function and how to update its value?
It's a context binding issue, you have to bind the setRating function to the class this otherwise it is going to use its own this which is different than the classes this no having access to this.rating. You can achieve this by using setRating.bind(this).
You can start by changing the DOMContentLoaded to an arrow function so that you inherit the context's this like so:
document.addEventListener('DOMContentLoaded', () => {
// this.rating is visible here now
...
})
Then you can do the same to the forEach handler:
stars.forEach((star) => {
// this.rating is visible here now too
...
});
Finally, you can bind the this of your external function to the classes this:
star.addEventListener('click', setRating.bind(this));
Your final code would be something like bellow:
export class UserFeedbackComponent implements OnInit {
rating: number;
constructor() {
this.rating = 3;
}
ngOnInit() {
//initial setup
console.log("Rating " + this.rating);
document.addEventListener('DOMContentLoaded', () => {
let stars = document.querySelectorAll('.star');
stars.forEach((star) => {
star.addEventListener('click', setRating.bind(this));
});
let temps = parseInt(document.querySelector('.stars').getAttribute('data-rating'));
console.log("Rating 2: " + this.rating);
let target = stars[temps - 1];
target.dispatchEvent(new MouseEvent('click'));
});
}
function setRating(ev) {
//Printing 'undefined' in console.log
console.log('At top: ' + this.rating);
let span = ev.currentTarget;
let stars = document.querySelectorAll('.star');
let match = false;
let num = 0;
stars.forEach(function(star, index) {
if (match) {
star.classList.remove('rated');
} else {
star.classList.add('rated');
}
//are we currently looking at the span that was clicked
if (star === span) {
match = true;
num = index + 1;
}
});
this.rating = num;
console.log("value after update: " + this.rating)
document.querySelector('.stars').setAttribute('data-rating', num.toString());
}
}
Further observation: You are declaring a function inside a class, that is totally unnecessary, you can declare it as a member
export class UserFeedbackComponent implements OnInit {
...
setRating(ev) {
...
}
}
Then you don't even ave to bind it to call it like so:
star.addEventListener('click', ev => this.setRating(ev));
You do not have to define the function using the function keyword in the class. Declare it as a class member and you should be able to access it normally. This is the best practice in Typescript class declaration.
EX:
export class UserFeedbackComponent implements OnInit {
rating: number;
constructor() {
this.rating = 3;
}
ngOnInit() {
//initial setup
console.log("Rating " + this.rating);
......................more code............
}
setRating(ev) {
//Printing 'undefined' in console.log
console.log(this.rating); //should work fine
// do other stuff with class members
}
}

Find object by id in an array using angular 2 and typescript

Hello guys i am using asp.net mvc 5.
When i try to find object by id it returns undefined.Here objects are displayed fine when i do console.log(this.vtypes)
controller.cs:
public JsonResult GetVerificationType(){
List<verificationType> vlist = new List<verificationType>();
vlist.Add(new verificationType() { vType = "vtype1", vtypeID = 1 });
vlist.Add(new verificationType() { vType = "vtype2", vtypeID = 2 });
vlist.Add(new verificationType() { vType = "vtype3", vtypeID = 3 });
return Json(vlist, JsonRequestBehavior.AllowGet);}
typescript file:
export class DashboardComponent implements OnInit {
model: any = {};
vtypes = [];
constructor(private LoginServiceModule: LoginService, private router: Router, ) { }
ngOnInit() {
this.LoginServiceModule.getVerificationTypes().subscribe(x => {
for (let i = 0; i <= x.length - 1; i++) {
this.vtypes.push(x[i]); <--- x[i].vtypeID doesn't work here
}
});
console.log(this.vtypes); <----- works fine shows array of 3 length
var item = this.vtypes.find(x => x.vtypeID === 1);
console.log(item); <---- returns undefined even when vtypeID = 1 is present
}}
You need to move the async logic inside the subscribe
this.LoginServiceModule.getVerificationTypes().subscribe(x => {
for (let i = 0; i <= x.length - 1; i++) {
this.vtypes.push(x[i]); <--- x[i].vtypeID doesn't work here
}
console.log(this.vtypes); <----- works fine shows array of 3 length
var item = this.vtypes.find(x => x.vtypeID === 1);
console.log(item);
});
Suggested reading: How do I return the response from an Observable/http/async call in angular2?

Why array save last data and doesn't clears?

I have a simple AngularJs application of medical cards.
I have storage with it and display it at my home.html using dx-datagrid:
One card has many records, I get records of card from recordsArray by cardId
getVardsRecordsByCardId: function (id, recordsArray) {
if (recordsArray.length != 0) {
for (var i = 0; i < recordsArray.length; i++) {
if (recordsArray[i].cardId === id) {
cardsRecords = cardsRecords.concat(recordsArray[i]);
}
}
}
return cardsRecords;
}
Now I have records just in the third card. I added a function on button for testing it:
var jdskla = [];
var localCardId = 0;
$scope.showCardDetails = {
text: "",
type: "default",
icon: "preferences",
onClick: function () {
if ($scope.itemIdFromShowButton) {
$location.path('/carddetail/' + $scope.itemIdFromShowButton);
var jdskla =[];
var jdskla = businessLogicOfMyApp.getVardsRecordsByCardId($scope.itemIdFromShowButton, $scope.recordsArray);
console.log($scope.itemIdFromShowButton)
console.log(jdskla);
}
else {
alert("Error!!!");
}
}
};
1,3,1 is cardId's and array of records. But, why array of card records don't clears and save last data?
May be somebody know how I can resolve it? Thanks for your answers!
P.S. I'm using ng-view directive in my app and i tried to clear my array use another button:
$scope.backToGeneralPage = {
text: "Back",
onClick: function () {
jdskla = [];
$location.path('/');
}
};
but it wasn't helpful.
You should initialize cardsRecords array in function getVardsRecordsByCardId.
getVardsRecordsByCardId: function (id, recordsArray) {
var cardsRecords = []; // initialize array locally
if (recordsArray.length != 0) {
for (var i = 0; i < recordsArray.length; i++) {
if (recordsArray[i].cardId === id) {
cardsRecords.push(recordsArray[i]);
}
}
}
return cardsRecords;
}

Resources