React: Setting state with array of objects - ruby-on-rails

Mission:
This section is to illustrate fully what I'm trying to do, and the code I'm working with.
I'm trying to set the state of a SearchContainer component using an ajax call. The data that gets returned looks something like this:
[
{
"id":903,
"name":"Adrian College",
"url":"https://en.wikipedia.org/wiki/Adrian_College",
"nickname":"Bulldogs",
"city":"Adrian",
"state":"Michigan",
"conference":"Michigan Intercollegiate Athletic Association",
"athletics_url":null,
"enrollment":20000,
"selectivity":"High",
"slug":null,
"college_sports":[
{
"sport":{
"id":71,
"name":"Softball",
"gender":"Female",
"division":"Division III"
}
},
{
"sport":{
"id":68,
"name":"Volleyball",
"gender":"Female",
"division":"Division III"
}
}
]
}
]
I'm getting this information with the following ajax (within the SearchContainer component), which I then try to set the state of filteredColleges with the data returned (referenced above):
$.ajax({
url: `/api/search/colleges.json/?query=${query}`,
method: 'GET',
success: (data) => {
this.setState({ filteredColleges: data });
}
});
I'll be updating my SearchResults component like so:
<SearchResults filteredColleges={ this.state.filteredColleges }/>
Within SearchResults, I render the following:
const colleges = this.props.filteredColleges.map( function(college) {
return <College college={college} key={college.id}/>
});
And within each College component, I want to be able to reference the list of sports that belong to that college.
My Problem:
When I try to set the state of filteredColleges within my SearchContainer component...
this.setState({ filteredColleges: data });
I get the following error:
Uncaught Error: Objects are not valid as a React child (found: object with keys {sport}).
I've tried parsing the data many different ways, but no luck. I'm pretty sure this means that I can't have the sport objects within my data array? Am I interpreting this error correctly? Is there any way to get around this?
Edit #1: Sharing my College component
import React from 'react';
export default class College extends React.Component{
constructor(props) {
super(props);
}
render() {
const { name, nickname, conference, url, enrollment, selectivity, city, state } = this.props;
return (
<tr>
<td>{ this.props.college.name }</td>
<td>{ this.props.college.nickname }</td>
<td>{ this.props.college.conference }</td>
<td>{this.props.college.url}</td>
<td>{ this.props.college.enrollment }</td>
<td>{ this.props.college.selectivity }</td>
<td>{ this.props.college.city }</td>
<td>{ this.props.college.state }</td>
<td>{ this.props.college.college_sports }</td>
</tr>
)
}
}

I think the problem is that your College component is outputting objects from the college_sports array directly, something like:
const college_sports = this.state.college.college_sports
<ul>
{ college_sports.map(sport=> <li>{sport}</li>) }
</ul>
But you can't output whole objects, like the error says. Try something like:
const college_sports = this.state.college.college_sports
<ul>
{ college_sports.map(sport=> <li>{sport.name}</li>) }
</ul>

What is happening here is that you have multiple objects with the same name in an array. You could change your api to give you an array with no object names or unique object names for this section.

Related

how to retrieve form values and labels from react hook form and antd Select

I am using antd Select and react hook form via 'Controller'. I am populating the Select options from a fetched data with structure;
{
{
"id": "232342",
"term": "hello"
}
{
"id": "232342",
"term": "hello"
}
}
the Select component properly displays the term for selection. However, i want to retrieve both the 'id'and 'term' of the selected and use it to populate another json object.
getValues(" ") retrieves the 'id' only. How do i retrieve and access both the 'id' and 'term'.
Here is a portion of code:
import React from 'react'
import { useForm, Controller } from 'react-hook-form'
import { Select } from 'antd'
const { Option } = Select
export default function PatientRegistrationForm({ options }) {
const { register, handleSubmit, getValues, control, formState: { errors } } = useForm({
defaultValues: {
customer: "",
}
})
const children = []
for (const {id, pt: {term}} of options){
children.push(<Option key={id}>{term}</Option>)
}
// Define the retrieved values from the form
const retrievedID = getValues("customer")
// Use the retreived values to populate this object
const customer = {
customerId = retrievedID
customerName = "nothing happens here"
},
return (
<div className="">
<form onSubmit={handleSubmit(onSubmit)} className="">
<section>
<Controller
control={control}
name="customer"
render={({ field }) => (
<Select {...field} defaultValue=""
bordered={true}
filterOption={true}
className="form-control"
>
{ children }
</Select>
)}
/>
</section>
</form>
</div>
);
}
Thanks in advance for your help.
You have to retrieve the option manually using something like:
const retrievedID = getValues("customer")
const retrievedOption = options.find(option => option.id === retrievedID)
const customer = {
customerId: retrievedID,
customerName: retrievedOption.term
}
thank you #sgarcia.dev for your answer. I know its been a while but i want to put it out here incase it helps someone else. It turns out it had little to do with react hook form. Ant design select component has a prop 'labelInValue' which returns an object containing both the label and value.

How to display data from Firebase Database to Vuetify Datatable with VueJs

I have a problem on my code. I can't display my data to Vuetify Datatable, Although
it is enable Read and write on my Firebase Database:
Anyway here is my code for that, any wrong with my code? Comments were greatly appreciated:
<template v-for="user in users">
<v-data-table
v-bind:headers="headers"
:items="user"
hide-actions
class="elevation-1"
>
<template slot="items" scope="props">
<td>{{ users.title }}</td>
<td class="text-xs-right">{{users.description }}</td>
</template>
</v-data-table>
</template>
import * as firebase from 'firebase'
let config = {
//config here.....
}
let app = firebase.initializeApp(config);
let db = app.database();
let userRef = db.ref('users');
export default{
firebase:{
users:userRef
}
}
You need the data () function to return the object that is holding your data.
export default {
data () {
return {
// data for component here
}
}
}
By iterating over the template you would be creating multiple data tables. The data table component itself handles iterating over the items.
The array of objects you pass in to the :items will be available through the scope="props", the string assigned to scope can be named something besides props if you prefer. Your data will be in the props.item object. Also make sure your header array is defined somewhere.
<template>
<v-data-table
v-bind:headers="headers"
:items="exampleData" // pass the array of objects you want in the table here.
hide-actions
class="elevation-1"
>
<template slot="items" scope="props">
<td>{{ props.item.user}}</td>
<td class="text-xs-right">{{props.item.title}}</td>
</template>
</v-data-table>
</template>
import * as firebase from 'firebase'
let config = {
//config here.....
}
let app = firebase.initializeApp(config);
let db = app.database();
let userRef = db.ref('users');
export default{
data () {
return {
firebase:{
users:userRef
},
exampleData: [
{'user': 'Neo', 'title': 'CEO'},
{'user': 'Trinity', 'title': 'CTO'}
],
headers: [
{text: 'User', value: 'user', align: 'left'},
{text: 'Title', value: 'title', align: 'left'}
]
}
}
}

Adding data to a DB using an Angular 2 service, UI not refreshing

First timer here....be gentle. I've searched quite a bit and didn't find anything that specifically seemed to address this problem. We have adopted Angular2 for a Singe Page Application. I have a page that has a data entry block with a grid below it. The page calls a "get" service to fill the grid upon loading. Clicking the submit button in the data entry area calls an "add" service that executes a stored proc and inserts the data into a postgre sql db. All good. Except, I'm struggling to get the grid to refresh to show the row that was newly added (even tried calling the "Get" service after the "Add"). All of the examples I've seen to date only use local arrays as the data stores (pop, push to manipulate the data). As our data is being persisted in a database, these examples don't quite get me all the way there. IContent is an interface that models the data. _contentList is an array of IContent and populated by the "Get" service. Any help appreciated!
Update: per JB's suggestion, I commented out the caching code in the get service and added an explicit call to the get service following the add >service call. Still got the same behavior.
MainComponent:
import {Component} from 'angular2/core';
import {IContent} from '../../classes/interfaces';
import {ContentService} from '../../services/content.service';
...
import {OnInit} from 'angular2/core';
import {ControlGroup, FormBuilder, Validators} from 'angular2/common';
import {Observable} from 'rxjs/Observable';
#Component({
selector: 'view',
templateUrl: 'src/views/the-view.html',
providers: [ContentService],
directives: [ROUTER_DIRECTIVES, MdToolbar, MdButton, MdCard, MD_LIST_DIRECTIVES, MdInput],
})
export class ContentMgmtComponent {
public _contentList: IContent[];
myForm: ControlGroup;
contentAdded: boolean = false;
constructor(private _formBuilder: FormBuilder, private _contentService: ContentService) {
// Programmatically build out form
this.myForm = _formBuilder.group(
{
pageID: ["", Validators.compose([Validators.required])],
zoneID: ["", Validators.required],
contentText: ["", Validators.compose([Validators.required, Validators.maxLength(10)])],
startDate: ["", Validators.required],
endDate: ["", Validators.required]
});
// Get existing pages / content
this._contentService.getAllContent()
.subscribe((content: IContent[]) => {
this._contentList = content[0]["contentInfo"];
});
}
//Add the new content to the database.
onAddContentClick(pageId: string, zoneId: string, contentText: string, startDate: string, endDate: string) {
this._contentService.addContent(pageId, zoneId, contentText, startDate, endDate)
this.contentAdded = true;
// *** PROBLEM BE HERE ... tried calling the Get Service, etc. ***
}
}
Portion of the the-view.html that should refresh:
<div class="panel panel-primary">
<div class="panel-heading"><h4>Nova Custom Content Manager</h4> </div>
<div class="panel-body">
<table class="col-md-12 table-bordered table-striped table-hover table-condensed">
<thead>
<tr>
<th>Content Id</th>
<th>Page Id</th>
<th>Zone Id</th>
<th>Content</th>
<th>Active FL</th>
<th>Start Date</th>
<th>End Date</th>
</tr>
</thead>
<tbody>
<tr class="info" *ngFor="#contentItem of _contentList">
<td>{{contentItem.contentID}}</td>
<td>{{contentItem.pageID}}</td>
<td>{{contentItem.zoneID}}</td>
<td>{{contentItem.contentText}}</td>
<td>{{contentItem.activeFlag}}</td>
<td>{{contentItem.startDate}}</td>
<td>{{contentItem.endDate}}</td>
</tr>
</tbody>
</table>
</div>
Services:
import {Injectable} from 'angular2/core';
import {Http, Response, Headers, RequestOptions, URLSearchParams} from 'angular2/http';
import {Observable} from 'rxjs/Rx';
import {IContent} from '../classes/interfaces';
import {Observer} from 'rxjs/Observer';
#Injectable()
export class ContentService {
content: IContent[]; //returned by the actual service call to the consumer
constructor(private http: Http) {
}
addContent(_pageId: string, _zoneId: string, _content: string, _startDate: string, _endDate: string) {
let body = JSON.stringify({ pageID: _pageId, zoneID: _zoneId, contentText: _content, activeFlag: "true", startDate: _startDate, endDate: _endDate });
let headers = new Headers({ 'content-type': 'application/json' });
let options = new RequestOptions({ headers: headers });
this.http.post('http://whereever.com/api/addcontent', body, options)
.subscribe(
data => console.log(data),
err => console.log(err.json().message),
() => console.log('Authentication Complete')
);
}
getAllContent(): Observable<IContent[]> {
if (!this.content) {
//return this.http.get("/src/services/content.json")
return this.http.get("http://whereever.com/api/getallcontents")
.map((res: Response) => {
this.content = res.json();
return this.content;
})
.catch(this.handleError);
}
else {
//return cached data
return this.createObservable(this.content);
}
}
private extractData(res: Response) {
if (res.status < 200 || res.status >= 300) {
throw new Error('Bad response status: ' + res.status);
}
let body = res.json();
return body.data || { };
}
**strong text**...
}
Your service returns cached data. It shouldn't.
Make it make an http request every time it's called, not just the first time. Otherwise, obviously, it always returns the same, stale data.
Just a shot in the dark, but you are replacing your content list from that returned by the service. Maybe try clearing your bound list and adding the content returned from the service. I'm assuming that the binding to the list will not work if you replace the entire list with a new instance.
this._contentList = content[0]["contentInfo"]; // clear this._contentList and rebuild it from content[0]["contentInfo"]

Accessing values in ViewModel in a dxList - PhoneJS

So in my PhoneJS web app, I have a dxList widget, with checkboxes on each item. I want to be able to select multiple items, and then do something with them. I'm trying to bind the 'checked' binding to an observable, but I get an 'undefined' error.
Here's the code for the dxTemplate for the list
<div data-options="dxTemplate:{name:'item'}">
<span data-bind="text: $data.information"></span>
<div data-bind="dxCheckBox: { checked: check_boxes }"></div>
</div>
The problem is that check_boxes is in the viewModel, not the item array. I need to access values in the viewModel. I've tried viewModel.check_boxes, but with no success.
Here's the js code:
AppNamespace.otherView = function (params) {
var viewModel = {
my_list: [
{
key: 'Group 1',
items: [
{ information: 'Test 1' },
{ information: 'Test 2'},
{ information: 'Test 3' }
]
}
],
check_boxes: ko.observable(false),
//...etc
Has anyone had any experience with this, and is there a solution?
Thanks!
Knockout provides special properties to access parent binding contexts. In your case both $parent and $root should work.
More on this topic in Knockout docs: Binding context.

How to bind kendo mvc ui dropdownlist dynamically

I am working on asp.net mvc with Kendo UI mvc. I have two kendo dropdown lists. one for list of clinics and another of list of patients in selected clinic. But there is no direct relationship between clinic and patient to use the cascading dropdownlist. for that i have used ajax calls in dropdownlist change event and get list of patients. and this is my first dropdownlist for list clinics
#(
Html.Kendo().DropDownList()
.Name("ddlClinics")
.Events(e=>e.Change("ChangeClinic"))
.BindTo(new SelectList((List<Account.Entities.Clinic>)ViewBag.lstClinic,
"ClinicID", "ClinicName")))
and this is my second dropdownlist for listpatients
#(
Html.Kendo().DropDownList()
.Name("ddlPatients")
.BindTo(new SelectList((List<Patient>)ViewBag.Patients,
"PatId", "PatName"))))
I want to dynamically bind the list of patients to second dropdownlist when the first dropdownlist changes,
function ChangeClinic()
{
$.ajax({
url: '/Messages/GetPatient',
type: 'Post',
data: { email: '#User.Identity.Name' },
cache: false,
success: function (result) {
var ddlPatients = $('#ddlPatients').data('kendoDropDownList');
var main = [];
$.each(result, function (k, v) {
main.push({ "PatId": v.PatId, "PatName": v.PatName });
});
ddlPatients.dataTextField = "PatName";
ddlPatients.dataValueField = "PatId";
ddlPatients.dataSource.data(main);
ddlPatients.reload();
}
});
}
i am able to bind the list to dropdownlist but all items are shows as 'undefined'. so please guide me.
From what I can tell, there is a relationship between clinics and patients so you should be able to use the CascadeFrom("DropDownList1") provided in the wrappers. We use a cascading dropdownlist in a similar fashion for the relationship between school districts and schools:
#(Html.Kendo().DropDownList()
.Name("District")
.HtmlAttributes(new { style = "width:300px;" })
.BindTo(ViewBag.districts)
.DataTextField("DistrictName")
.DataValueField("DistrictID")
.OptionLabel("Select District")
)
#(Html.Kendo().DropDownList()
.Name("School")
.HtmlAttributes(new { style = "width:300px;" })
.CascadeFrom("District")
.BindTo(ViewBag.schools)
.DataTextField("SchoolName")
.DataValueField("SchoolID")
.OptionLabel("Select School")
)
If you want fill second DropDown on basis of first DropDown value.
Telerik Provided,
.CascadeTo("DropDownList2")
Please see following link for detailed information.
Cascading of Dropdown in Telerik dropdownlist
Instead of creating such array which is useless to the dataSource use:
success: function (result) {
var ddlPatients = $('#ddlPatients').data('kendoDropDownList');
var main = [];
$.each(result, function (k, v) {
main.push({ "text": v.PatId, "value": v.PatName });
});
ddlPatients.dataSource.data(main);
}
});
If you are not using
.DataSource(source =>
{
source.Read(read =>
{
read.Action ("FunctionName", "ControllerName").Data("filterDropdown1");
}).ServerFiltering(true);
})
.CascadeFrom("Dropdown1")
properties in the definition of second dropdown and you are using the definition mentioned in question above. i.e:-
#(
Html.Kendo().DropDownList()
.Name("ddlPatients")
.BindTo(new SelectList((List<Patient>)ViewBag.Patients,"PatId", "PatName"))
)
then you can bind the data to the 2nd dropdown directly in the success function of ajax post.
function ChangeClinic()
{
$.ajax({
url: '/Messages/GetPatient',
type: 'Post',
data: { email: '#User.Identity.Name' },
cache: false,
success: function (result) {
$('#ddlPatients').data('kendoDropDownList').dataSource.data(result);
//ddlPatients.reload();
}
});
}
#Note:- 1) The result value should contain the list of new patients with properties "PatId" and "PatName" based on the parameter email passed to the function "Messages" in GetPatient controller, and there will be no need for ddlpatients.reload(), infact .reload() is not supported, it will break the execution, so don't use .reload().

Resources