Issues populating dynamic dropdown with React Hooks - node.js

I've never had any issue with this before but I've been struggling to get this to work so I'm hoping someone can notice something that I'm just missing. I'm pulling data from an API to populate a dropdown in my form. The data gets returned just fine and I assign in to my state using useState with no issues. However, when I try to map my results to options for my dropdown it never populates.
Here is my state and my useEffect function that I'm calling once when the page loads.
const [users, setUsers] = useState([]);
useEffect(() => {
axios.get("/api/v1/users/find").then(res => {
setUsers(res.data)
})
}, [])
Like I said this all works with no issues. However this is where the issue is, my form:
<div className="relative">
<h3 className="text-2xl text-center font-bold mb-6">Create Item</h3>
<select className="h-full w-full border-gray-300 px-2 transition-all border-blue rounded-sm" name="owner" onChange={(e) => props.handleChange(e, 'owner')}>
<option selected value=''>Owner</option>
{users.map((i) => {
<option value={i.name}>{i.name}</option>
})}
</select>
</div>
Once again I've never had any issues with this so I'm not sure why it doesn't seem to be populating. Appreciate the help in advance!

Currently the function passed to .map() doesn't return anything. You can fix this by replacing the curly braces with parentheses:
{users.map((i) => (
<option value={i.name}>{i.name}</option>
))}
When curly braces are used in an arrow function you need an explicit return statement to return a value. Without curly braces there is an implicit return on the one line of the function body. (In this case parentheses just help define that one line since it includes carriage returns.)

Related

onSelect not triggering for React dropdown

I am very new to React so I am still learning a lot. No matter what I do, the onSelect just does not seem to work. I made a simple function that it should call. You can tell it's been called via the console.log but in the browser when I look at the console it just does nothing.
Here is the code for the dropdown:
import React,{useState} from 'react';
import SEPractices from "../dummydata/SEPractices"
const optionItems = SEPractices.map((SEPractice) =>
<option key={SEPractice.practice}>{SEPractice.practice}</option>
);
const Dropdown = () => {
const handleSelect=(e)=>{
console.log("Inside");
}
return (
<div>
<select onSelect={handleSelect}>
<option Default hidden>
Select an SE Practice
</option>
{optionItems}
</select>
</div>
)
}
export default Dropdown;
Try using onChange
<select value={selectValue} onChange={handleSelect}>
<option Default hidden>
Select an SE Practice
</option>
{optionItems}
</select>
And handleSelect like this:
const [selectValue, setValue] = useState("")
cosnt handleSelect = (e) => {
setValue(e.target.value);
}
<select value={selectValue} onChange={handleSelect}>
<option value="dummyData">
Select an SE Practice
</option>
{optionItems}
</select>
and don't use the Default and hidden, React will take care of default value why specifying value={selectValue} in element.
Also, we need to pass the value to the <option>, it will track based on the value.

form.item doesn't seem to get the validated class when entering a value in a jest test

I have a jest test to test a custom component containing the form.item from antd. I've trimmed everything down to clean out-of-the-box code to ensure this is not in my customizations.
My test file contains a defined form like this:
const TestForm = () => {
return (
<Form id='frm_test' name='frm_test'>
<Form.Item data-testid="test" label='email' name='email' rules={[{ type: 'email', message: 'test' }]}>
<Input />
</Form.Item>
</Form>
);
};
Next I have a test like this:
it('Should should validate email input', () => {
const { getByLabelText, getByTestId } = render(<TestForm />);
screen.debug(getByTestId('test'))
userEvent.type(getByLabelText('email'), 'T');
screen.debug(getByTestId('test'));
});
When I now run the test, I expect that the generated form.item will have the error message bellow the input field if you look at the debug information. This happens when you manually type something in such a situation, so I expect it to happen in the test as well. However this doesn't happen as you can see in the output from the test debug.
What I do see is that the form item is having the class ant-form-item-is-validating. I expect this to be ant-form-item-has-error (or something like that). It's as if the validation 'hangs' somewhere.
The debug information is:
<div
class="ant-row ant-form-item ant-form-item-is-validating"
data-testid="test"
>
<div
class="ant-col ant-form-item-label"
>
<label
class=""
for="frm_test_email"
title="email"
>
email
</label>
</div>
<div
class="ant-col ant-form-item-control"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<input
class="ant-input ant-input-sm"
id="frm_test_email"
type="text"
value="T"
/>
</div>
</div>
</div>
</div>
Okay so I saw that the validation was actually also doing a log in the console. With some messing around, I can actually do a test to see if the message appears using spy. Right now I did it with this code :
const oSpy = jest.spyOn(console, 'warn').mockImplementation();
userEvent.type(getByLabelText('email'), 'T');
expect(oSpy).toHaveBeenCalledTimes(1);
Now the above example won't reflect my message, but will work. I adjusted my custom component to contain some console.warn('some message') and then the spy can get it.
Not exactly what I wanted though. Also, I'll end up having console... in my code. I certainly don't want that when I build it. So I'll remove that with uglyfyjs.
Spend 2 days on this but i found an answer.
You can wait for the validation to end by accessing the validateFields method as follows:
const form = wrapper.find(Form);
form.props().form.setFieldsValue({
email: 'invlid email'
});
return form.props().form.validateFields().catch(err => {
expect(!!err.errorFields).toBe(true);
})

Handlebars disable attribute if id exist

I have list of electoralUnits and I need to disable some in that list if that id of electoralUnit is added in other collection called StateResult.
Route:
router.get('/', (req, res) => {
StateResult.find({})
.then(stateResults => {
ElectoralUnit.find({})
.then(electoralUnits => {
StateList.find({})
.then(stateLists => {
res.render('home/results/stateResults', {
stateResults: stateResults,
electoralUnits: electoralUnits,
stateLists: stateLists
});
});
});
});
});
Now, I tried this in handlebars with if helper:
<select name="electoralUnit" multiple class="form-control" id="exampleFormControlSelect2" size="40">
{{#each electoralUnits}}
<option value="{{id}}" {{#each stateResults}} {{#if this}} disabled {{/if}} {{/each}}>{{name}}</option>
{{/each}}
</select>
and a lot of variations of this, like:
<option value="{{id}}" {{#each stateResults}} {{#if electoralUnit}} disabled {{/if}} {{/each}}>{{name}}</option>
but nothing works. Where am I wrong?
This is data from mongodb:
electoralunits collection
{"_id":"5ab906612f30fe23dc592591","town":"5ab903952e9dc70408a81e32","name":"1. МЗ Аеродром - Дом Здравља","__v":0,"electoralNumber":4200,"safeVoter":360,"date":"2018-04-25T15:19:37.900Z"}
stateresults collection
{"_id":"5ac4e01d46fa2b21280bd981","electoralUnit":"5ab906612f30fe23dc592591","allVotes":100,"validVotes":90,"invalidVotes":10,"partyVotes":[50,10,10,10,5,5],"__v":0}
I tried as #doowb explain me with custom handlebars helper:
includes: function(arr, prop, val, options) {
const matches = arr.map(item => item[prop]).includes(val);
if (matches) {
return options.fn(this);
} else {
return options.inverse(this);
}
}
<select name="electoralUnit" multiple class="form-control" id="exampleFormControlSelect2" size="40">
{{#each electoralUnits}}
<option value="{{this._id}}" {{#includes ../stateResults "electoralUnit" this._id}} hidden {{/includes}}>{{name}}</option>
{{/each}}
</select>
but this won't work either.
It would help if you show that the stateResults, electoralUnits, and stateLists objects look like, but I think your issue is with knowing which "depth" you're currently trying to access data from. In the express middleware you're passing all of those objects to the template at the root of the object:
{
stateResults: [], // assuming this is an array
electoralUnits: [], // assuming this is an array
stateLists: [] // assuming this is an array
}
In the {{#each stateResults}} you'll need to use the parent specifier: {{#each ../stateResults}} since the first {{#each}} block created a new depth. Also, the logic of the data objects doesn't seem correct since you don't need to add multiple disabled attributes to the option tag. If you post what the actual data objects look like, I'll edit this answer with additional information.
Edit: After getting more information, I think that using the {{pluck}} and {{inArray}} helpers and subexpressions like the following will achieve your goal. If the ../stateResults does work, then try #root.stateResults (I'm still assuming that the collections returned are arrays of those mongodb objects):
<select name="electoralUnit" multiple class="form-control" id="exampleFormControlSelect2" size="40">
{{#each electoralUnits}}
<option value="{{this._id}}" {{#inArray (pluck ../stateResults "electoralUnit") this._id}} disabled {{/inArray}}>{{name}}</option>
{{/each}}
</select>
If you don't want to use the handlebars-helpers library (indicated in a comment below) you can create your own helper:
Handlebars.registerHelper('includes', function(arr, prop, val, options) {
const matches = arr.map(item => item[prop]).includes(val);
if (matches) {
return options.fn(this);
} else {
return options.inverse(this);
});
Then you can use that helper like this:
<select name="electoralUnit" multiple class="form-control" id="exampleFormControlSelect2" size="40">
{{#each electoralUnits}}
<option value="{{this._id}}" {{#includes ../stateResults "electoralUnit" this._id}} disabled {{/includes}}>{{name}}</option>
{{/each}}
</select>

MVC 5 / Bind dropdownlist including disabled values

(My first quesion, I'm quite impressed :) )
First, please excuse my English, I'm French ;)
My issue is about DropDownList which is linked(bind) to a required field (F, int) of an object O (edited in a view V) and contains a list of elements (LE), some of them disabled.
The behavior I want in the view :
when I create an object, the validation must trigg if nothing
is selected in the list (OK)
when I create an object, the disabled elements of the list must not be selectable (OK)
when I edit an object, if the field is among enabled values, same behavior (OK)
when I edit an object, if the field is among disabled values, it must be displayed and selected when viewed (OK)
when I edit an object, if the field is among disabled values, when I post data, the client validation must authorize disabled values to be validated (OK with a little javascript)
My issue :
when I edit an object, if the field is among disabled values, when I
post data, the model contains null for the field linked to the
dropdownlist even if I include an hidden field with the Id.
Here is some of my code to help understand my issue.
Any idea of how I could include disabled values of my dropdown list in the model when I post data ?
Thanks for any help !
View :
<div class="col-md-3">
#Html.DropDownListFor(model => model.Currency.Id, (SelectList)ViewBag.Currencies, new { #class = "form-control ignore-desactivated" })
#Html.ValidationMessageFor(model => model.Currency, "", new { #class = "text-danger" })
</div>
JS :
$(function () {
$('form').validate().settings.ignore = '.ignore-desactivated';
});
Source when edition :
<div class="col-md-3">
<select class="form-control ignore-desactivated" data-val="true" data-val-number="The field Id must be a number." data-val-required="The Id field is required." id="Currency_Id" name="Currency.Id">
<option value="-1"></option>
<option disabled="disabled" value="9">Angolan kwanza (desactivated)</option>
<option value="10">Argentine peso</option>
<option disabled="disabled" selected="selected" value="1">Euro (desactivated)</option>
<option disabled="disabled" value="56">Gibraltar pound (desactivated)</option>
<option value="3">Great Britain Pound</option>
</select>
<span class="field-validation-valid text-danger" data-valmsg-for="Currency" data-valmsg-[replace][1]="true"></span>
</div>
My model when I want to save data :
https://i.stack.imgur.com/jQ9aH.png
... and I found an answer a few minutes after asking it (thanks to my colleagues)...
I don't know if that's correct, but a little js code to remove disabled items before the post did the trick :
//Delete disabled elements of lists before submit
$('form').submit(function () {
$('.ignore-desactivated').each(function () {
$(this).children().each(function () {
$(this).removeAttr('disabled');
});
})
})

AngularJS input from mongoose module decimals

I have a mongoose model containing a salesPrice type number. Since mongoose doesn't support decimals I created a setter and a getter like this
function getSalesPrice(num){
if (num)
return (num/100).toFixed(2);
else
return (0).toFixed(2);
}
function setSalesPrice(num){
return num*100;
}
In Angular view i want to view value with decimals, so e.g. I store 99 in model meaning $0.99
only in my form will show as 99
<label for="salesPrice" class="col-md-2 control-label">Salesprice</label>
<div class="col-md-10">
<input type="number" class="form-control" data-ng-model="asset.salesData.salesPrice" id="salesPrice" placeholder="Sales price in USD">
</div>
What's way to work with this correctly?
If I understand your need correctly, then the best way to solve this problem is to use angular's built-in 'currency' filter. If you have a $resource that returns values in decimal format from e.g. mongoose, then you can display the values like:
<div ng-repeat="value in values">
{{ value | currency }}
</div>
And achieve a locale specific (based on the browser viewing it) result.
here is an example plnkr showing this off (without a mock resource)

Resources