I am pretty much new to React so apologies for lack of code here.
Lets say I have a text field which shows the name of a user. When I click a button, I want this field to be editable.
Can this be done in ReactJS?
Use like this,
ES6 js:
class App extends React.Component{
constructor(){
super()
this.state={disable:true,
val:"username"}
}
handleClick(e){
this.setState({disable:!this.state.disable})
}
render(){
return(
<div className="wrapper">
<input type="text" disabled={this.state.disable} value={this.state.val}/>
<input type="button" value="Enable/Disable" onClick={this.handleClick.bind(this)}/>
</div>
)
}
}
Working Jsbin
Related
Initial there is a text box with add icon at right of text box, on click of add icon...a new text box to be added below and for previous text box, add icon to be change to delete icon
Initial there is a text box with add icon at right of text box, on click of add icon...a new text box to be added below and for previous text box, add icon to be change to delete icon
Update 17 January: I added the trackBy in template driven form, else the "focus" is loose when enter anything in the inputs
In general, when we have a "series of buttons" we use an array.
Imagine some like
<div class="group" *ngFor="let bt of buttons;let i=index">
<input> <button>add</button><button>delete</button>
</div>
If you use a .css like
.group:not(:last-child)>input+button{
display:none;
}
.group:last-child>input+button+button{
display:none;
}
The last "group" show the button "add" and the others show the button "delete"
Well. I imagine you want to store the inputs. So we can use a template driven form (use ngModel) or reactive Forms (use a FormArray)
Using template driven form
<div class="group" *ngFor="let value of values; let i = index;trackBy:trackFunction ">
<input [(ngModel)]="values[i]" />
<button (click)="add()">add</button>
<button (click)="remove(i)">delete</button>
</div>
Where we define
values:string[]=["",""]
add()
{
this.values.push("")
}
remove(index:number)
{
this.values.splice(index,1)
}
trackFunction(index:number,item:string){
return index
}
If we use a FormArray we iterate over formArray.controls
<div class="group" *ngFor="let control of formArray.controls; let i = index">
<input [formControl]="getControl(i)" />
<button (click)="addControl()">add</button>
<button (click)="removeControl(i)">delete</button>
</div>
And we use
formArray=new FormArray([new FormControl(""),new FormControl("")])
getControl(index:number)
{
return this.formArray.at(index) as FormControl;
}
addControl()
{
this.formArray.push(new FormControl(""))
}
removeControl(index:number)
{
this.formArray.removeAt(index)
}
You have the two approach in this stackblitz
Update^ in Angular js it's the same, some like
(function () {
'use strict';
angular.
module('plunker').
controller('ComponentController', ComponentController);
ComponentController.$inject = ['$scope'];
function ComponentController($scope)
{
$scope.values = ['','']
$scope.add = function add() {
$scope.values.push('');
};
$scope.remove = function remove(index) {
$scope.values.splice(index,1)
};
}
})();
And
<div ng-controller="ComponentController">
<div class="group" ng-repeat="value in values track by $index">
<input type="text" ng-model="values[$index]">
<button ng-click="add()">add</button>
<button ng-click="remove($index)">delete</button>
</div>
</div>
</div>
Below is my code
ComponentA.js
return statement inside the component
return (
......
<a id="MytoolTip" ......
<ComponentB
content={
`
<div class="share_cart_tt">
<a
data-automation-id="..."
class="callFunction"
> Invite </a>
</div>
`
}
target={'MytoolTip'}
closeButton
/>
);
ComponentB.js (this is a tooltip which will be displayed when user clicks on anchor tag MytoolTip)
.....
class ComponentB extends Component {
launchModal() {
console.log("hey its fine");
}
...
renderContent = () => {
document.getElementsByClassName('callFunction').
addEventListener('click', this.launchModal);
**I am trying to bind click event here but its not working out**
}
}
I am beginner to react and I tried different methods to bind the click event but nothing worked out.. need help.
When the user click on the anchor tag inside the tooltip with class .callFunction console.log should be printed.
Please note that I am trying to add onClick event to the anchor tag, which is just a static content in ComponentA and a tooltip will be created by getting static content in prop.content in ComponentB
React components have synthetic event listeners. All you need to do is add the onClick property to the element. So yours would look like this:
return (
......
<a id="MytoolTip" ......
<ComponentB
content={
`
<div class="share_cart_tt">
<a
data-automation-id="..."
onClick={FunctionToBeCalledWhenClicked}
class="callFunction"
> Invite </a>
</div>
`
}
target={'MytoolTip'}
closeButton
/>
);
i have created the a button and i want on click to disply the text box which replaces the existing text that i get from api
trnmnt-name.copmonent.ts
export class TrnmntNameComponent implements OnInit {
#Output() public trnameEvent = new EventEmitter<any>();
constructor(){}
ngOnInit(){
}
trEvent(){
this.trnameEvent.emit("Put text box here");
}
}
trnmnt-name .component.html
<div class="TrnmtName">
Hello There
<button type="submit" (click)="trEvent()" >Edit</button>
</div>
tournament.component.ts
export class TournamentComponent implements OnInit {
public message:"";
constructor(){}
ngOnInit() {
}
recieveMsg($event)
{
this.message = $event
}
}
tournament.component.html
<div>
{{message}}
<app-trnmnt-name (trnameEvent)="recieveMsg($event)"[trname]="tournamentdata?.tournament?.name"></app-trnmnt-name>
</div>
any solution to solve this
You need to do something like this. the (click) event in span will turn editingOn = true and that will replace text with input box. clicking on save button will call the save() and will turn the editingOn=false.
<ng-container *ngIf="!editingOn; else notEditing">
<p><span (click)="editingOn=!editingOn">{{yourTextModel}}</span></p>
</ng-container>
<ng-template #notEditing>
<input type="text" [(ngModel)]="yourTextModel" [ngModelOptions]="{standalone: true}">
<button type="button" (click)="save();editingOn=!editingOn">Save</button>
</ng-template>
I would like to change display property of this input (actually of div which contains it) after onclick ("Click here"). This part goes well ... but after clicking again I want it to be hidden again (has display="none" instead of "block" again and so on) and here I have difficulties.
I've tried classList.toggle but ... I' don't want to change classes, I want to change just one property. I know there is also possibility of creating my input field by using Javascript but I presume I'll come to a deadlock again in the same point.
<form role="form">
<p id="mag">Click here!</p>
<div id="switch" class="xxx"style="display:none;" >
<input id="sr"class="offf" autocomplete="off" placeholder="Search" type="text" >
</div>
</form>
document.getElementById("mag").addEventListener("click", function toffi(){
document.getElementById("switch").style.display="block";
console.log(document.getElementById("switch").style.display);
});
http://codepen.io/zeeebra/pen/RgRYxK
Try this:
document.getElementById("mag").addEventListener("click", function toffi(){
var sw = document.getElementById("switch");
if (sw.style.display === "block") {
sw.style.display = "none";
} else {
sw.style.display = "block";
}
console.log(sw.style.display);
});
I am facing an intriguing bug in React.
I have this component:
'use strict';
import SummaryStore from '../stores/SummaryStore';
import React from 'react';
export default class ChangeSummaryForm extends React.Component {
constructor() {
// store initialisation
SummaryStore.register();
var vRating = SummaryStore.getBookForSummaryPrint().summaryRating;
var vStarClassName = this.getRatingClasses(vRating);
this.state = {
sStarClassName: vStarClassName,
sCurrentBookToShow: SummaryStore.getBookForSummaryPrint()
};
this.thereIsASummaryToShow = this.thereIsASummaryToShow.bind(this);
}
getRatingClasses(pRating) {
var vI, vStarClassName = [];
for(vI = 0; vI < 4; vI++) {
if(pRating > 0) {
vStarClassName.push("glyphicon glyphicon-star");
pRating--;
} else {
vStarClassName.push("glyphicon glyphicon-star-empty");
}
}
return vStarClassName;
}
componentDidMount() {
SummaryStore.addChangeListener(this.thereIsASummaryToShow);
}
componentWillUnmount() {
SummaryStore.removeChangeListener(this.thereIsASummaryToShow);
}
thereIsASummaryToShow() {
this.setState({sCurrentBookToShow: SummaryStore.getBookForSummaryPrint(),
sStarClassName: this.getRatingClasses(SummaryStore.getBookForSummaryPrint().rating)
});
$("#summaryModal").modal('show');
}
render() {
return (<div className="modal fade" id="summaryModal">
<form>
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<button type="button" className="close" data-dismiss="modal" ariaLabel="Close"><span ariaHidden="true">× </span> </button>
<div style={{color: 'black'}}>
{this.state.sStarClassName.map(function(pCurrentClassName) { return (<span className={pCurrentClassName}></span>
);
})}
<h4 className="modal-title">Summary of {this.state.sCurrentBookToShow.title}</h4>
</div>
</div>
<div className="modal-body">
<div className="form-group">
<textarea className="form-control" rows="22" ref="summaryContent" >{this.state.sCurrentBookToShow.summary}</textarea>
</div>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default" data-dismiss="modal" >Close</button>
<input type="submit" className="btn btn-primary" value="Save"/>
</div>
</div>
</div>
</form>
</div>
);
}
}
As you might notice, it's a controller-view listening at a store which is registered to my AppDispatcher.
The steps above are correctly performed. i.e, when the particular action is triggerd, my component is correctly rendered with the variables {this.state.sCurrentBookToShow.title} and this.state.sCurrentBookToShow.title up-to-date.
The problem comes from this part:
<textarea className="form-control" rows="22" ref="summaryContent" >
{this.state.sCurrentBookToShow.summary}
</textarea>
The string is not printed in the textarea.
I tried this to debug:
render() {
var summary = "this is a summary";
return (// .. shortened for brevity
<textarea className="form-control" rows="22" ref="summaryContent">
{summary}
</textarea> ..);
}
the summary string printed correctly inside the mutable textearea.
Note that my browser says:
Warning: Use the defaultValue or value props instead of setting
children on <textarea>.
But I will fix this later since I think it doesn't have an effect on the current problem.
EDIT:
I took your remarks (so far) in consideration, so I updated my code like so:
<h4 className="modal-title">Summary of {this.state.sCurrentBookToShow.summary}</h4>
</div>
</div>
<div className="modal-body">
<div className="form-group">
{this.state.sCurrentBookToShow.summary}
<textarea className="form-control" rows="22" ref="summaryContent" defaultValue={this.state.sCurrentBookToShow.summary}></textarea>
</div>
I replaced this.state.sCurrentBookToShow.title by .summary to make
sure the ladder is not empty.
I put the summary into a defaultValue prop
Here is the output:
Second edit:
I uploaded a sample app that highlights the issue. I hope this would help to find a proper solution
Check this link from react docs: React Textarea Value
Basically for textArea react does not supports text enclosed within and you rather need to specify that as value or defaultValue.
The right way thus is
<textarea name="description" value="This is a description." />
or
<textarea name="description" defaultValue="This is a description." />
The difference with value and defaultValue is that specifying defaultValue leaves the component uncontrolled:
With an uncontrolled component, you often want React to specify the initial value, but leave subsequent updates uncontrolled. To handle this case, you can specify a defaultValue attribute instead of value.
...while specifying value instructs React to control the component, meaning you need to update value property to make sure that change is reflected in the component:
Since the value attribute is set on our form element, the displayed value will always be this.state.value, making the React state the source of truth.
To get a clear idea of difference between value / default value check this: Fiddle for value Default Value Distinction Console will always show new value but component will not.
Actually that is exactly what is wrong. From the docs:
If you want to initialize the component with a non-empty value, you can supply a defaultValue prop.
I had the same problem. I solved it by using controlled component, e.g.
state.value = this.props.value
<textarea value={this.state.value} onchange={handler} />
It works fine to control the input part. However, I had another issue, I need to init/change the state.value to props.value whenever there is a re-render.
I used the lift-cycle methods and it works perfect fine.
componentWillReceiveProps: function(){
this.setState({
value: this.props.value
}) }
hope this helps.