SharePoint react TextField onChanged error - sharepoint-online

When 'gulp serve' my SharePoint extension solution, I am receiving this error
Type { label: string; required: true; name: string; defaultValue: string; onChanged: (text: string) => void; } is not assignable to type 'IntrinsicAttributes & ITextFieldProps & { children?: ReactNode; }'.
Property 'onChanged' does not exist on type 'IntrinsicAttributes & ITextFieldProps & { children?: ReactNode; }.ts(2322)
I am using:
#microsoft/generator-sharepoint#1.10.0
gulp#4.0.2
npm#6.14.4
yo#3.1.1
SendEMailDialogContent.tsx file code below:
import * as React from 'react';
import {
TextField,
PrimaryButton,
Button,
DialogFooter,
DialogContent,
Spinner,
SpinnerSize
} from 'office-ui-fabric-react';
import { ISendEMailDialogContentProps } from './ISendEMailDialogContentProps';
import { EMailProperties, EMailAttachment } from '../models';
import { ISendEMailDialogContentState } from './ISendEMailDialogContentState';
export class SendEMailDialogContent extends React.Component<ISendEMailDialogContentProps, ISendEMailDialogContentState> {
private _eMailProperties: EMailProperties;
constructor(props) {
super(props);
this.state = {
isLoading: false
};
this._eMailProperties = this.props.eMailProperties;
this._onChangedTo = this._onChangedTo.bind(this);
this._onChangedSubject = this._onChangedSubject.bind(this);
this._onChangedBody = this._onChangedBody.bind(this);
this._submit = this._submit.bind(this);
}
public render(): JSX.Element {
var getDialogContent = () => {
if (this.state.isLoading) {
return (
<Spinner size={SpinnerSize.large} label="loading..." ariaLive="assertive" />
);
}
else {
return (
<div>
<TextField label='To' required={true} name="To" defaultValue={this._eMailProperties.To} onChanged={this._onChangedTo} />
<TextField label='Subject' required={true} defaultValue={this._eMailProperties.Subject} onChanged={this._onChangedSubject} />
<TextField label='Body' required={true} multiline defaultValue={this._eMailProperties.Body} onChanged={this._onChangedBody} />
<DialogFooter>
<Button text='Cancel' title='Cancel' onClick={this.props.close} />
<PrimaryButton text='OK' title='OK' onClick={this._submit} />
</DialogFooter>
</div>);
}
};
// UI
return <DialogContent
title='Send E-Mail Details'
subText=''
onDismiss={this.props.close}
showCloseButton={true}
>
{getDialogContent()}
</DialogContent>;
}
private _onChangedSubject(text: string) {
this._eMailProperties.Subject = text;
}
private _onChangedTo(text: string) {
this._eMailProperties.To = text;
}
private _onChangedBody(text: string) {
this._eMailProperties.Body = text;
}
The error is related to the TextField - onChanged={this._onChangedxxxxx}

Depending on the version of office-ui-fabric-react being used, it's likely that this callback is now called onChange instead of onChanged.
In addition to being renamed, onChange expects the first argument to be a react event, so you'll need to change your onChanged* methods to expect one more argument (and they're free to ignore it).
Relevant documentation: https://developer.microsoft.com/en-us/fluentui#/controls/web/textfield#implementation

Related

react-admin: Access <List/> records from bulkActions

The docs state that bulkActions don't get selected records of a List component, just the selected ids, but i need to check a specific field in each selected record from a button's click handler of the bulkActionsButtons.
Any ideas of how this could be achieved?
I used the aside prop that is passed to the List component. Your ref based solution did not work for me.
https://marmelab.com/react-admin/List.html#aside-component
Check the above link.
The component passed as the aside prop to List component receives selectedIds and the data as part of the props.
Just to expand on #Sudharsan-Ravikumar's answer, the ref solution didn't work in my situation either (react-admin 3.14.1, using classes instead of functions+hooks mostly). I used aside like this...
import React, {Fragment} from 'react';
import {List, Datagrid, TextField, useListContext} from 'react-admin';
import Button from '#material-ui/core/Button';
import AccountTreeIcon from '#material-ui/icons/AccountTree'
import dataProvider from './dataProvider';
export class MyList extends React.Component {
list = null
handleStartMyTask = async () => {
if (!this.list || !this.list.ids || this.list.ids.length===0) return;
// console.log(`LIST DATA:`, this.list.data)
try {
const result = await dataProvider.doMyRemoteTask(this.list.data)
console.log(result)
}
catch(err) {
console.log()
}
}
/**
* This was the only way i could figure out how to get the list details.
* That is, the props you get access to when you do useListContext().
* Refs didn't work.
*/
Aside = () => {
this.list = useListContext()
// We don't actually want an aside component for the list.
return null;
}
render = () => {
return <Fragment>
/* A little card outside the list with some useful buttons */
<div class="card">
<Button
variant="outlined" color="primary" size="medium"
onClick={this.handleStartMyTask}
>
<AccountTreeIcon/> Start my task now!
</Button>
</div>
<List {...this.props} aside={<this.Aside/>} >
<Datagrid>
<TitleField source="title" />
</Datagrid>
</List>
</Fragment>
}
}
Probably absolute heresy to hooks dorks but life is short!
Ok, this is what i did and it works. A combination of a render prop and a ref.
Please if anyone have a better idea, please let me now.
import React, {Component} from 'react';
import {
List,
Datagrid,
TextField,
Button
} from 'react-admin';
class MyBulkButtons extends Component {
handleClick = () => {
const {getSelectedRecords} = this.props;
const records = getSelectedRecords();
const selectedRecords = records.filter(i => i.title === 'Extra action!');
this.processExtraActions(selectedRecords);
};
processExtraActions(selectedRecords) {
//...
}
render() {
return (
<Button onClick={this.handleClick} label={"Check for extra actions"}/>
);
}
}
export class MyList extends Component {
constructor(props) {
super(props);
this.myDataGrid = React.createRef();
}
getSelectedRecords() {
const gridProps = this.myDataGrid.current.props;
return gridProps.selectedIds.map(id => gridProps.data[id]);
}
render() {
return (
<List {...this.props}
bulkActionButtons={<MyBulkButtons getSelectedRecords={this.getSelectedRecords.bind(this)}/>}>
<Datagrid ref={this.myDataGrid}>
<TextField source="id"/>
<TextField source="title"/>
</Datagrid>
</List>
);
}
}

how to fix this message angular observable

I have a little probleme, I use the node(express) and angular,
I want display all classes in angular but it's show an error like this
and it's not show json of back-end, what is wrong I don't understand
Return of method http get clasRoutes.js
res.json({ success: true, classes: postQuery, total: nbClasses });
Clas.interface.ts
export interface Clas {
room: string;
img?: string;
}
Clas.service.ts
private urlGetClasses = environment.url + 'classes';
constructor( private http: HttpClient ) { }
getClass(): Observable<Clas[]> {
return this.http.get(this.urlGetClasses).pipe(map(a => console.log(a)));
}
Clas.component.ts
public clas: Clas[] = [];
constructor(private clasService: ClasService) {}
ngOnInit() {
this.getClass().subscribe(res => {
this.clas = res.classes
});
}
private getClass(): Observable < Clas[] > {
return this.clasService.getClass().pipe(tap((value) => {
console.log(value)
}));
}
Clas.component.html
<p *ngFor="let cla of clas">
zefd {{cla.room}}
</p>
The problem is with Clas.service.ts. In your getClass() method, you are mapping to void as the return type of console.log is void; i.e. you are returning void whereas you've specified the return type of getClas() is Observable<Clas[]>. Therefore, you need to change your getClas() method to:
getClass(): Observable<Clas[]> {
return this.http.get(this.urlGetClasses);
}
Or as Nikola mentioned, you can use tap to log like so:
private getClass(): Observable<Clas[]> {
return this.clasService.getClass().pipe(tap((value) => {console.log(value)}));
}
There is new RxJS syntax. You should write it as
private getClass(): Observable<Clas[]> {
return this.clasService.getClass().pipe(tap((value) => {console.log(value)}));
}
make sure you import pipe and tap from rxjs.

Perform action in parent on child event click

So I'm trying to perform some action on the parent component of the child component when a click event is fired in the child component. Currently I have a dynamic loader which is able to load different child components. The problem I have is that the #Output() is being emitted but the parent component doesn't seem to have any knowledge when this event is fired. Is there something I am missing?
child2.component.ts
import {Component, Injector, Output, EventEmitter} from '#angular/core';
#Component({
selector: 'hello-world',
template: `
<div>Hello World {{showNum}}</div>
<li (click)="childButtonClicked(false)"> </li>
`,
})
export class HelloWorldComponent {
showNum = 0;
#Output() childEvent = new EventEmitter<boolean>();
constructor(private injector: Injector) {
this.showNum = this.injector.get('showNum');
console.log("HelloWorldComponent");
}
childButtonClicked(agreed: boolean) {
this.childEvent.emit(agreed);
console.log("clicked");
}
}
child1.component.ts
import {Component, Injector, Output, EventEmitter} from '#angular/core';
#Component({
selector: 'world-hello',
template: `
<div>World Hello {{showNum}}</div>
<li (click)="childButtonClicked(false)"> </li>
`,
})
export class WorldHelloComponent {
showNum = 0;
#Output() childEvent = new EventEmitter<boolean>();
constructor(private injector: Injector) {
this.showNum = this.injector.get('showNum');
console.log("WorldHelloComponent");
}
childButtonClicked(agreed: boolean) {
this.childEvent.emit(agreed);
console.log("clicked");
}
}
dynamic.componentloader.ts
import {Component, Input, ViewContainerRef,ComponentRef, ViewChild, ReflectiveInjector, ComponentFactoryResolver} from '#angular/core';
#Component({
selector: 'dynamic-component',// Reference to the components must be here in order to dynamically create them
template: `
<div #dynamicComponentContainer></div>
`,
})
export class DynamicComponent {
currentComponent:any = null;
#ViewChild('dynamicComponentContainer', { read: ViewContainerRef }) dynamicComponentContainer: ViewContainerRef;
// component: Class for the component you want to create
// inputs: An object with key/value pairs mapped to input name/input value
#Input() set componentData(data: {component: any, inputs: any }) {
if (!data) {
return;
}
// Inputs need to be in the following format to be resolved properly
let inputProviders = Object.keys(data.inputs).map((inputName) => {return {provide: inputName, useValue: data.inputs[inputName]};});
let resolvedInputs = ReflectiveInjector.resolve(inputProviders);
// We create an injector out of the data we want to pass down and this components injector
let injector = ReflectiveInjector.fromResolvedProviders(resolvedInputs, this.dynamicComponentContainer.parentInjector);
// We create a factory out of the component we want to create
let factory = this.resolver.resolveComponentFactory(data.component);
// We create the component using the factory and the injector
let component = factory.create(injector);
// We insert the component into the dom container
this.dynamicComponentContainer.insert(component.hostView);
// We can destroy the old component is we like by calling destroy
if (this.currentComponent) {
this.currentComponent.destroy();
}
this.currentComponent = component;
}
constructor(private resolver: ComponentFactoryResolver) {
}
}
main.component.ts
import { Component, OnInit } from '#angular/core';
import { HelloWorldComponent } from '../../views/main/sidebar-views/comps/hello-world.component';
import { WorldHelloComponent } from '../../views/main/sidebar-views/comps/world-hello.component';
#Component({
selector: 'main-component',
template: require('./main.component.html')
})
export class MainComponent {
private pressed: boolean = false;
componentData:any = null;
constructor() { }
createHelloWorldComponent(){
this.componentData = {
component: HelloWorldComponent,
inputs: {
showNum: 9
}
};
}
createWorldHelloComponent(){
this.componentData = {
component: WorldHelloComponent,
inputs: {
showNum: 2
}
};
}
test(){
console.log("some click event");
}
};
main.component.html
<div>
<h2>Lets dynamically create some components!</h2>
<button (click)="createHelloWorldComponent()">Create Hello World</button>
<button (click)="createWorldHelloComponent()">Create World Hello</button>
<dynamic-component [componentData]="componentData" (childEvent)="test()"></dynamic-component>
</div>
Since you are passing a parameter to the EventEmitter, you need to change your event binding on your component selector in your template to this:
<dynamic-component [componentData]="componentData" (childEvent)="test($event)"></dynamic-component>
Also, don't forget to change function signature in your component to accept the parameter:
test(agreed: boolean){
console.log("some click event");
}
More info on official docs.

How to connect second datasource to the same kendo-grid

I want to use the second datasource [data] to replace the IDs of a column with the NAMEs from another table(from another datasource) which have a common ID field. I have tried with kendo-grid-span-column, but it is working only with the same datasource as the grid.
<form novalidate #myForm="ngForm">
<kendo-grid
[data]="gridData | async"
<kendo-grid-column field="Contract_id" title="Contract_id"></kendo-grid-column>
<kendo-grid-span-column>
<kendo-grid-column field="Employee_id" title="Employee_id">
</kendo-grid-column>
<ng-template kendoGridCellTemplate let-dataItem>
<h4>{{dataItem.NameFromAnotherDataSource}}</h4>
</ng-template>
</kendo-grid-span-column>
<kendo-grid-column field="Contract_Num" title="Contract_Num"></kendo-grid-column>
</kendo-grid>
Any ideas?
Thanks,
Martin
I found the way.
container.component.html
<kendo-grid-column field="Employee_id" title="Employee" width="200">
<ng-template kendoGridCellTemplate let-dataItem>
<h4>{{category(dataItem.Employee_id)?.Name}}</h4>
</ng-template>
<ng-template kendoGridFilterCellTemplate let-filter>
<my-dropdown-filter
[filter]="filter"
[data]="employees"
textField="Name"
valueField="Employee_id">
</my-dropdown-filter>
</ng-template>
container.component.ts
private employees;
constructor(
public dataServiceEmployees: EmployeesDashboardService,
) {
this.dataServiceEmployees.fetch('Employee',this.gridState).subscribe((x) => this.employees = x.data);
}
public category (id: number): any {
if(this.employees){
return this.employees.find(x => x.Employee_id === id);
}
}
And using a filter template:
custom.filter.ts
import { Component, Input } from '#angular/core';
import { CompositeFilterDescriptor } from '#progress/kendo-data-query';
import { FilterService, BaseFilterCellComponent } from '#progress/kendo-angular-grid';
#Component({
selector: 'my-dropdown-filter',
template: `
<kendo-dropdownlist
[data]="data"
(valueChange)="onChange($event)"
[defaultItem]="defaultItem"
[value]="selectedValue"
[valuePrimitive]="true"
[textField]="textField"
[valueField]="valueField">
</kendo-dropdownlist>
`
})
export class DropDownListFilterComponent extends BaseFilterCellComponent {
public get selectedValue(): any {
const filter = this.filterByField(this.valueField);
return filter ? filter.value : null;
}
#Input() public filter: CompositeFilterDescriptor;
#Input() public data: any[];
#Input() public textField: string;
#Input() public valueField: string;
public get defaultItem(): any {
return {
[this.textField]: "Избери...",
[this.valueField]: null
};
}
constructor(filterService: FilterService) {
super(filterService);
}
public onChange(value: any): void {
this.applyFilter(
value === null ? // value of the default item
this.removeFilter(this.valueField) : // remove the filter
this.updateFilter({ // add a filter for the field with the value
field: this.valueField,
operator: "eq",
value: value
})
); // update the root filter
}
}

Angular2: how can component bind to class?

I can't find the obvious evidence that how a #Component() binds to a class.
How can it know component binds with class RedditArticle instead of class Article? After switching the position of the two class, it is messed up. Does that mean the class we need to bind should followed by the corresponding component?
import { bootstrap } from "angular2/platform/browser";
import { Component } from "angular2/core";
#Component({
selector: 'reddit-article',
template: `
<div class="row">
<div class="col-md-3"></div>
<div>Points: {{article.votes}}</div>
<div class="col-md-9"></div>
<!--<div class="row">-->
Title: {{article.title}}
Link: {{article.link}}
<button (click)="voteUp()">upvote</button>
<button (click)="voteDown()">downvote</button>
<!--</div>-->
</div>
`
})
class RedditArticle {
article: Article;
constructor() {
this.article = new Article('angular2', 'google.com', 0);
}
voteUp() {
this.article.votes++;
}
voteDown() {
this.article.votes--;
}
}
class Article {
title: string;
link: string;
votes: number;
constructor(title: string, link: string, votes: number) {
this.title = title;
this.link = link;
this.votes = votes;
}
}
The #Component() decorator applies directly to the class that follows the annotation.
This is the same for all annotations.
For example
constructor(#Inject('xxx') private val:string, private zone:NgZone) {}
Here #Inject() is bound to val

Resources