How to remder Array in vaadin grid from Hilla application? - vaadin-grid

What is the best approach to display an array in a the cell of vaadin grid?
I model a list of values like this in springboot.
private List<String> responsibilities;
If responsibilities contains ["Cut grass", "Wash dishes", "Clean floor"] I would like to render this as a bullet list in the cell:
Cut grass
Wash dishes
Clean floor
Sorry my typescripts skills a poor.
Checked docs and examples and had a try with cell rendering but was not very obvious how to setup the array display.
This attempt gives exception ERROR: Invalid binding pattern!
import '#vaadin/grid';
import { columnBodyRenderer } from '#vaadin/grid/lit.js';
import type { GridColumnBodyLitRenderer } from '#vaadin/grid/lit.js';
import { html } from 'lit';
import { Binder, field } from '#hilla/form';
import { customElement, state } from 'lit/decorators.js';
import { View } from '../../views/view';
import Career from 'Frontend/generated/com/example/application/models/Career';
import CareerModel from 'Frontend/generated/com/example/application/models/CareerModel';
import { getCareers } from 'Frontend/generated/CareerEndpoint';
#customElement('career-view')
export class CareerView extends View {
#state()
private careers: Career[] = [];
private binder = new Binder(this, CareerModel);
async firstUpdated() {
this.careers = await getCareers();
}
render() {
return html`
<div>
<vaadin-grid theme="wrap-cell-content" .items=${this.careers}>
<vaadin-grid-column path="title"> </vaadin-grid-column>
<vaadin-grid-column path="aliases"> </vaadin-grid-column>
<vaadin-grid-column path="description"> </vaadin-grid-column>
<vaadin-grid-column
header="Responsibility"
${columnBodyRenderer(this.responsibilityRenderer, [])}
></vaadin-grid-column>
<vaadin-grid-column path="experience"> </vaadin-grid-column>
</vaadin-grid>
</div>
`;
}
responsibilityRenderer: GridColumnBodyLitRenderer<Career> = (this.careers) => {
return html`
<ul>
${careers.responsibilities.map(r => html`
<li>${r}</li>
`)}
</ul>
`;
};
}

You are on the right track with using a cell renderer. See the docs here: https://vaadin.com/docs/latest/components/grid/#content
TypeScript code is allowed in Lit bindings, thus you can e.g. use array.map function to iterate over array and generate list or repeated elements.
Your renderer method would look something like this:
responsibilityRenderer: GridColumnBodyLitRenderer<Person> = (person) => {
return html`
<ul>
${person.responsibilities.map(r => html`
<li>${r}</li>
`)}
</ul>
`;
};

Note the fiddling with typescript undefined messages using undefined and '?' at various locations. Data displays ok now no console errors.
Thanks to Marcus & Tatu for the help!
import '#vaadin/grid';
import { columnBodyRenderer } from '#vaadin/grid/lit.js';
import type { GridColumnBodyLitRenderer } from '#vaadin/grid/lit.js';
import { html } from 'lit';
import { Binder, field } from '#hilla/form';
import { customElement, state } from 'lit/decorators.js';
import { View } from '../../views/view';
import Career from 'Frontend/generated/com/example/application/models/Career';
import CareerModel from 'Frontend/generated/com/example/application/models/CareerModel';
import { getCareers } from 'Frontend/generated/CareerEndpoint';
#customElement('career-view')
export class CareerView extends View {
#state()
private careers: (Career | undefined)[] = [];
async firstUpdated() {
this.careers = await getCareers();
}
render() {
return html`
<div>
<vaadin-grid theme="wrap-cell-content" .items=${this.careers}>
<vaadin-grid-column path="title"> </vaadin-grid-column>
<vaadin-grid-column path="aliases"> </vaadin-grid-column>
<vaadin-grid-column path="description"> </vaadin-grid-column>
<vaadin-grid-column
header="Responsibility"
${columnBodyRenderer(this.responsibilityRenderer, [])}
></vaadin-grid-column>
<vaadin-grid-column path="experience"> </vaadin-grid-column>
</vaadin-grid>
</div>
`;
}
responsibilityRenderer: GridColumnBodyLitRenderer<Career> = (item, _model, column) => {
return html`
${item?.responsibilities?.map(r => html`
<li>${r}</li>
`)}
`;
};
}

Related

Apollo GraphQL pass object

In GraphQL, how do I pass an object instead of a string?
Take this code from Apollo's website as an example, with my minor change:
import React, { useState } from 'react';
import { useLazyQuery } from '#apollo/client';
function DelayedQuery() {
const [dog, setDog] = useState(null);
const [getDog, { loading, data }] = useLazyQuery(GET_DOG_PHOTO);
if (loading) return <p>Loading ...</p>;
if (data && data.dog) {
setDog(data.dog);
}
const myObject = {
type: {
favors: [
tom: true,
bill: false
]
}
}
return (
<div>
{dog && <img src={dog.displayImage} />}
<button onClick={() => getDog({ variables: { theObject: myObject } })}>
Click me!
</button>
</div>
);
}
I believe React is trying to parse the object into a string, but (as the error message explains) JSON.stringify cannot serialize cyclic structures.
What do I do?

How to pass props to child component from parent component and receive it in child component using react-redux

My Parent component is like - Requestdetails component
import React, { Component } from "react";
import TopBarComponent from '../../common/TopBar/topBar'
export default class RequestDetailsComponent extends Component {
showBreadcrumb: boolean;
breadcrumbs: { title: string; navigate: string; state: boolean; }[];
constructor(props: any) {
super(props)
this.showBreadcrumb = true;
this.breadcrumbs = [
{ title: 'Dashboard', navigate: 'dashboard', state: true },
{ title: 'Requests', navigate: 'requestList', state: true },
{ title: 'Request Details', navigate: '', state: false }]
}
render() {
return (
<div>
<TopBarComponent showBreadcrumb={this.showBreadcrumb} breadcrumbs={this.breadcrumbs}/>
</div>
);
}
}
Child component -- TopBar component
import React, { Component } from "react";
import { Breadcrumb, BreadcrumbItem } from 'carbon-components-react'
export default class TopBarComponent extends Component {
showBreadcrumb:boolean;
constructor(props:any){
super(props);
this.showBreadcrumb = props.showBreadcrumb
}
render() {
let breadcrumbClass = 'dte-breadcrumbs dte-breadcrumbs--with-layout';
if(this.showBreadcrumb){
return (
<div className={breadcrumbClass}>
<div className="dte-page-container">
<div className="container-fluid">
<Breadcrumb >
<BreadcrumbItem>
Breadcrumb 1
</BreadcrumbItem>
</Breadcrumb>
</div>
</div>
</div>
);
}
return null;
}
}
I want to pass 'showBreadcrumb' and 'breadcrumbs' array to topBar component from Requestdetails component. but unable to do it in react-redux.
The above approach i used to follow in react but now i'm trying this react-redux., but failed to pass.
Please advise how i can pass this.
Assuming you have your redux store and provider setup and configured correctly, the way to connect UI components to your redux store, believe it or not, is with react-redux's connect HOC.
import React, { Component } from "react";
import { connect } from 'react-redux'; // import the connect HOC
import { Breadcrumb, BreadcrumbItem } from "carbon-components-react";
class TopBarComponent extends Component {
render() {
let breadcrumbClass = "dte-breadcrumbs dte-breadcrumbs--with-layout";
if (this.props.showBreadcrumb) {
return (
<div className={breadcrumbClass}>
<div className="dte-page-container">
<div className="container-fluid">
<Breadcrumb>
<BreadcrumbItem>
Breadcrumb 1
</BreadcrumbItem>
</Breadcrumb>
</div>
</div>
</div>
);
}
return null;
}
}
// define a function that maps your redux state to props
const mapStateToProps = state => ({
breadcrumbs: state.breadcrumbs, // these should match how your reducers define your state shape
showBreadcrumb: state.showBreadcrumb,
});
// export the connected component
export default connect(mapStateToProps)(TopBarComponent);

Expected null to be truthy. Jasmine / Karma

I'm trying to test my component injected into a mock class I created. Although the component works when I try to test its existence it returns null.
Injectable Component:
import { Injectable, ElementRef, Renderer2, RendererFactory2 } from '#angular/core';
#Injectable()
export class NgBackdropComponent {
private renderer: Renderer2;
private appElementRef: ElementRef;
message: string = 'Carregando...';
constructor(rendererFactory: RendererFactory2) {
this.renderer = rendererFactory.createRenderer(null, null);
this.appElementRef = new ElementRef(<Element>document.getElementsByTagName('body').item(0));
}
show() {
const divSpinnerItem1 = this.renderer.createElement('i');
const divSpinnerItem2 = this.renderer.createElement('i');
const divSpinnerItem3 = this.renderer.createElement('i');
const divSpinner = this.renderer.createElement('div');
this.renderer.addClass(divSpinner, 'spinner');
this.renderer.appendChild(divSpinner, divSpinnerItem1);
this.renderer.appendChild(divSpinner, divSpinnerItem2);
this.renderer.appendChild(divSpinner, divSpinnerItem3);
const spanMensagem = this.renderer.createElement('span');
spanMensagem.innerHTML = this.message;
const div = this.renderer.createElement('div');
this.renderer.addClass(div, 'lock-content');
this.renderer.appendChild(div, divSpinner);
this.renderer.appendChild(div, spanMensagem);
this.renderer.appendChild(this.appElementRef.nativeElement, div);
}
hide() {
const elemento = this.appElementRef.nativeElement.querySelector('.lock-content');
if (elemento) {
elemento.remove();
}
}
}
my testing environment:
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { NgBackdropComponent } from './ng-backdrop.component';
import { Component } from '#angular/core';
import { By } from '#angular/platform-browser';
#Component({
template: `
<button (click)="clickButton()"></button>
`
})
class MockNgBackdropComponent {
constructor(private backdrop: NgBackdropComponent) { }
clickButton() {
this.backdrop.message = 'Teste BackDrop aesdas';
this.backdrop.show();
console.log('iniciei backdrop');
}
closeBackdrop() {
this.backdrop.hide();
}
}
describe('NgBackdropComponent', () => {
let component: MockNgBackdropComponent;
let fixture: ComponentFixture<MockNgBackdropComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MockNgBackdropComponent],
providers: [NgBackdropComponent]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MockNgBackdropComponent);
component = fixture.componentInstance;
});
describe('Deve injetar', async () => {
it('Deve ter uma div principal', function () {
const btnClick = fixture.nativeElement.querySelector('button');
btnClick.click();
fixture.detectChanges();
const el = fixture.nativeElement.querySelector('.lock-content');
console.log(el);
expect(el).toBeTruthy();
});
});
});
In testing I create a Mock class where I inject my component.
I do not understand why it can not find the class because it exists.
The reason you can't find it in the component is because you did not create it in the component. If you look at this line in your constructor:
this.appElementRef = new ElementRef(<Element>document.getElementsByTagName('body').item(0))
You are creating it on the document directly in the <body> element. If you search for that in your spec, you will find it there. I created a STACKBLITZ to show you what I mean. Here is the spec from that stackblitz:
it('Deve ter uma div principal', () => {
const btnClick = fixture.nativeElement.querySelector('button');
console.log(btnClick);
btnClick.click();
fixture.detectChanges();
const appElementRef = new ElementRef(<Element>document.getElementsByTagName('body').item(0));
const el = appElementRef.nativeElement.querySelector('.lock-content');
expect(el).toBeTruthy();
});
Adding a little more clarification:
If you console.log(appElementRef) you'll notice that its tagName is body, and note the contents of its nativeElement.innerHTML Here is what that would look like "prettyfied":
<body>
<div class="jasmine_html-reporter">
<div class="jasmine-banner"><a class="jasmine-title" href="http://jasmine.github.io/" target="_blank"></a><span
class="jasmine-version">3.3.0</span></div>
<ul class="jasmine-symbol-summary"></ul>
<div class="jasmine-alert"></div>
<div class="jasmine-results">
<div class="jasmine-failures"></div>
</div>
</div>
<div id="nprogress" style="transition: none 0s ease 0s; opacity: 1;">
<div class="bar" role="bar" style="transform: translate3d(0%, 0px, 0px); transition: all 200ms ease 0s;">
<div class="peg"></div>
</div>
</div>
<div id="root0" ng-version="7.0.1">
<button></button>
</div>
<div class="lock-content">
<div class="spinner">
<i></i>
<i></i>
<i></i>
</div>
<span>Teste BackDrop aesdas</span>
</div>
</body>
Note how the button was created within the div with id="root0"? However, the div with class="lock-content" was created right off the root <body> element, and therefore is not within the div of the component.
In fact, you can see this very clearly when you console.log(fixture.nativeElement) and see that the tagName is "div", its innerHTML is <button></button>, and it has two attributes: id: "root0" and ng-version: "7.0.1". Put that all together and it looks like this:
<div id="root0" ng-version="7.0.1">
<button></button>
</div>
So you can clearly see that you cannot find the div you created in the component because you created it outside the component.
I hope this helps.
I think you should use DebugElement, for example:
it('Deve ter uma div principal', function () {
const btnClick = fixture.debugElement.query(By.css('button'));
btnClick.click();
fixture.detectChanges();
const el = fixture.debugElement.query(By.css('.lock-content'));
console.log(el);
expect(el).toBeTruthy();
});
Follow this link for more information.

Redux store error: <Provider> does not support changing `store` on the fly

I am trying to setup my first react/redux/rails app. I am using react_on_rails gem to pass in my current_user and gyms props.
Everything appears to work ok so far except my console shows error:
<Provider> does not support changing `store` on the fly. It is most likely that you see this error because you updated to Redux 2.x and React Redux 2.x which no longer hot reload reducers automatically. See https://github.com/reactjs/react-redux/releases/tag/v2.0.0 for the migration instructions.
Googling gives me hints that this can happen if you try to create a store within a render method, which causes store to get recreated. I don't see that issue here. Where am I going wrong?
//App.js
import React from 'react';
import { Provider } from 'react-redux';
import configureStore from '../store/gymStore';
import Gym from '../components/Gym';
const App = props => (
<Provider store={configureStore(props)}>
<Gym />
</Provider>
);
export default App;
../store/gymStore.jsx
//the store creation.
/*
// my original way
import { createStore } from 'redux';
import gymReducer from '../reducers/';
const configureStore = railsProps => createStore(gymReducer, railsProps);
export default configureStore;
*/
/* possible fix: https://github.com/reactjs/react-redux/releases/tag/v2.0.0 */
/* but adding below does not resolve error */
import { createStore } from 'redux';
import rootReducer from '../reducers/index';
export default function configureStore(railsProps) {
const store = createStore(rootReducer, railsProps);
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept(() => {
const nextRootReducer = require('../reducers').default;
store.replaceReducer(nextRootReducer);
});
}
return store;
}
I am not sure my rendered component is necessary but in case it is:
//compenents/Gym.jsx
import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
import LeftMenu from './LeftMenu';
class Gym extends React.Component {
static propTypes = {
//name: PropTypes.string.isRequired // this is passed from the Rails view
};
/**
* #param props - Comes from your rails view.
*/
constructor(props) {
super(props);
this.state = {
current_user: this.props.current_user,
gyms: JSON.parse(this.props.gyms),
active_gym: 1, //JSON.parse(this.props.gyms)[0],
name: 'sean',
title: 'Gym Overview'
};
}
updateName = name => {
this.setState({ name });
};
isLoggedIn = () => {
if (this.state.current_user.id != '0') {
return <span className="text-success"> Logged In!</span>;
} else {
return <span className="text-danger"> Must Log In</span>;
}
};
isActive = id => {
if (this.state.active_gym == id) {
return 'text-success';
}
};
render() {
return (
<div className="content">
<h2 className="content-header">{this.state.title}</h2>
{LeftMenu()}
{this.state.current_user.id != '0' ? <span>Welcome </span> : ''}
{this.state.current_user.first_name}
<h3 className="content-header">Your Gyms</h3>
<ul>
{this.state.gyms.map((gym, key) => (
<li key={key} className={this.isActive(gym.id)}>
{gym.name}
</li>
))}
</ul>
{this.isLoggedIn()}
<hr />
{/*
<form>
<label htmlFor="name">Say hello to:</label>
<input
id="name"
type="text"
value={this.state.name}
onChange={e => this.updateName(e.target.value)}
/>
</form>
*/}
</div>
);
}
}
function mapStateToProps(state) {
return {
current_user: state.current_user,
gyms: state.gyms,
active_gym: state.active_gym
};
}
export default connect(mapStateToProps)(Gym);

Set state like this props in react

Hello i have this code
import React from 'react'
import Link from 'react-router/lib/Link'
import { connect } from "react-redux"
import { load } from '../../actions/customerActions'
import List from './list';
import MdAdd from 'react-icons/lib/md/add'
#connect(store => {
return {
customers: store.customer.customers
}
})
export default class Customer extends React.Component {
componentDidMount() {
this.props.dispatch(load({location:localStorage.getItem('locationb')}));
}
render() {
const { customers } = this.props;
const tea = customers.customers && customers.customers.map(customer => <List key={customer.profile} customer={customer} />) || [];
return(
<div class="container">
{ customers.customers ?
<div class="columns is-multiline mb-100">
{ tea }
</div>
: 'Não exitem Clientes'}
<Link to="/customer/create" class="button is-info btn-rounded" title="Novo"><MdAdd /></Link>
</div>
)
}
}
But i only have access to customers in render passing this props.
How can i pass customer to a state variable in component did mount or else ?
i mean customers const { customers } = this.props; how i make like this.setState({customers: customers}) Having in the beginning this.state(customers: [])
YOu can use
componentWillReceiveProps(newProps){
console.log(newProps.customers.customers)
this.setState({customers: newProps.customers.customers})
}
it works for me
thanks
Carlos Vieira

Resources