I have a big analytics page. At the top, there are some form elements to choose what you'd like to see the report about.
Below that are some graphs and tables.
So I have the hierarchy set up like this:
Page
Otherstuff1
Report
Setting1
Setting2
Graph1
Graph2
Table
Otherstuff2
I think the state of this report should be held in the Report component, as it's the lowest component that contains everything that needs access to this state.
So how can I update the state of Report when there is a change in Setting1?
Here's a simplified version of my code, relevant to the question.
var Report = React.createClass({
getInitialState: function() {
return {
dateRange: "not changed",
}
},
changeDateRange: function(event) {
console.log("changed");
this.setState({dateRange: "changed"});
},
render: function() {
return (
<div>
<ReportDateRange change={this.changeDateRange}/>
{this.state.range}
</div>
);
}
});
var ReportDateRange = React.createClass({
render: function() {
return (
<select className="form-control" id="historicalRange"
onChange={this.props.change}>
<option value="yesterday">Yesterday</option>
<option value="week">Last week</option>
<option value="fortnight">Last fortnight</option>
<option value="month" selected>This month</option>
</select>
)
}
});
This code runs, and it does log in the console that something changed, but it does not update the state of Report. I think it might be updating the state of ReportDateRange instead.
The control flow looks right to me.
I think what you've got wrong is how you've referenced the state variable.
In your render() function you reference this.state.range but elsewhere that variable is dateRange not range.
Related
I'm using vue cli and I have a select to display a dropdown list of measurements. I’m wondering how I can update props when I select a measurement?
I have a user profile card that opens a modal with a chart displaying measurement for that user and when I'm in the modal I would like to select another measurement and display that instead.
So when I click on volume, the chart text changes to volume and displays volume data for that user.
I tried to do it for text of the chart via an event listener #change but the props are not passing down and changing the text:
Select: <select name="show" class="show" v-model="value" #change="newText($event)">
<option :value="{ text: 'Volume'}">Vol</option>
<option :value="{ text: 'Weight'}">Weight</option>
<option :value="{ text: 'Temperature'}">Temp</option>
</select>
<script>
newText: function(event) {
const text = this.value.text;
this.text = text;
},
<script>
Here is a codesandbox example.
Any help would be great, thanks!
You need to find the user object and render the value of that user. Here I find the clicked user using computed property inside the tab and render the value of that user. codesandbox .
Computed inside Tabs.vue :
computed: {
selectedUser() {
if (this.name) {
let name = this.name;
return this.users.find(e => e.name == name);
} else return {}
}
}
Simple array in an component, that it will be display in a selector tag. I'm not being able to make it to work. The firstSelector doesn't display anything.
In the component.js:
sortPropertiesAsc: ['value:asc'],
selectors: ['model.modelObjects', 'model.location'],
firstSelector: function(){
const firstGroupObjects = this.get('selectors')[0];
return Ember.computed.sort(firstGroupObjects, 'sortPropertiesAsc');
}.property(),
In the component.hbs
<select onchange={{ action 'selectBrand' value="target.value" }}>
<option value="" >Select company</option>
{{#each firstSelector as |company|}}
<option value={{company.id}} selected={{eq brand company.id}}> {{company.value}}</option>
{{/each}}
</select>
if I write the firstSelector like this in the component.hbs, it will work:
firstSelector: Ember.computed.sort('model.modelObjects', 'sortPropertiesAsc'),
How can I write it like the other way(as a function)
I'm not sure all that's at play in your setup, but one thing I noticed off the bat was if you want your computed property to fire based on changes to selectors, you'd want something like this.
firstSelector: Ember.computed('selectors', function() {
const firstGroupObjects = this.get('selectors.firstObject');
return Ember.computed.sort(firstGroupObjects, 'sortPropertiesAsc');
})
If sortPropertiesAsc changes values you'd want to add it in too
firstSelector: Ember.computed('selectors', 'sortPropertiesAsc', function() {
const firstGroupObjects = this.get('selectors.firstObject');
return Ember.computed.sort(firstGroupObjects, 'sortPropertiesAsc');
})
We are working on a project where we are using boxes, and need one of the select boxes to perform an action when the user selects an item from the list. This is our first time touching React, let alone building a project, so we are stumped at this point. What we are using for the onChange code is as follows:
var React = require('react');
var ReactPropTypes = React.PropTypes;
var ProgramSelectorComponent = React.createClass({
propTypes: {
allPrograms: ReactPropTypes.array.isRequired
},
_updateProgram: function(e) {
this.setState({
value: "TEST"
});
},
render() {
var Programs = this.props.allPrograms;
var options = Programs.map(function (prog) {
return <option key={ prog.program_id } value={ prog.program_id } >
{ prog.program_name }
</option>;
});
return (
<select className="form-control margin-bottom" name="Program" id="programSelect" ref="progRef" onChange={this._updateProgram} >
<option value="select">Select</option>
{options}
</select>
)
}
});
module.exports = ProgramSelectorComponent;
I know that rendering does work in the <select> tag as I can add something like data-test-id="test" and that renders properly, but for some reason, the onChange is not showing in the code, and therefore, not working.
Like I said, we are all new to this, so any help would be greatly appreciated.
In your select tag try: <select value={this.state.value}>
This is a good read about controlled vs uncontrolled components: https://facebook.github.io/react/docs/forms.html
I have to create a dynamic page with dropdown list and checkbox, this part is completed.
Second part is that the selected dropdown list should go to default value(00000000-0000-0000-0000-000000000000) when I click the checkbox.
I tried but no luck. Can anyone suggest ideas?
var AnswerView = React.createClass({
getInitialState: function() {
return {defaultselected: "00000000-0000-0000-0000-000000000000"};
},
render: function() {
//Picklist -1
//Text -2
//Date -3
return (<div>
<tr><td><select value={this.props.responses.length==1?this.props.responses[0].lw_questionanswerid.Id:""}><div>
<FormatePickList answers={this.props.answers} defaultselected ={this.state.defaultselected}/>
</div></select></td></tr><br></br><br></br>
<tr><td>
No Answer<input type="checkbox" checked={this.props.responses.length==1?this.props.responses[0].LW_UnabletoobtainData:false}
onChange={this.handleChange.bind(this, 'question')}/>
Not Applicable<input type="checkbox" checked={this.props.responses.length==1?this.props.responses[0].LW_NotApplicable:false}/>
</td>
</tr>
</div>);
}
,handleChange: function(field, e) {
this.replaceState({defaultselected: "00000000-0000-0000-0000-000000000000"});
this.setState(e.target.checked)
}
});
var FormatePickList=React.createClass({
render: function() {
var rows = [];
rows.push(<OptionValues key={-1+"-v"} value={this.props.defaultselected} text={""}/>);
for(var i = 0; i < this.props.answers.length; i++)
{
rows.push(<OptionValues key={i+"-v"} value={this.props.answers[i].LW_questionanswerId} text={this.props.answers[i].LW_answer}/>);
}
return (<b>{rows}</b>);
}
});
var OptionValues=React.createClass({
render: function() {
return(<option value={this.props.value}>{this.props.text}</option>);
}
});
Since I don't know what this.props.answers and this.props.responses are, I have difficulties reproducing your problem. However, it's clear to me that you want some checkbox to automatically change the value of a dropdown to the 'default' or 'no answer' value.
You'll need to handle the onChange event for both the checkbox and the select-element. In the event handler you'll update the state to whatever answer is selected, so, in the case of the select-element it's the selected answer and in case of the checkbox it's always the 'no answer' value.
An example of how you're able to do this:
(JSFiddle: http://jsfiddle.net/thijs_s/r00haqjt/1/)
var AnswerView = React.createClass({
getInitialState: function() {
return {
'selectedAnswer': 'no_answer'
}
},
render: function() {
return (
<div>
<select onChange={this.handleChange} value={this.state.selectedAnswer}>
<option value='no_answer'>No answer</option>
<option value='answer1'>Answer 1</option>
<option value='answer2'>Answer 2</option>
</select>
<br /><br /><br />
<input onChange={this.handleNoAnswer} type='checkbox' value='no_answer' checked={this.state.selectedAnswer=='no_answer'} /> No Answer
<p>Current state: {this.state.selectedAnswer}</p>
</div>
);
},
handleChange: function(e) {
console.log(e.target.value);
this.setState({
selectedAnswer: e.target.value
});
},
handleNoAnswer: function (e) {
console.log(e.target.checked);
if(e.target.checked) {
this.setState({
selectedAnswer: 'no_answer'
});
}
}
});
React.render(<AnswerView />, document.body);
Keeping in mind that you have to stay away from 'anti-patterns' (http://facebook.github.io/react/tips/props-in-getInitialState-as-anti-pattern.html), you can define the initial state through the properties. From your comments I understand that if there's a response (checking the length of the array) then the first answer should be selected (the [0] in your code). So what you basically want, is to have your initial state set to this first answer. The logic (code) for this, can be put outside of your React class and the initial state can passed to it through a property. I updated the JSFiddle to show how it's done: http://jsfiddle.net/thijs_s/r00haqjt/2/
I have a dropdown list:
<select
ng-model="filter.country"
ng-options="country.code as country.name for country in countries"
ng-change="broadcast()">
<option value="">All Countries</option>
</select>
$scope.countries is initially populated by a service, and then another dropdown change event would limit the values of $scope.countries by calling the service again, passing by the other dropdown's selected item.
The issue here is when $scope.filter.country is already bound to a value (other than the default value) and $scope.countries gets updated to a new list that doesn't include $scope.filter.country's value. I can see countries dropdown reverting back to its default option "All Countries", however $scope.filter.country remains as it was.
Any ideas about this scenario? Shouldn't $scope.filter.country get updated back to the default value?
Update: Here is a fiddle
Update:
Just to illustrate this, here is a screenshot from the fiddle:
This does look like a bug to me, I have opened an issue for it.
Update: This has been addressed and fixed by angularjs team; demo here.
The behavior you see is exactly how double-binding should work:
Most developers have a "feel" for double-binding more than an understanding. The "feeling" is that any visual change to a model-bound input results in a model change.
But any developers who have tried mode-binding a JQuery date picker will attest to this phenomenon: The JQuery Date-Picker will not update an angular model bound to same input. In other words, picking a date through JQuery does not register with any AngularJS events.
Similarly, pasting values into model-bound inputs usually does not result in the model being updated.
Analyzing your case:
You have changed the list of options for a model bound drop-down. As a result, the displayed option reverts back to default --> because the model-bound value is not a relevant choice.
Angular's ng-model doesn't care about the "options list" for your drop-down. The framework is waiting for some event that will cause it to run through the digest cycle and absorb a change of value picked. Even though something has "appeared" to change, this is an illusion or loop-hole depending on how you see it.
For a drop-down (select element), a change event is a "click" on an option.
I had issues with that in the past. It took me a good while to fix.
Try replacing your select by:
<select ng-model="changeMe" ng-options="d as d for d in dummyValues" ng-change="changeMeChange(); filter.country = undefined">
<option value="">Change Me</option>
</select>
adding the "filter.country = undefined" to your ng-change should reset the value of your model.
Hopefully that will sove your problem.
If the contents of the countries select is independent of the value of changeMe, then two-way binding won't help us here. Instead, we have to make the change manually as follows:
$scope.changeMeChange = function () {
// this or a $scope.$watch for independent values in the changeMe drop down menu
if ($scope.filter) {
$scope.filter.country = null;
}
$scope.countries = [
{
"code": "MX",
"name": "Mexico"
}
];
};
If the the values in the changeMe select can be used to filter the country select and thus make the country select dependent upon the change me select, then we can use a filter as follows:
'use strict';
var myApp = angular.module('myApp', []);
function FilterCtrl($scope) {
$scope.countries = [
{
"code": "KW",
"name": "Kuwait"
},
{
"code": "US",
"name": "USA"
},
{
"code": "MX",
"name": "Mexico"
}
];
$scope.dummyValues = ['MX'];
$scope.changeMeChange = function() {
$scope.countries = [
{
"code": "MX",
"name": "Mexico"
}
];
};
};
<select ng-model="changeMe" ng-options="d as d for d in dummyValues" ng-change="changeMeChange()">
<option value="">Change Me</option>
</select>
<select ng-model="filter.country" ng-options="country.code as country.name for country in countries | filter: { code: changeMe} ">
<option value="">All Countries</option>
</select>
Updated Fiddle using the filter approach as it seems the more likely use case. If you want your users to have access to the complete list of countries if no change me option has been selected, then a Custom Filter is the way to go.
Hope this helps.
The ng-model is binded to the scope variable called filter.country. When you update the ng-options values, it doesn't trigger the change that would update filter.country. It's a separate and unrelated variable at this point. So, there is no implicit bound connection. Only on selection of the list, the selection field is updated.
Though you can easily reset the select model from where you call the service or include a $watch on the options to reset the ngmodel value.
$scope.$watch('countries', function() {
//reset the model here
});
<table>
<tr ng-init="states()">
<td>State</td>
<td><select ng-model="selectedState" data-ng-change="cities1()" data-ng-options="state.Id as state.Name for state in states" /></td>
</tr>
<tr>
<td>City</td>
<td><select ng-model="selectedCity" data-ng-options="city as city for city in cities" /></td>
</tr>
</table>
.JS file contents -
$scope.cities = function () {
$scope.working = true;
$scope.title = "Loading Cities...";
$scope.cities = [];
$scope.queryDisabled = true;
var stateId = $scope.selectedState;
if (stateId) {
$http.get("/api/city", { params: { "stateId": $scope.selectedState } }).success(function (data, status, header, config) {
$scope.cities = data;
$scope.title = "Displaying Cities...";
$scope.working = false;
$scope.queryDisabled = false;
}).error(function (data, status, headers, config) {
$scope.title = data + status;
$scope.working = false;
$scope.queryDisabled = false;
});
}
else {
$scope.cities = [];
}
};
$scope.$watch($scope.selectedState, function () {
$scope.cities1 = $scope.cities;
});
$scope.cities1 = $scope.cities;
I don't know the reason why it is so and how it works, but I have to create a copy of the cities variable as cities1. Assign cities1 from cities iself, then use cities1 on State combo ng-change event and cities on City combo ng-model directive.
ng-click doesn't seems the right replace of ng-change. Child combobox contents should change when selected item is changed in Parent (State) combo, and not when someone just click on Parent combo.