Using react multi select to choose a users interests that is saved in mongoDB. The issue is that I want the users already selected choices to be reflected within the multi-select even if i refresh. As currently if you refresh the page it clears the selections and if you submit changes with a blank interest spot, it creates an empty interest array.
user model
const model = mongoose.Schema(
{
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
role: {
type: String,
enum: [roles.ADMIN, roles.STUDENT, roles.ALUMNI],
},
name: {
type: String,
required: true,
},
major: {
type: String,
required: true,
},
minor: {
type: String,
required: false,
},
interests: {
type: Array ,
required: false,
default : []
},
profilePic: {
type: String,
required: false,
default : "/images/default.png"
}
},
{ timestamps: true }
);
module.exports = new mongoose.model("User", model);
How its stored in mongo
Code for multi-select and things involved with it
<Form.Group className="mb-3" controlId="formBasicPassword">
<Form.Label>Interests</Form.Label>
<Select
isMulti
name="colors"
options={colourOptions}
className="basic-multi-select"
classNamePrefix="select"
styles={customStyles}
value={colourOptions.filter(obj => selectedValue.includes(obj.value))} // set selected values
onChange={handleChange}
/>
</Form.Group>
const [selectedValue, setSelectedValue] = useState([]);
const handleChange = (e) => {
setSelectedValue(Array.isArray(e) ? e.map(x => x.value) : []);
}
const customStyles = {
option: (provided, state) => ({
...provided,
color: state.isSelected ? 'red' : 'black',
padding: 5,
})
}
const colourOptions = [
{ value: "Artificial Intelligence", label: "Artificial Intelligence" },
{ value: "Product Management", label: "Product Management" },
{ value: "Mental Health", label: "Mental Health" },
{ value: "Physics", label: "Physics" },
{ value: "Statistics", label: "Statistics" },
{ value: "Technology", label: "Technology" },
{ value: "Finance", label: "Finance" },
];
const handleSubmit = (e) => {
e.preventDefault();
const form = e.currentTarget;
e.stopPropagation();
setValidated(true);
if (form.checkValidity() === false) {
return;
}
const user = {
name: nameRef.current.value,
email: emailRef.current.value,
role: roleRef.current.value,
major: majorRef.current.value,
minor: minorRef.current.value,
interests: selectedValue,
profilePic: profilePicRef.current.value,
};
onSubmit(user);
};
visual representation of multi-select
Everything is working except loading the users already selected interests into the drop down multi select so that it doesn't clear a users interest if they don't re-choose their interests while changing something else and submitting it. Tried to do something with default values but the code value={colourOptions.filter(obj => selectedValue.includes(obj.value))} // set selected values is kind of messing with doing it that way.
Related
Maybe this is a simple fix, but I can't seem to figure out what I'm doing wrong here.
I have a table that lists all the states
Model:
static get jsonSchema() {
return {
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string', minLength: 1, maxLength: 100 },
},
}
}
static get relationMappings() {
return {
users: {
relation: Model.HasManyRelation,
modelClass: User,
join: {
from: `${tableNames.state}.id`,
to: `${tableNames.user}.state_id`,
},
},
}
Migration:
await knex.schema.createTable(tableNames.state, (table) => {
table.increments().primary().notNullable()
table.string('name', 100).notNullable()
User table model:
static get jsonSchema() {
return {
type: 'object',
properties: {
id: { type: 'integer' },
first_name: { type: 'string', minLength: 1, maxLength: 100 },
last_name: { type: 'string', minLength: 1, maxLength: 100 },
state_id: { type: 'integer', default: null },
},
}
}
static get relationMappings() {
return {
state: {
relation: Model.BelongsToOneRelation,
modelClass: State,
join: {
from: `${tableNames.user}.state_id`,
to: `${tableNames.state}.id`,
},
}
}
}
User table migration:
await knex.schema
.createTable(tableNames.user, (table) => {
table.increments().primary().notNullable()
table.string('first_name', 100).notNullable()
table.string('last_name', 100).notNullable()
table.integer('state_id').unsigned()
table
.foreign('state_id')
.references('id')
.inTable(tableNames.state)
.onDelete('SET NULL')
})
Now the issue: I want the state_id column to be nullable, as in not every user will have a state assigned to them. But when I try inserting a user with no state_id, I get this: insert or update on table \"user\" violates foreign key constraint \"user_state_id_foreign\".
two things you are doing wrong
in your json schema define your column as state_id: {type: ['integer', 'null']}
in your user migrations make table.integer('state_id').unsigned().nullable()
I am using tcomb forms to create my react-native forms. Within the form structure there are 3 dropdown selectors: birthdate, gender, and country. I am testing the component using jest and I cannot find a way to select the dropdown form sections. Currently I am trying this:
const firstNameInput = getByLabelText('First Name');
const lastNameInput = getByLabelText('Last Name');
const birthDateInput = getByLabelText('Date of Birth');
const genderInput = getByLabelText('Gender');
const countryInput = getByLabelText('Country');
const bioInput = getByLabelText('Bio');
const zipInput = getByLabelText('Zip');
This does not find any instance of the label, even though I have the label set as such:
const Birthdate = t.refinement(t.Date, (d: Date) => isValidBirthdate(d));
const Name = t.refinement(t.String, (n: string) => isValidName(n));
const ZipCode = t.refinement(t.String, (z: string) => isValidZipCode(z));
const AboutYouStruct = t.struct({
birthdate: Birthdate,
gender: t.enums.of(
Object.values(genders).map(g => g!.value),
'gender'
),
country: t.enums.of(
countries.map(c => c.value),
'country'
),
firstName: Name,
lastName: Name,
zipCode: ZipCode,
userBio: t.String
});
const FORM_OPTIONS = {
stylesheet: Widgets.tCombStylesheet,
fields: {
firstName: {
label: 'First Name',
placeholder: '',
error: Strings.invalidFirstNameWarning
},
lastName: {
label: 'Last Name',
placeholder: '',
error: Strings.invalidLastNameWarning
},
birthdate: {
mode: 'date',
label: 'Date of Birth',
error: Strings.invalidBirthDateWarning,
config: {
placeholder: ''
}
},
gender: {
label: 'Gender',
error: Strings.invalidGenderWarning,
config: {
items: Object.values(genders),
placeholder: 'Select a gender'
}
},
country: {
label: 'Country',
error: Strings.invalidCountryWarning,
config: {
items: countries,
placeholder: 'Select a country'
}
},
zipCode: {
label: 'Zip',
placeholder: '',
error: Strings.invalidZipCodeWarning
},
userBio: {
label: 'Bio',
placeholder: '',
error: Strings.emptyWarning,
type: 'textarea',
multiline: true,
numberOfLines: 3,
stylesheet: {
...Widgets.tCombStylesheet,
textbox: {
...Widgets.tCombStylesheet.textbox,
normal: {
...Widgets.tCombStylesheet.textbox.normal,
height: Widgets.tCombStylesheet.textbox.normal.height * 3,
textAlignVertical: 'top'
},
error: {
...Widgets.tCombStylesheet.textbox.error,
height: Widgets.tCombStylesheet.textbox.error.height * 3,
textAlignVertical: 'top'
}
}
}
}
}
};
Render form
<Form
ref={formRef}
type={AboutYouStruct}
value={form}
onChange={(f: AboutYouEntity) => setForm(f)}
options={formOptions}
/>
Form Component
import t from 'tcomb-form-native';
import DatePickerTemplate from './templates/DatePickerTemplate';
import SelectTemplate from './templates/SelectTemplate';
/**
* Instantiate tcomb-form-native with custom templates
*/
const { Form } = t.form;
Form.templates.datepicker = DatePickerTemplate;
Form.templates.select = SelectTemplate;
export default Form;
I have the following models defined:
var Order = sequalize.define(
"order",
{
id: {
type: Sequelize.STRING,
primaryKey: true,
},
menuId: {
type: Sequelize.UUID,
field: "menu_id",
},
},
{
timestamps: false,
}
);
Item.belongsToMany(Order, { through: OrderItem });
Order.belongsToMany(Item, { through: OrderItem });
and
var OrderItem = sequalize.define(
"order_item",
{
orderId: {
type: Sequelize.UUID,
field: "order_id",
},
itemId: {
type: Sequelize.UUID,
field: "item_id",
},
count: {
type: Sequelize.INTEGER,
field: "count",
},
},
{
timestamps: false,
freezeTableName: true,
}
);
I am trying to figure out how to add a order with items without creating items but just adding them to the relationship.
I have this initial format for the order:
{
"id": "som-other-id7",
"items": [{"id": "727f9b52-a88b-4ec3-a68c-98d190564497", "count": 2}, {"id": "7dfd30e7-2d4a-4b16-ae3d-20a330d9b438"}],
"menuId": "7715af03-968f-40e5-9eb2-98016f3deeca"
}
and I try to add it to the db in the following way:
Order.create(orderJson)
.then((order) =>
orderJson.items.map((item) => order.addItem(item.id, { count: item.count }))
)
However the count is not populated. I tried:
using setItem instead of addItem
instead of passing item.id passing {itemId, orderId}
You should call addItem like this:
order.addItem(item.id, { through: { count: item.count }})
See an example in BelongsToMany section
i need to work with a mongodb record like this one:
{
"_id": {
"$oid": "5c09ae281646e8d8bad07d73"
},
"name": "somename",
"genre": "somegenre",
"indexes": {
"index1": 1,
"index2": 7,
"index3": 7,
"index4": 7,
"index5": 7,
"index6": 7,
"index7": 7,
"index8": 7,
"index9": 7,
"index10": 7,
"index11": 7,
"index12": 9,
"index13": 5,
"index14": 15,
"index15": 8,
"index16": 17
},
"date": {
"$date": "2018-12-06T23:18:00.532Z"
},
"__v": 0
}
And them i set the Schema for mongoose like this
const ItemSchema = new Schema({
name: {
type: String,
required: true
},
genre: {
type: String,
required: true
},
indexes: {
type: Object,
required: true,
properties: {
index1: {
type: Number,
required: true
},
index2: {
type: Number,
required: true
},
index3: {
type: Number,
required: true
},
index4: {
type: Number,
required: true
},
index5: {
type: Number,
required: true
},
index6: {
type: Number,
required: true
},
index7: {
type: Number,
required: true
},
index8: {
type: Number,
required: true
},
index9: {
type: Number,
required: true
},
index10: {
type: Number,
required: true
},
index11: {
type: Number,
required: true
},
index12: {
type: Number,
required: true
},
index13: {
type: Number,
required: true
},
index14: {
type: Number,
required: true
},
index15: {
type: Number,
required: true
},
index16: {
type: Number,
required: true
}
}
},
them my router post (express)
router.post("/", (req, res) => {
const newItem = new Item({
name: req.body.name,
genre: req.body.genre,
indexes: {
index1: req.body.indexes.index1,
index2: req.body.indexes.index2...
them the inputs to set the index values (where im using the name property to defined how i will set the new item):
<input
type="text"
name="indexes.index1"
placeholder="sample 1"
onChange={this.onChange}
/>
and the functions
onChange = e => {
this.setState({ [e.target.name]: e.target.value });
};
and
onSubmit = e => {
const newItem = {
name: this.state.name,
genre: this.state.genre,
indexes: {
index1: this.state.indexes.index1,
...
the result them in the MongoDB is:
{
"_id": {
"$oid": "5c0c22d41e3d5115dda930aa"
},
"name": "name",
"genre": "somegen",
"indexes": {
"index1": "",
"index2": ""
no values in the indexes
Any idea? suggestions?
Thanks guys.
Seba
The problem lies in your setState ReactJS call.
onChange = e => {
this.setState({ [e.target.name]: e.target.value });
};
The target.name is always a string, so what you are doing is that you are setting a property with a key that contains a . ( indexes.index1 ), so your state after onChange event would look like :
state = {
"name": "name",
"genre": "somegen",
"indexes.index1": "1",
"indexes.index2": "2",
...
}
This is not being recognised by express, and your request fails to deliver the proper body.
You can achieve what you want by having the indexes somehow converted to an object. Simplest way with your setup would be :
onChange = e => {
if ( e.target.name.indexOf( "indexes" ) === 0 ) {
this.setState({ indexes: Object.assign( this.state.indexes || {}, {
[e.target.name.replace(/^indexes\./, "")]: e.target.value
} ) } );
} else {
this.setState({ [e.target.name]: e.target.value });
}
};
If your application structure allows, you can move the inputs that define the indexes in another component that would have as a callback the whole indexN object. This will help you to simply update the state without having to parse the index key.
I am using Mongoose 5.1.7 and attempting to create a compound index across multiple text indexes in my defined schema. Here is my schema definition:
const mongoose = require('mongoose');
const alumniSchema = mongoose.Schema({
firstName: {
type: [String],
required: true
},
lastName: {
type: [String],
required: true
},
classYear: {
type: Number,
required: true
},
photoURL: {
type: String,
},
education: [
{
school: {
type: String,
required: true
},
gradYear: {
type: Number,
required: true
},
degreeType: String,
degreeSubject: String,
}
],
jobs: [
{
employer: {
type: String,
required: true
},
position: String,
startDate: Date,
endDate: Date,
isCurrent: Boolean
}
],
contactInfo: {
phoneNumber: {
type: String,
},
email: {
type: String,
}
},
})
alumniSchema.index({ firstName: 'text', lastName : 'text', email: 'text' });
module.exports = mongoose.model('Alumni', alumniSchema);
When I boot up the server, I receive the following error:
MongoError: Index: { v: 2, key: { _fts: "text", _ftsx: 1 }, name: "firstName_text_lastName_text_email_text", ns: "5b3be578c0c6e317f7c1bc2b_test.alumnis", background: true, weights: { email: 1, firstName: 1, lastName: 1 }, default_language: "english", language_override: "language", textIndexVersion: 3 } already exists with different options: { v: 2, key: { _fts: "text", _ftsx: 1 }, name: "firstName_text_lastName_text_classYear_text_education.school_text", background: true, weights: { classYear: 1, education.school: 1, firstName: 1, lastName: 1 }, default_language: "english", language_override: "language", ns: "5b3be578c0c6e317f7c1bc2b_test.alumnis", textIndexVersion: 3 }
I have been messing around with this for a while and evidently previously created an index. When I use the mongo shell to check the indexes that I currently have set up, however, I cannot find the index "firstName_text_lastName_text_classYear_text_education.school_text" referenced by the error message:
> db
test
> db.collection.getIndexes()
[ ]
I am at an impasse--I'm not sure if I've incorrectly created the index, or if I am supposed to drop the index (it doesn't look like Mongoose natively supports a dropIndex() function).
Has anyone else dealt with this issue? Thanks!
Looks like Mongoose dynamically creates the index at runtime. The trick for me was adding:
var Alumni = mongoose.model('Alumni', alumniSchema);
Alumni.collection.dropIndex('firstName_text_lastName_text_classYear_text_education.school_text', function(err, result) {
if (err) {
console.log('Error dropping index!', err);
}
});
and then restarting the server.
THEN I was able to change the index to whatever I wanted. Note that I still needed to add the above code segment and restart the server every time I wanted to update the index.