I have some data I'm distilling to render, essentially I'd like to have a checkbox to reset the state and DOM as it would when the page loads.
Initially I had a selected property on the state and a conditional to make sure it was working. That worked. But I can't get it to work, what am I missing?
UPDATE May 9th 2018
As Jay suggested below I am going to put the whole module in a snippet and focus on the parts which are the crux of the question/problem,
The whole module is in a snippet below...
I have a component that displays an array of objects, and each object is getting distilled into its own card. Below is a screenshot for clarity.
Here is my method in my component:
handleReset() {
this.setState({
data: this.props.data,
});
}
And this is the JSX which is being rendered.
<label>
<input type="checkbox" onChange={this.handleReset} />
<b>reset</b>
</label>
With some time to think about this I realize that my handeReset is not doing anything is probably because it is just rendering the state as it is now. So how my question is how do you go back to the way the UI looked initially? Pre sorting?
import React, {
Component
} from 'react';
import {
Card,
Select,
Segment,
Container,
Divider,
Grid,
Header,
Image
} from 'semantic-ui-react';
import '../css/app.css';
class FilterOptions extends Component {
constructor(props) {
super(props);
this.state = {
data: this.props.data,
priority: '',
category: '',
selected: false,
};
this.handleChange = this.handleChange.bind(this);
this.handleReset = this.handleReset.bind(this);
}
handleReset() {
this.setState({
data: this.state.data,
});
}
handleChange(e) {
var val = e.target.value;
if (!isNaN(val)) {
this.setState({
priority: val
});
} else if (isNaN(val)) {
this.setState({
category: val
});
}
this.props.changeOption(val);
}
render() {
var reset;
if (!this.state.data) {
reset = 'reset';
} else {
reset = 'not reset';
}
return ( <
div >
<
h5 > By category < /h5> <
label >
<
input type = "checkbox"
onChange = {
this.handleReset
}
/>
reset {
reset
} <
/label> <
h5 > By category < /h5> <
ul >
<
li >
<
label >
<
input type = "radio"
value = "cat1"
checked = {
this.state.category === 'cat1'
}
onChange = {
this.handleChange
}
/>
cat1 <
/label> <
/li> <
li >
<
label >
<
input type = "radio"
value = "cat2"
checked = {
this.state.category === 'cat2'
}
onChange = {
this.handleChange
}
/>
cat2 <
/label> <
/li> <
li >
<
label >
<
input type = "radio"
value = "cat3"
checked = {
this.state.category === 'cat3'
}
onChange = {
this.handleChange
}
/>
cat3 <
/label> <
/li> <
/ul> <
h5 > By priority < /h5> <
ul >
<
li >
<
label >
<
input type = "radio"
value = "1"
checked = {
this.state.priority === '1'
}
onChange = {
this.handleChange
}
/>
1 <
/label> <
/li> <
li >
<
label >
<
input type = "radio"
value = "2"
checked = {
this.state.priority === '2'
}
onChange = {
this.handleChange
}
/>
2 <
/label> <
/li> <
li >
<
label >
<
input type = "radio"
value = "3"
checked = {
this.state.priority === '3'
}
onChange = {
this.handleChange
}
/>
3 <
/label> <
/li> <
li >
<
label >
<
input type = "radio"
value = "4"
checked = {
this.state.priority === '4'
}
onChange = {
this.handleChange
}
/>
4 <
/label> <
/li> <
/ul> {
/*<h5>By Color</h5>
<ul>
<li>
<label>
<input type="radio" value="Orange" checked={this.state.color === 'Orange'} onChange={this.handleChange} />
<div className="circle orange-filter-bg" />
</label>
</li>
<li>
<label>
<input type="radio" value="Green" checked={this.state.color === 'Green'} onChange={this.handleChange} />
<div className="circle green-filter-bg" />
</label>
</li>
<li>
<label>
<input type="radio" value="Blue" checked={this.state.color === 'Blue'} onChange={this.handleChange} />
<div className="circle blue-filter-bg" />
</label>
</li>
<li>
<label>
<input type="radio" value="Purple" checked={this.state.color === 'Purple'} onChange={this.handleChange} />
<div className="circle purple-filter-bg" />
</label>
</li>
</ul>*/
} <
/div>
);
}
}
function FilterUsers(props) {
return ( <
Container >
<
br / >
<
br / >
<
Grid columns = {
3
}
doubling stackable > {
props.data.map((user /* leveraging arrow functions implicit return */ ) => ( <
Grid.Column key = {
user.name
} >
<
Segment className = {
`priority${user.priority}`
} >
<
Card >
<
Card.Content >
<
Card.Header >
<
h2 > name: {
user.name
} < /h2> <
/Card.Header> <
Card.Meta >
<
span className = "card__age" > age: {
user.age
} < /span> <
/Card.Meta> <
Card.Description > priority: {
user.priority
} < /Card.Description> <
Card.Description className = "card__catergory" > category: {
user.category
} < /Card.Description> <
/Card.Content> <
/Card> <
/Segment> <
/Grid.Column>
))
} <
/Grid> <
/Container>
);
}
export default class SortAndFilterForm extends Component {
constructor(props) {
super(props);
this.state = {
data: this.props.data,
priority: '',
category: '',
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(val) {
if (!isNaN(val)) {
this.setState({
priority: val
});
var filteredByPriority = this.props.data.filter(function(item) {
return parseInt(item.priority) === parseInt(val);
});
} else {
this.setState({
category: val
});
var filteredByPriority = this.props.data.filter(function(item) {
return item.category === val;
});
this.setState({
category: val
});
}
console.log('filteredByPriority', filteredByPriority);
this.setState({
data: filteredByPriority
});
}
render() {
return ( <
Container >
<
h1 > Sorts < /h1> <
FilterOptions data = {
this.state.data
}
changeOption = {
this.handleChange
}
/> <
FilterUsers data = {
this.state.data
}
/> <
/Container>
);
}
}
I am guessing the issue is here:
handleReset() {
this.setState({
data: this.state.data,
});
}
where you set the state.data to state.data, which unsurprisingly doesn't change anything. I imagine you want to do:
handleReset() {
this.setState({
data: this.props.data,
});
}
Your HandleReset() method should be setting everything in state as it was at the start:
this.state = {
data: this.props.data,
priority: '',
category: '',
};
And your cards are rendered with props.data.map meaning they would remain unaffected with changes in state. You should use data from state to render them out.
If I were you, I would turn FilterOptions into a purely functional component (e.g accepts only props, has no constructor, no state, move handleReset and handleChange upwards to SortAndFilterForm and pass them back down via props).
In SortAndFilterForm's constructor(), I would store a copy of its initial state data element (which has been passed to it as props.data from something else) as a state variable (n.b. Object.assign creates a shallow shallow copy which seems like it should work here but it depends on the contents and how you mutate them elsewhere):
this.state = {
data: props.data,
initData: Object.assign({}, props.data),
priority: '',
category: ''
}
The handleReset method in SortAndFilterForm's would then look like this:
handleReset() {
this.setState({
data: Object.assign({}, this.state.initData)
})
}
You will of course need to bind that in SortAndFilterForm's constructor():
this.handleReset = this.handleReset.bind(this)
...and pass it down to FilterOptions:
<FilterOptions
data={this.state.data}
changeOption={this.handleChange}
resetOption={this.handleReset} />
I believe all of the above should take care of your problem.
Related
I am new to JS Community and I am practicing JS. I have Input text field where User will enter the array like 1,2-5,6-9,10,12,13-15 . What I am trying is to expand this array like 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
<input class="inputC" id="inputC" type="text" value="" placeholder="Select Control" required />
JS I tried
var items = $('#inputC').val().split(",");
for (var i in items) {
console.log(items[i]);
}
Note: You should specify the logic of adding 11 in the output based on the given input.
You should split on - as well which you can do inside a map call back function.
Demo:
var items = $('#inputC').val().split(',');
items = items.flatMap(i =>{
var list = [];
i = i.split('-');
if(i.length > 1){
for (var j = i[0]; j <= i[1]; j++) {
list.push(+j);
}
}
else{
list.push(+i[0]);
}
return list;
});
console.log(items);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input class="inputC" id="inputC" value="1,2-5,6-9,10,12,13-15" type="text" value="" placeholder="Select Control" required />
let str='1,2-5,6-9,10,12,13-15';
str.split(',').map((e)=>{
if(e.includes('-')){
const values = e.split('-');
const end = parseInt(values[1]);
let start = parseInt(values[0]);
while(start < end){
console.log(start);start++;
}
}else{
console.log(parseInt(e))
}
})
This is a great scenario in which you want to create a small derived webcomponent that does just what you need:
<input is="array-input" />
class ArrayInput extends HTMLInputElement {
get value() {
let inter = super.value.split(',').map(el => el.trim());
let ret = [];
for (const el of inter) {
const [ start, end ] = el.split('-').map(Number);
if (isNaN(start)) continue;
if (!end || start > end) {
ret.push(start);
} else if (start === end) {
ret.push(start)
} else if (start < end) {
for (let i = start; i <= end; i++) {
ret.push(i);
}
}
}
return [...new Set(ret)].sort();
}
}
customElements.define('array-input', ArrayInput, {
extends: 'input'
});
<input is="array-input" onchange="console.log(this.value)" />
I have a provider that provides tags for news articles (list with news). If they are more than three, then the additional tags (>3) will be grouped together (called plusTags in the example code). I can see in my console that I have all the tags, but they are not distributed correctly. Ex.
On the first page I have four news with the distributed tags "a,b,c", b", "ac" "b". On the next page, the news articles are (obviously) different, but the distribution of the tags is the SAME ("a,b,c", b", "ac" "b") as on the first page, even if the tags should be different. So the distribution follows the same pattern. I suspect it's my code in my "componentDidMount" function, as its there where I distribute all the tags. Suspect it might repeat because this function repeats itself?
public componentDidMount(): void {
let tags = [];
let plusTags = [];
if (this.props.tags != null) {
if (this.props.tags.length > 0) {
for (var i = 0; i < 3; i++) {
if (this.props.tags[i] != undefined) {
tags.push(this.props.tags[i] + " ");
}
}
for (var j = this.props.tags.length - 1; j >= 3; j--) {
if (this.props.tags[i] != undefined) {
plusTags.push(this.props.tags[j] + " ");
}
}
} else {
tags = this.props.tags;
}
}
this.setState({
tags: tags,
plusTags: plusTags
});
}
and in my render
public render(): React.ReactElement<INewsTagsProps> {
return <React.Fragment>
<div className={styles.tagContainer}>
{
this.state.tags ?
this.state.tags.map((t) => {
if (this.props.background == BackgroundType.None) {
return (
<a href={this.props.tagPageUrl + "?tag="+ t}>
<span className={styles.tagNewsTiles}>{t}</span>
</a>);
}
else {
return(
<a href={this.props.tagPageUrl + "?tag="+ t}>
<span className={styles.tagFollowedNews}>{t}</span>
</a>);
}
})
: null
}
{this.state.plusTags.length > 0 ?
<span className={`callout-target-${this.state.targetIndex} ${this.props.background == BackgroundType.None ? styles.tagNewsTiles : styles.tagFollowedNews}`}
onClick={(e) => {e.stopPropagation(); this.setState({plusTagsDialogOpen: true});}}>+ {this.state.plusTags.length}</span>
:
null
}
</div>
<Callout
className="ms-CalloutExample-callout"
gapSpace={0}
target={this.state.target}
onDismiss={() => this.closeDialog()}
hidden={!this.state.plusTagsDialogOpen}
isBeakVisible={true}
beakWidth={10}
directionalHint={DirectionalHint.topCenter}
>
<div className={styles.tagPopupWrapper}>
<div className={styles.tagPopupContainer}>
{this.state.plusTags ?
this.state.plusTags.map((t) => {
if (this.props.background == BackgroundType.None) {
return (
<a href={this.props.tagPageUrl+ "?tag="+t}>
<span className={styles.tagNewsTiles}>{t}</span>
</a>);
}
else {
return(
<a href={this.props.tagPageUrl+ "?tag="+t}>
<span className={styles.tagFollowedNews}>{t}</span>
</a>);
}
}):
null}
</div>
</div>
</Callout>
;
var Admin = React.createClass({
saveUpload: function(id) {
alert(id);
},
getInitialState() {
return {
uploads: []
};
},
componentDidMount() {
var self = this;
$.ajax({
url: 'http://localhost:8080/admin/uploads',
success: function(data) {
self.setState({
uploads: data
})
}
});
},
render: function() {
var obj = this.state.uploads.map(function(product) {
return (
<Uploads product = {product}
saveHandle = {this.saveUpload}
/>
)
});
return (
< div >
<div className = "container" >
<br / >
<h1 className = "text-center" > Welcome Admin < /h1>
<br / > < br / >
<hr / >
</div>
<h3 className = "text-center" > Company Upload Details < /h3>
<div className = "container" >
<table className = "table" >
<thead className = "thead-light" >
<tr >
<th > Id < /th> <th > CompanyName < /th>
<th > Date & Time < /th> <
th > FileName(csv) < /th> <
th > Size(KB) < /th> <
th > Status < /th> <
/tr> <
/thead> {
obj
} <
/table>
</div> </div>
)
}
});
here is the uploads component
var Uploads = React.createClass({
show() {
this.props.saveHandle(this.props.product.id);
},
render() {
return (
<tr>
<td> {this.props.product.id} </td>
<td> {this.props.product.company.companyName} </td>
<td> {(new Date(this.props.product.date)).toString()} </td>
<td> {this.props.product.fileName} </td>
<td> {this.props.product.filesize} </td>
<td> {this.props.product.status} </td>
<td>
<button className = "button" onClick = {this.show}> Save </button>
</td>
</tr>
)
}
});
Here is my code i am passing id from Uploads component to admin component when save button is clicked but it gives me an error that saveUpload is not defined.
I am confused my it is giving me that error i have a function saveUpload in Admin Component. what is wrong in this code
The bug is here:
var obj = this.state.uploads.map(function(product) {
return (
<Uploads product = {product}
saveHandle = {this.saveUpload}
/>
)
});
Inside the map(), this is no longer the instance of your Admin component, it is window. If you bind it like so:
var obj = this.state.uploads.map(function(product) {
return (
<Uploads product = {product}
saveHandle = {this.saveUpload}
/>
)
}.bind(this));
Then this will point to the Admin instance and you should get the function you're expecting. If you have ES6 available, you could also write it like this:
var obj = this.state.uploads.map(product =>
<Uploads product = {product}
saveHandle = {this.saveUpload}
/>);
Using the "fat arrow" => lambda expression, this is automatically bound to the enclosing scope inside, saving you some effort.
I'm trying to render a select dropdown menu with years.
I'm using a simple loop to generates all the years for the dropdown menu, see dateYear().
If I place {this.dateYear()} outside of {this.state.careerHistoryPositions.map((careerHistoryPosition) it renders correctly however when I place it inside {this.state.careerHistoryPositions.map((careerHistoryPosition) it renders the select element however the years don't populate.
I'm not getting any errors in console either.
export default class CareerHistoryFormPage extends Component {
constructor(props) {
super(props);
const profileCandidateCollection = props.profileCandidate;
const careerHistoryPositions = profileCandidateCollection && profileCandidateCollection.careerHistoryPositions;
this.state = {
careerHistoryPositions: [{company: '', startDateYear: ''}],
};
}
dateYear() {
var yearDate = '';
for (var i = new Date().getFullYear(); i >= 1975; i--) {
yearDate += '<option value="' + i + '">' + i + '</option>';
}
$('select').html('<option>Year</option>' + yearDate);
}
}
render() {
return (
<form onSubmit={this.handleFormSubmit}>
{this.state.careerHistoryPositions.map((careerHistoryPosition) => (
<div key={careerHistoryPosition.uniqueId}>
<input
type="text"
value={careerHistoryPosition.company}
onChange={this.handleCompanyNameChange(careerHistoryPosition.uniqueId)}
/>
<select value={CareerHistoryFormPage.startDateYear} >
{this.dateYear()}
</select>
</div>
</form>
);
}
}
I don't think this is the most elegant solution, however, it's how I got it working. The problem was jquery. Thanks to #nem035 for pointing that out.
export default class CareerHistoryFormPage extends Component {
constructor(props) {
super(props);
const profileCandidateCollection = props.profileCandidate;
const careerHistoryPositions = profileCandidateCollection && profileCandidateCollection.careerHistoryPositions;
this.state = {
careerHistoryPositions: [{company: '', startDateYear: ''}],
};
}
getStartDateMonthSelect(careerHistoryPosition) {
const monthRange = [];
for (let i = 0; i <= 11; i++) {
monthRange.push(i);
}
return (
<select value={careerHistoryPosition.startDateMonth} onChange={this.handleStartDateMonthChange(careerHistoryPosition.uniqueId)}>
<option>Month</option>
{monthRange.map(month => (
<option key={month} value={month}>{moment().month(month).format('MMMM')}</option>
))}
</select>
);
}
}
render() {
return (
<form onSubmit={this.handleFormSubmit}>
{this.state.careerHistoryPositions.map((careerHistoryPosition) => (
<div key={careerHistoryPosition.uniqueId}>
<input
type="text"
value={careerHistoryPosition.company}
onChange={this.handleCompanyNameChange(careerHistoryPosition.uniqueId)}
/>
{this.getStartDateMonthSelect(careerHistoryPosition)}
</div>
</form>
);
}
}
I have a problem with the uib-carousel of angular-bootstrap 2.0.1.
I don't understand what happen... I had angular-bootstrap 2.0.0 with a slider with images and video (videogular directives). Everything was perfectly working, then I maj to 2.0.1 and since, nothing work...Even if I rollback to 2.0.0!!
The problem is that the slider doesn't change slide, nor if I click on the indicators, nor if I wait for the timer (set by myInterval).
Do you have an idea ?
Here is my HTML code:
<div uib-carousel active = "dv.active" interval = "dv.myInterval" no-wrap = "dv.noWrapSlides" class = "carousel-container" >
<div uib-slide ng-repeat = "slide in dv.slides track by slide.slide_id" index = "slide.slide_id" >
<div ng-if = "!slide.isVideo" >
<img ng-src = "{{slide.slide_path}}" title = "{{slide.nom_slide}}"
alt = "{{slide.nom_slide}}" width = "360" height = "280" style = "margin:auto;" >
</div >
<div ng-if = "slide.isVideo" >
<div class = "videogular-container" >
<videogular vg-theme = "slide.theme.url" vg-update-state = "dv.startStopSlideshow()" >
<vg-media vg-src = "slide.source" ></vg-media >
<vg-controls class = "vg-controls" vg-autohide = "slide.plugins.controls.autoHide"
vg-autohide-time = "slide.plugins.controls.autoHideTime" >
<vg-play-pause-button ></vg-play-pause-button >
<vg-time-display >{{ currentTime | date:'mm:ss':'+0000' }}</vg-time-display >
<vg-scrub-bar >
<vg-scrub-bar-current-time ></vg-scrub-bar-current-time >
</vg-scrub-bar >
<vg-time-display >{{ timeLeft | date:'mm:ss':'+0000' }}</vg-time-display >
<vg-time-display >{{ totalTime | date:'mm:ss':'+0000' }}</vg-time-display >
<vg-volume >
<vg-mute-button ></vg-mute-button >
<vg-volume-bar ></vg-volume-bar >
</vg-volume >
<vg-fullscreen-button ></vg-fullscreen-button >
</vg-controls >
<vg-overlay-play class = "overlay-play" ></vg-overlay-play >
<vg-poster vg-url = 'slide.plugins.poster' ></vg-poster >
<vg-buffering ></vg-buffering >
</videogular >
</div >
</div >
</div >
</div >
<!-- Indicators -->
<ol class = "custom-carousel-indicators" ng-show = "dv.slides.length > 1" >
<li ng-repeat = "slide in dv.slides track by $index" ng-class = " { active: dv.isActive(slide) }"
ng-click = " dv.setActive($index)" >
<img ng-src = "{{slide.slide_path}}" alt = "{{slide.nom_slide}}" width = "120px" height = "93px" >
</li >
</ol >
And here is my JavaScript:
$q.all([
services.getAllVideosFromMaterielId(mat_id).then(
function(dataVideos)
{
var videos = dataVideos.data;
for (var j = 0; j < videos.length; j ++) {
var slide = {
source : [
{
src: "assets/videos/materiel/" + videos[j]['video_path'], type: "video/mp4"
}
],
theme : {
url: "bower_components/videogular-themes-default/videogular.css"
},
plugin : {
controls: {
autoHide : true,
autoHideTime: 5000
},
poster : "assets/videos/posters/" + videos[j]['video_path']
},
slide_id : vm.slides.length,
//Pour les vidéos, slide_path correspond au chemin vers le poster de la vidéo
slide_path: "assets/videos/posters/" + videos[j]['video_path'] + ".png",
nom_slide : videos[j]['nom_video'],
legende : "",
isVideo : true
};
vm.slides.push(slide);
}
}
),
services.getAllImagesFromMaterielId(mat_id).then(
function(dataImages)
{
var images = dataImages.data;
for (var j = 0; j < images.length; j ++) {
var slide = {
slide_id : vm.slides.length,
slide_path: "assets/images/materiel/" + images[j]['image_path'],
nom_slide : images[j]['nom_image'],
legende : images[j]['legende_image'],
isVideo : false
};
vm.slides.push(slide);
}
}
)
]).then(function()
{
vm.myInterval = 5000;
vm.startStopSlideshow = function()
{
if (vm.myInterval == 5000) {
vm.myInterval = 0;
} else {
vm.myInterval = 5000;
}
};
vm.noWrapSlides = false;
vm.active = 0;
vm.isActive = function(slide)
{
return vm.active === slide.slide_id;
};
vm.setActive = function(idx)
{
vm.active = idx;
vm.slides[idx].active = true;
};
vm.noMateriel = false;
})
}
else {
vm.noMateriel = true;
}
Thanks a lot for the help!
Problem solved! It was just a compatibility problem with the update of bootstrap (and not angular-bootstrap as I thought)!
Downgrade to Bootstrap 3.3.7 solved the problem! :D