Ternary Operator (Inline If without Else) - reactjs

I have two checkbox in a <form>. The <form> have an onChange={this.checkBoxOnChange} assigned to it which fires up checkBoxOnChange(event){..} function on every change in the form. I am trying to map (Log) the status (ie whether they are checked or not). So, I've initially taken there value as false as they are not-checked then on each event I'm trying to change there value respectively (ie if false the true & vice versa).
On following this SO post I tried this:
(event.target.value=='BILLDED') && billed=(!billed)
By doing this I get:
Syntax error: Invalid left-hand side in assignment expression
Then I tried this:
(event.target.value=='BILLDED') ? (billed=(!billed)) : null
However, it gives me BILLED:true on every onChange (when clicked on BILLED checkbox)
This is code for checkbox inside render method:
render() {
return (
<div>
<form onChange={this.checkBoxOnChange}>
<input type="checkbox" name="invoicestatus" value="BILLDED" />BILLDED<br />
<input type="checkbox" name="invoicestatus" value="PENDING" />PENDING<br />
</form>
<ListData data={this.state.data}/>
</div>
);
}
This is the checkBoxOnChange() function within that same class (Component):
checkBoxOnChange(event){
var billed=false;
var pending=false;
// (event.target.value=='BILLDED') ? (billed=(!billed)) : null;
(event.target.value=='BILLDED') && billed=(!billed)
// (event.target.value=='PENDING') ? pending=(!pending) : null;
console.log("BILLDED:"+billed+" PENDING:"+pending);
}
What's wrong with the code?
Can I not use inline statement for this scenario?
Is there any better, more concise approach?

What's wrong with the code?
You initialise the billed variable inside your event handler to false, that's why you always get true instead of toggling a state.
Can I not use inline statement for this scenario?
You can, you just have to put the parenthesis in the right place:
(event.target.value == 'BILLDED') && (billed = !billed);
// ^
Is there any better, more concise approach?
Use a normal if statement. It's both shorter and more readable:
if (event.target.value == 'BILLDED') billed = !billed;

However, it gives me BILLED:true on every onChange (when clicked on
BILLED checkbox)
Is this not because you use a local variable as below
var billed=false;
which always starts as false?

Related

Prevent check of checkbox until function has been called

We are using AngularJS, and we require to run a function that determines whether or not the checkbox the user clicked on is actually checked. I have tried to pass $event on ngChange, but discovered it is unable to pass the $event object. The reason I want to access the $event object is so I could prevent the default behaviour until the required function has been called.
The code is as follows (does not include the code where I attempted to pass $event as it didn't work):
<label for="specificCase">
Yes
<input
type="checkbox"
name="specificCase"
data-ng-click="specificCase.no = false; specificCase.unsure = false;"
data-ng-model="specificCase.yes"
data-ng-change="$ctrl.specificCaseCheck('yes', specificCase.yes)">
</label>
<label for="notSpecificCase">
No
<input
type="checkbox"
name="notSpecificCase"
data-ng-click="specificCase.yes = false; specificCase.unsure = false;"
data-ng-model="specificCase.no"
data-ng-change="$ctrl.specificCaseCheck('no', specificCase.no)">
</label>
controller
function specificCaseCheck(value, boolean) {
//HERE I WANT TO PREVENT THE DEFAULT BEHAVIOUR OF THE CHECKBOX CHECKING
vm.optionSelected = !boolean ? false : true;
vm.caseSpecific = value == 'yes' && boolean ? true : false;
}
Question
How do I keep the checkbox unchecked until I have run a function in the controller?
Since you need to prevent default behavior but dont have the event you can follow the approach of this question Angular 2 Checkbox preventDefault
Basically you need to return false in your event handler to prevent the default behavior of "checking the checkbox when is clicked" then you can click it programatically
For that you can check this question Angular 4 checkbox set programmatically

AngularJs Divs won't toggle using <select>

I have this in my view:
<div>
<select ng-model="chartType" ng-change="AnalyticsChartTypeChanged(chartType)"
ng-init="chartType='Data grid'">
<option value="Data grid">Data grid</option>
<option value="Histogram">Histogram</option>
</select>
{{chartType}}
<br>showAnalyticsDataGrid == {{showAnalyticsDataGrid}}
<br>showAnalyticsHistogram == {{showAnalyticsHistogram}}
<div ng-show="{{showAnalyticsDataGrid}}">
<p>Data grid goes here</p>
</div>
<div ng-show="{{showAnalyticsHistogram}}">
<p>Histogram goes here</p>
</div>
and the ng-chnage funtion is
$scope.AnalyticsChartTypeChanged = function(chartType)
{
switch (chartType)
{
case 'Data grid': $scope.showAnalyticsDataGrid = true;
$scope.showAnalyticsHistogram = false;
console.log('Show analytics data grid');
break;
case 'Histogram': $scope.showAnalyticsDataGrid = false;
$scope.showAnalyticsHistogram = true;
console.log('Show analytics histogram');
break;
}
}
When I alernately select each of the options, the debug text updates correcrly:
Data grid
showAnalyticsDataGrid == true
showAnalyticsHistogram == false
and
Histogram
showAnalyticsDataGrid == false
showAnalyticsHistogram == true
BUT, it constantly shows the Data grid DIV and never the Historgram DIV.
Obviouly I am making an extermely simple mistake, but I just can't see it :-(
With ng-show, you give it an expression and it evaluates it for you. What you're trying to do here is evaluating the expression by using the double curly braces{{}}, which doesn't make sense. If you simply remove those double curly braces and use the ng-show like this:
ng-show="showAnalyticsDataGrid"
it will work just fine.
Also, remember that those 2 variables aren't being initialized so when the view loads there will be no div shown by default, but you can fix that with an ng-init.
Here's a fiddle. http://jsfiddle.net/5DMjt/5248/
You don't use {{}} interpolation in ng-show. Like many other directives it directly evaluates expressions
Change
<div ng-show="{{showAnalyticsHistogram}}">
To
<div ng-show="showAnalyticsHistogram">

ngclass form validation to add a new class

is it possible to check in ng-class whether if the myform.$valid then add a class?
I tried, so if the form is valid it will add the class "orange". Not sure if ng-class can do it like this or im just doing it wrong.
<input data-ng-disabled="vm.registerPassword != vm.registerConfirmPassword ? vm.registerPassword : disabled; registerForm.$invalid" disabled="disabled"
type="submit" id="submitForm" data-ng-click="vm.registerForm()" value="Register" class="btn-main orange maxw-200" ng-class="myform.$valid: 'orange'">
Here for the password condition:
data-ng-disabled="vm.registerForm.Password !== vm.registerForm.ConfirmPassword ? vm.registerForm.Password : disabled || registerForm.$invalid"
Yes it possibly. See in doc
In the case of a map, the names of the properties whose values are truthy will be added as css classes to the element.
ng-class="{'orange' : myform.$valid}"
Alternatively you can use a function, such as ,
ng-class="isValid(myform)"
and in your script...
$scope.isValid = function (frm){
if(frm.$valid == true)
return 'Orange'
else
return '';
}

Dynamic attribute in ReactJS

I want to dynamically include/omit the disabled attribute on a button element. I have seen plenty of examples of dynamic attribute values, but not of attributes themselves. I have the following render function:
render: function() {
var maybeDisabled = AppStore.cartIsEmpty() ? "disabled" : "";
return <button {maybeDisabled}>Clear cart</button>
}
This throws a parse error because of the "{" character. How can I include/omit the disabled attribute based on the (boolean) result of AppStore.cartIsEmpty()?
The cleanest way to add optional attributes (including disabled and others you might want to use) is currently to use JSX spread attributes:
var Hello = React.createClass({
render: function() {
var opts = {};
if (this.props.disabled) {
opts['disabled'] = 'disabled';
}
return <button {...opts}>Hello {this.props.name}</button>;
}
});
React.render((<div><Hello name="Disabled" disabled='true' />
<Hello name="Enabled" />
</div>)
, document.getElementById('container'));
By using spread attributes, you can dynamically add (or override) whatever attributes you'd like by using a javascript object instance. In my example above, the code creates a disabled key (with a disabled value in this case) when the disabled property is passed to the Hello component instance.
If you only want to use disabled though, this answer works well.
I'm using React 16 and this works for me (where bool is your test):
<fieldset {...(bool && {disabled:true})}>
Basically, based on the test (bool) you return an object with the conditional attributes or you don't.
Also, if you need to add or omit multiple attributes you can do this:
<fieldset {...(bool && {disabled:true, something:'123'})}>
For more elaborate attribute managed I suggest you prefab the object with (or without) the attributes outside of JSX.
You can pass a boolean to the disabled attribute.
render: function() {
return <button disabled={AppStore.cartIsEmpty()}>Clear cart</button>
}
function Test() {
return (
<div>
<button disabled={false}>Clear cart</button>
<button disabled={true}>Clear cart</button>
</div>
);
}
ReactDOM.render(<Test />, document.querySelector("#test-container"));
console.log(Array.from(document.querySelectorAll("button")));
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="test-container"></div>
Far cleaner than the accepted solution is the solution which AnilRedshift mentioned, but which I'll expand on.
Simply put, HTML attributes have a name and a value. As a shorthand, you can use the name only for "disabled", "multiple", etc. But the longhand version still works, and allows for React to work in it's preferred way.
disabled={disabled ? 'disabled' : undefined} is the most legible solution.
The version I used was:
<button disabled={disabled ? 'disabled' : undefined}>
Click me (or dont)
</button>
More cleaner way of doing dynamic attributes which works for any attributes is
function dynamicAttributes(attribute, value){
var opts = {};
if(typeof value !== 'undefined' && value !== null) {
opts['"'+attribute+'"'] = value;
return opts;
}
return false;
};
Call in your react component like following
<ReactComponent {...dynamicAttributes("disabled",false)}
{...dynamicAttributes("data-url",dataURL)}
{...dynamicAttributes("data-modal",true)} />
Tips :
You could have dynamicAttributes function in a common place/utilities and
import it to use it across all components
you could pass value as null to not render dynamic attribute
A simple and clean way of doing it
<button {...disabled && {disabled: true}}>Clear cart</button>
disabled should come from props like this
<MyComponent disabled />
You can find something similar to this at the documentation.
https://facebook.github.io/react/docs/transferring-props.html
In your case could be something like this
function MyComponent(props) {
let { disabled, ...attrs } = props;
if (disabled) {
// thus, it will has the disabled attribute only if it
// is disabled
attrs = {
...attrs,
disabled: true
}
};
return (
<button {...attrs}>MyLabel</button>
)
}
This code is using ES6, but I thing you got the idea.
This is cool because you can pass many others attributes to this component and it will still work.
First you can simply check
<button disabled={true}>Button 1</button>
<button disabled={false}>Button 2</button>
Note: **disabled value is not String, it should be Boolean.
Now for dynamic. You can simply write
<button type="button" disabled={disable}
onClick={()=>this.setSelectValue('sms')} >{this.state.sms}</button>
As you can see I am using disabled property and in curly brackets it can be local variable/state var.
The variable disable contains values true/false.
In case others come here for attributes other than disabled, e.g., custom attributes like data-attr, one can assign empty string "" as the value in the object to be spread to eliminate the value of the attribute. With the attribute name only available on the resulted HTML.
For example:
<div {...(trueOrFalse && { [`data-attr`]: "" })}></div>
Furthermore, if you wish the name of the attribute being dynamic too. Due to template strings support string interpolation, you can put state into it to make the name of attribute dynamic.
<div {...(trueOrFalse && { [`${state}`]: "" })}></div>
This could work, problem with disabled is one could not simply set boolean for it.
if(AppStore.cartIsEmpty()){
return "<button disabled>Clear cart</button>"
}
else
{
return "<button>Clear cart</button>"
}

unable to get ng-checked property for a radio button to check the button when populating a form

EDIT: for those of you who don't want to go through the code, I'm basically passing the form a "node" object with node.selectedAnswer = "4,1,4" or some string like that. The form has radio buttons and one of the buttons has a value "4,1,4". the radio button also has ng-checked="node.selectedAnswer" expression. But that doesn't work. I know for sure that node.selectedAnswer has the appropriate value.
I have a series of radio button questions that I'm asking the user. I want them to be able to go previous and next. I'm using a stack to store the data retrieved from ajax call, as well as selectedAnswer when they select an option and click next. I've commented the code itself to explain the situation where I can. Everything seems to be working, except ng-checked is just not picking up node.selectedAnswer, even though I can output {{node.selectedAnswer}} properly to the page.
<div class="container-fluid" ng-app="AccountRequest" ng-controller="GameNode" ng-init="outside={}">
<div class="row-fluid">
<div class="span2"></div>
<div class="span10">
<form>
<!-- node.selectedAnswer displays the selectedAnswer correctly when clicking previous and going back.
However, ng-checked is somehow not selecting the appropriate radio button. -->
<span>{{node.Question.Text}} selected answer: {{node.selectedAnswer}}</span>
<div class="radio" ng-repeat="answer in node.Answers">
<input type="radio" id="answerGroup" name="answerGroup" ng-checked="node.selectedAnswer" ng-model="outside.selectedAnswer"
value="{{answer.BranchId}},{{node.LeafId}},{{answer.Id}}"/> {{answer.Text}}
</div>
<div>
<input type="button" ng-click="previous()" value="Previous"/>
<input type="button" ng-click="next(outside.selectedAnswer)" value="Next"/>
</div>
</form>
</div>
</div>
</div>
//below is the script
app.controller('GameNode', function ($scope, $http) {
var nodes = [];
function load(branchId, leafId, answerId) {
$http.get("/AccountRequest/GetNode?branchId=" + branchId +
"&leafId=" + leafId +
"&answerId=" + answerId)
.success(function (data) {
//get data and push it in the stack
nodes.push(data);
$scope.node = data;
});
}
function populateValues(selectedAnswer) {
var answer = null;
if (selectedAnswer === undefined || selectedAnswer == null)
selectedAnswer = "0,0,0";
//when next is clicked, retrieve the selectedAnswer from form and store it in current node as a property.
if (nodes.length > 0) {
var curNode = nodes.pop();
curNode.selectedAnswer = selectedAnswer;
nodes.push(curNode);
}
answer = selectedAnswer.split(',');
if (answer != null) {
load(answer[0], answer[1], answer[2]);
}
}
$scope.next = populateValues;
$scope.previous = function () {
//when previous is clicked, pop the current node out and throw it away.
//then pop the previous node out, read it, and push it back in as current node.
if (nodes.length > 1) {
nodes.pop();
var prevNode = nodes.pop();
nodes.push(prevNode);
$scope.node = prevNode;
}
};
populateValues();
});
Older Answer - This works, (was marked correct) but using $parent can get a bit messy in nested repeats.
In this instance, you don't need to use ng-checked at all. Since this is a radio group, the checked attribute will be bound to the model. If the model is bound to the value of an individual radio button, then your ability to change which button is "checked" becomes very simple.
Here is a plunk that demonstrates the concept.
So in your case a few changes need to be made.
1. Get rid of 'id' attribute - the ID must be unique for each element.
2. Each item created in an ng-repeat creates its own child scope. So to access the original model, "$parent" must be invoked.
<input type="radio" name="answerGroup" ng-model="$parent.someAnswerAttribute"
value="{{answer.BranchId}},{{node.LeafId}},{{answer.Id}}"/>
In your controller define the model as you already did, then modify it to be tied to a value of a button, which in your case will be a bit lengthy, since you have multiple attributes within your value.
$scope.someAnswerAttribute = // exactly what the value of a radio button would be.
Again, the plunker above reflects this concept. Hope this helps!
..
..
Edit - Better Answer:
Since the ng-repeat creates its own child scope, and two-way binding is necessary, the ng-model should be referencing an object instead of a primitive. In other words, if the model was $scope.myModel="Biff", the child scope can not access that without invoking $parent (in the answer below). However, if the model is referencing a property of an object, the child will receive prototype inheritance of that object. (I think I said that right).
So using the older answer example, we can change:
From this in the parent controller:
$scope.someAnswerAttribute = "Biff";
To this in the parent controller:
$scope.someAnswerAttribute = {value: "Biff"}
And in the radio group:
<input type="radio" name="answerGroup" ng-model="someAnswerAttribue.value"
value="{{answer.BranchId}},{{node.LeafId}},{{answer.Id}}"/>
This plunk is forked from the older answer and demonstrates model as an object property.

Resources