Cannot read property 'value' of undefined for this.refs.variable.value - reactjs

I am new to react.
I have a input text like so
<input type="text" class="form-control" placeholder="Number Only"
value={that.state.value}
ref={"qtype3" + item.questionId}
/>
now in a method I want to get the value inputted in the textbox,so i wrote following block in the method:
const uniqueNames = Array.from(new Set(this.state.arrrefs));
if (uniqueNames.length > 0) {
for (let i = 0; i < uniqueNames.length; i++) {
var inval = uniqueNames[i];
var ans = this.refs.inval.value;
var newans = {
taskId: this.props.location.state.tskId,
userId: this.props.location.state.userid,
questionId: "",
answer: ans
};
this.state.radioObj.push(newans);
}
}
here arrrefs is an array in the state with refs of several textboxes. In the variable inval I am getting the first value of the arrrefs. but i am getting the exception in the line var ans = this.refs.inval.value;
TypeError: Cannot read property 'value' of undefined
If I pass a hardcode value in line this.refs.inval.value instead of inval I am getting response.
for example, my uniqueNames = ["qtype3800316", "qtype3800317", "qtype3800318", "qtype3800324"]
so i want this.refs.qtype3800316.value
What am I doing wrong here?

Try using the [] operators if you want to access within an array/object by a variable.
this.refs[inval].value

Try just with: this.refs.inval
Also you can console.log(this.refs) and check what you have there.

Related

Iteration over an array of objects

I have an array of objects from which I intend to copy few of its fields to a different object values on a specific condition
fromPage: "home";
sample = [
{iD:100A,fName:"jack",age:28},
{iD:200A,fName:"kate",age:29},
{iD:300A,fName:"rose",age:30}
]```
` copyObj = [{id:string, name:string}] `
Expecting result:
copyObj = [
{id:100A,name:"jack"},
{id:100B,name:"kate"}
]
I want to copy these specific fields to my copyObj from the sample. I tried in below way but ended up getting undefined.
for(var i=0;i<=sample.length;i++){
if(this.fromPage == "home")
{
this.copyObj[i].id = this.sample[i].iD;
this.copyObj[i].name = this.sample[i].fName;
}
}
The above is throwing error like: cannot read property 'ID' of undefined
Any other way to implement this , please help. I also tried using forEach to iterate and then push the values to the new array but it didn't work.
Try the following code,
this.copyObj=[];
for(var i=0;i<=this.sample.length-1;i++){
if(this.fromPage === "home"){
this.copyObj.push({'id':this.sample[i].iD,'name':this.sample[i].fName});
}
}

Mobx, filter and unshift an array in #computed

Given an observable array holding an id-name-propertyName collection I'm trying to filter out a country array which has to be bound to a html select control in a React component. I need to present an empty default in my select html element. The filter for 'Country' works great, however when adding the .unshift(empty) portion I'm running into problems.
My observable:
class ReferenceStore {
#observable referenceData=[];
My #computed so far:
#computed get countries() {
var empty = { id: 0, name: '' };
var test = this.referenceData.filter(x => x.propertyName === "Country").unshift(empty);
return test;
}
The problem is that this code results in the following error message in my component :
ReferenceStore__.a.countries.map is not a function
How should I go about this? Thank you!
unshift returns the new length of the array, and not the array itself.
You can do unshift first, and then return the array instead.
#computed get countries() {
var empty = { id: 0, name: '' };
var test = this.referenceData.filter(x => x.propertyName === "Country");
test.unshift(empty);
return test;
}

ReactJS how to loop through dynamically named refs

I'm looking at a line of code that has dynamically named refs for an input, where 'item' is an incrementing value starting at zero.
"input type="text" ref={'name'+item} defaultValue={item} />"
How would I loop through these dynamic refs to scrape out the values? I tried this with no luck. It tells me object undefined. (the length of inputs will equate to the number of added elements)
var arr = this.state.inputs;
var arrayLength = arr.length;
for (var i = 0; i < arrayLength; i++) {
var c = this.refs.name + i.value
alert(c);
}
Though, this DOES work, but its dynamic, so I need to loop through it, not hard code it:
alert(this.refs.name0.value);
alert(this.refs.name1.value);
alert(this.refs.name2.value);
I believe you need to get the DOM objects for the inputs, not just the refs (at least that has been my experience):
import ReactDOM from 'react-dom';
const values = {};
Object.keys(this.refs)
.filter(key => key.substr(0,4) === 'name')
.forEach(key => {
values[key] = ReactDOM.findDOMNode(this.refs[key])).value || null;
});
Good luck!

How to get selected value from ng-option inside an ng-repeat

In my controller I loop through the questions in a questionnaire:
for (var j = 0; j < s.Questions.length; j++) {
var q = s.Questions[j];
var aq = datacontext.getAnsweredQuestion(propertySurvey.ID, q.ID);
if (q.QuestionType === 'SingleAnswer') {
q.dropdown = true;
q.selectedOption = aq.ActualAnswers.length > 0 ?
aq.ActualAnswers[0].AnswerID : null;
q.optionChanged = function () {
var debug = q.selectedOption; //ERROR - this is undefined
aq.toggleAnswer(q.selectedOption);
}
}
//etc
}
In my view I also loop through the questions:
<div class="card" ng-repeat="q in s.Questions">
<div class="item item-divider">{{q.Text}}</div>
<div class="item" ng-if="q.dropdown">
<select ng-model="q.selectedOption"
ng-options="va.AnswerID as va.Text for va in q.ValidAnswers"
ng-change="q.optionChanged()">
<option>--Select--</option>
</select>
</div>
</div>
When viewed in the browser, the correct selectedOption is displayed, and the q.optionChanged() function is called. But inside that function, q.selectedOption is undefined
EDIT
This function appears to work. But I can't explain why!
q.optionChanged = function () {
var debug = q.selectedOption;
var answerid = this.selectedOption;
aq.toggleAnswer(answerid);
}
The reason is because you are creating a function with closure inside a loop, which causes this unintuitive behavior. Read: Creating closures in loops: A common mistake
What ends up happening is that the inner function captures the last q in the loop, for which (for some reason, perhaps not shown in code here), q.optionChanged is undefined.
In contrast, this is bound to the right q as supplied by the expression in ng-change="q.optionChanged()".
To fix, use a function generator to create a function for each loop iteration, like so:
for (var j = 0; j < s.Questions.length; j++) {
var q = s.Questions[j];
var aq = datacontext.getAnsweredQuestion(...);
// ...
q.optionChanged = makeOptionChangeFn(q, aq)
// ...
}
function makeOptionChangeFn(q, aq){
return function(){
aq.toggleAnswer(q.selectedOption);
}
}
Using this would have worked if you only needed to reference the right q, but you also need aq, so a function generator is necessary.
In order to auto select the option from the controller, the ng-model must hold a reference to the array item. Assign q.selectedOption the whole item from the aq.ActualAnswers. You will need to refactor your comprehension expression.
// in the controller
q.selectedOption = aq.ActualAnswers.length > 0 ?
aq.ActualAnswers[0] : null;
// in the view
ng-options="va as va.Text for va in q.ValidAnswers"
This has caused me pain in the past. Make sure you read the documentation carefully:
https://docs.angularjs.org/api/ng/directive/ngOptions

Converting string to array using filter and using it in ng-repeat

I have a string which is in "1200:2,1300:3,1400:2" format. I need this to be printed like
<p>1200</p><p>2</p>
<p>1300</p><p>3</p>
<p>1400</p><p>2</p>
I tried using filter,
return function (input) {
//Validate the input
if (!input) {
return '';
}
var hoaArray = [];
var inputArray = input.split(',');
for (var i = 0; i < inputArray.length; i++) {
var adminTimeArray = inputArray[i].split(':');
hoaArray.push({ 'adminTime': adminTimeArray[0], 'dose': adminTimeArray[1]?adminTimeArray[1]:'' });
}
return hoaArray;
};
and inside html like
<p ng-repeat="timing in timing_list | formatter">{{timing.}}</p>{{timing .adminTime}}</div>
I am getting the following error,
Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: [[{"msg":"fn: regularInterceptedExpression","newVal":36,"oldVal":34}],[{"msg":"fn: regularInterceptedExpression","newVal":38,"oldVal":36}],[{"msg":"fn: regularInterceptedExpression","newVal":40,"oldVal":38}],[{"msg":"fn: regularInterceptedExpression","newVal":42,"oldVal":40}],[{"msg":"fn: regularInterceptedExpression","newVal":44,"oldVal":42}]]
Could anyone please help me understand what am I doing wrong?
Regards,
Raaj
In the IndexController.js file:
var inputString = "1200:2,1330:3,1400:4,1500:3";
var formatInputString = function (input) {
//Validate the input
if (!input) {
return '';
}
var hoaArray = [];
var inputArray = input.split(',');
for (var i = 0; i < inputArray.length; i++) {
var adminTimeArray = inputArray[i].split(':');
hoaArray.push({ 'adminTime': adminTimeArray[0], 'dose': adminTimeArray[1] ? adminTimeArray[1] : '' });
}
return hoaArray;
};
$scope.inputString = inputString;
$scope.formattedString = formatInputString(inputString);
In the HTML file:
<div ng-repeat="timing in formattedString" >
{{timing.adminTime}}
{{timing.dose}}
</div>
The issue here - possibly a limitation or a bug in Angular - is that your filter creates new array objects every time it runs. ng-repeat uses under the covers $scope.$watchCollection to watch for the expression "timing_list | formatter" - this watcher always trips up because, in trying to detect a change in a values in the collection, it compares objects with a simple "!==" - and the objects are always new and different objects.
In short, this is another way to reproduce:
$scope.items = [1, 2, 3];
$scope.$watchCollection("items | foo", function(){
});
where foo is a filter that operates on each element in the array creating a new object:
.filter("foo", function(){
return function(inputArray){
return inputArray.map(function(item){
return {a: item};
});
};
});
So, to answer your question - you cannot with Angular v1.3.15 use a filter that returns an array with objects (without some funky object caching) with $watchCollection, and by extension, with ng-repeat.
The best way is to create the array first (with ng-init or in the controller), and then use it in the ng-repeat.

Resources