angular 5 reactive form checkbox sends object - checkbox

In a reactive form, I need a checked box to send a typed object to the Typescript file and not just a string or a number. When I try using value={{ myObject }} I get [object Object]. Is it possible to retrieve the correct object? Here is my code:
unite.ts:
import { Deserialize } from './deserialize.model';
import { Arme, Armes } from './armes';
export class Unite implements Deserialize<Unite> {
id: number;
race: string;
cat: number;
unite: string;
profil: string;
type: string;
taille: number;
m: number;
cc: number;
ct: number;
f: number;
e: number;
pv: number;
i: number;
a: number;
cd: number;
svg: string;
promote: any;
cost: number;
armes: Array<Arme>;
deserialize(input: any): Unite {
Object.assign(this, input);
this.armes = input.armes.map((arme: Arme) => new Arme().deserialize(arme));
return this;
}
}
armes.ts:
import { Deserialize } from './deserialize.model';
export class Arme implements Deserialize<Arme> {
id: number;
nom: string;
race: Array<string>;
portee: number;
f: number;
svg: string;
regles: string;
deserialize(input: any): Arme {
Object.assign(this, input);
return this;
}
}
export class Armes implements Deserialize<Armes> {
deserialize(input: any): Armes {
Object.assign(this, input);
return this;
}
}
deserialize.model.ts
export interface Deserialize<T> {
deserialize(input: any): T;
}
unite-form.ts:
import { Arme } from './armes';
export class UniteForm {
private _id: number;
private _nom: string;
private _size: number;
private _weapons: Array<Arme>;
private _staff: Array<string>;
construct(
id: number,
nom: string,
size: number,
weapons: Array<Arme>,
staff: Array<string>
) {
this._id = id;
this._nom = nom;
this._size = size;
this._weapons = weapons;
this._staff = staff;
}
get id(): number {
return this._id;
}
set id(id: number) {
this._id = id;
}
get nom(): string {
return this._nom;
}
set nom(nom: string) {
this._nom = nom;
}
get size(): number {
return this._size;
}
set size(size: number) {
this._size = size;
}
get weapons(): Array<Arme> {
return this._weapons;
}
set weapons(weapons: Array<Arme>) {
this._weapons = weapons;
}
get staff(): Array<string> {
return this._staff;
}
set staff(staff: Array<string>) {
this._staff = staff;
}
}
unite.component.ts
import { Component, OnInit, OnChanges, Input, Output, EventEmitter } from '#angular/core';
import { FormBuilder, FormControl, FormGroup, Validators } from '#angular/forms';
import {Unite} from '../../../models/unite';
import { Arme, Armes } from '../../../models/armes';
import { UniteForm } from '../../../models/unite-form';
import { ApiService } from '../../../services/api.service';
#Component({
selector: 'wab-unite',
templateUrl: './unite.component.html',
styleUrls: ['./unite.component.css']
})
export class UniteComponent implements OnInit, OnChanges {
public uniteFormGroup: FormGroup;
public selectedUnite: Unite;
constructor(private _fb: FormBuilder, private apiService: ApiService) { }
ngOnInit() { }
ngOnChanges() {
this.apiService.getUniteData(this.gotRace, this.gotUnite)
.subscribe(unite => {
this.selectedUnite = unite;
this.createForm(this.selectedUnite);
});
}
private createForm(unite: Unite) {
this.uniteFormGroup = this._fb.group({
id: unite['id'],
nom: unite['unite'],
taille: unite['taille'],
weapons: '',
staff: ''
});
}
unite.component.html:
<label for="armes">Armes:</label>
<mat-selection-list id="armes" formControlName="weapons">
<mat-list-option *ngFor="let weapon of selectedUnite.armes" value="{{ weapon.id }}">{{ weapon.nom }}
</mat-list-option>
</mat-selection-list>
Thanks a lot for your precious help!

Related

React Typesript, Context : passe value array<object> as value

I'm trying to passe an object value via a context but it doesn't work !
What i want to do :
passing value for filling setQuizzQuestions: (value: object) => void; in context File
I want passing the value via the component in setQuizzQuestions(values obj)
There are also a way to change just a var from the object ?
What is the good way for set a context array object value ?
ty
Context file and component file
import React from "react";
export interface buttonChoices {
isFound: boolean;
isDisabled: boolean;
text: string;
}
export type GlobalContent = {
score: number;
setScore: (score: number) => {};
TotalQuestion: number;
tryLeft: number;
userId: number;
};
export type QuizzContent = {
quizzQuestions: {
id: number;
question: string;
choices: Array<buttonChoices>;
}[];
quizzResponses: {
id: number;
response: number;
}[];
setQuizzQuestions: (value: object) => void;
};
export const MyGlobalContext = React.createContext<Partial<GlobalContent>>({});
export const MyQuizzContext = React.createContext<Partial<QuizzContent>>({});
import react, { useState, useContext } from "react";
import "../css/game.css";
import Board from "./Board";
import { MyGlobalContext, MyQuizzContext } from "../Context/gameContext";
function Game() {
const { score, setScore } = useContext(MyGlobalContext);
const { quizzQuestions, setQuizzQuestions } = useContext(MyQuizzContext);
const { quizzResponses } = useContext(MyQuizzContext);
const a = quizzQuestions;
setQuizzQuestions(quizzQuestions);

Extending and overriding interface properties

I'm trying to extend interface and can't seem to grasp the idea on generics. As you can see I'm trying to add property auth inside the props.
My code works but do I really need to re-import every properties on the extended interface? It seems that I'm doing something wrong here.
Inertia-JS Typescript Definition
export interface PageProps {
[key: string]: unknown;
}
export interface Page<SharedProps = PageProps> {
component: string;
props: PageProps & SharedProps & {
errors: Errors & ErrorBag;
};
url: string;
version: string | null;
scrollRegions: Array<{
top: number;
left: number;
}>;
rememberedState: Record<string, unknown>;
resolvedErrors: Errors;
}
My Code:
export type User = {
id: number;
name: string;
email: string;
}
interface PropsInterface extends Page {
props: PageProps & {
errors: Errors & ErrorBag;
auth: {
user: User
}
}
}
JSX
import { Page, PageProps } from '#inertiajs/inertia';
import { ErrorBag, Errors } from '#inertiajs/inertia/types/types';
import { usePage } from '#inertiajs/inertia-react';
export default function Topbar(): JSX.Element {
const { url, component, props } = usePage<PropsInterface>();
const { user } = props.auth
}
Try this
export type User = {
id: number;
name: string;
email: string;
}
interface PropsInterface extends Page<{ auth: { user: User } }> {}

Updating nested objects in reducers

I'm using react, typescript, redux to create a simple app that manage my ingredients but somehow I couldn't manage it with my code. I have the following:
my types declared in this file types.ts:
export interface Mytype {
a: number;
b: number;
c: number;
d: number;
}
export interface Mytype2 {
Mytypes: Mytype[];
}
export const ADD = "INGREDIENT";
export const DELETE = "INGREDIENT";
interface AddA {
type: typeof ADD;
aName: Mytype;
}
interface DeleteA {
type: typeof DELETE;
aName: Mytype;
}
export type OrderActionTypes = AddA | DeleteA;
Turn this
[...action.ingredientName]: state.ingredients[action.ingredientName] + 1
into this
[action.ingredientName]: state.ingredients[action.ingredientName] + 1
and also remove the array inside your DELETE cases, since the initial state is an object not an array.
case DELETE_INGREDIENT:
return {
...state,
[action.ingredientName]: state[action.ingredientName] - 1
};
and change your types here
import {
Ingredient,
ADD_INGREDIENT,
DELETE_INGREDIENT,
OrderActionTypes,
} from "./types";
export function addIngredient(newIngredient: keyof Ingredient): OrderActionTypes {
return {
type: ADD_INGREDIENT,
ingredientName: newIngredient,
};
}
export function deleteIngredient(Ingredient: keyof Ingredient): OrderActionTypes {
return {
type: DELETE_INGREDIENT,
ingredientName: Ingredient,
};
}
and in your types
export interface Ingredient {
cheese: number;
bacon: number;
mushrooms: number;
ananas: number;
}
export const ADD_INGREDIENT = "ADD_INGREDIENT";
export const DELETE_INGREDIENT = "DELETE_INGREDIENT";
interface AddIngredientAction {
type: typeof ADD_INGREDIENT;
ingredientName: keyof Ingredient;
}
interface DeleteIngredientAction {
type: typeof DELETE_INGREDIENT;
ingredientName: keyof Ingredient;
}
export type OrderActionTypes = AddIngredientAction | DeleteIngredientAction;
Can you try like this:
const initialState= {
Ingredient: {
cheese: 0,
bacon: 0,
mushrooms: 0,
bananas: 0,
}
}
as your initialState has Ingredient, so use state.Ingredient:
case DELETE_INGREDIENT:
return {
Ingredients: [
...state.Ingredient,
[action.ingredient]: state.Ingredient[action.ingredient] -1
],
};

trying to use useState with types

import React, { Component } from 'react'
interface orderInformation {
customer: number;
picklePrice: number;
breadPrice: number;
}
interface ComponentState
{
customer: number;
picklePrice: number;
breadPrice: number;
}
export default class pickleSandwich extends Component<orderInformation,ComponentState> {
const [customer, setCustomer] = useState(0);
//Get information for the user
getInfo = orderInformation => {
orderInformation.preventDefault();
};
render() {
return (
<div>
</div>
);
}
}
I am currently learning typescript, however I cannot find any place with a good example with my needs. All I can find are string examples and in this case I am using type number.
Trying to use React Hooks in order to consume this information.
if you want to use hooks that is the way:
import React from 'react'
interface orderInformation {
customer: number;
picklePrice: number;
breadPrice: number;
}
interface ComponentState {
customer: number;
picklePrice: number;
breadPrice: number;
}
const PickleSandwich = (props:orderInformation) => {
const [customer, setCustomer] = useState<ComponentState>("valid_state");
//Get information for the user
getInfo = orderInformation => {
orderInformation.preventDefault();
};
return <div />
}
but if you go to use class, you are in the right path but in class you are not able to use hooks, then you need to use:
state = {your_state}

Argument of type '{ status: string; DetailsAnnouncementListItemState: { items: IListItem[]; columns: undefined

I have the following error, and below I will try to explain what I am trying to accomplish:
[ts]
Argument of type '{ status: string; DetailsAnnouncementListItemState: { items: IListItem[]; columns: undefined[]; }...' is not assignable to parameter of type 'Pick<IFactoryMethodState, "status" | "DetailsAnnouncementListItemState">'.
Types of property 'DetailsAnnouncementListItemState' are incompatible.
Type '{ items: IListItem[]; columns: undefined[]; }' is not assignable to type 'IDetailsAnnouncementListItemState'.
Types of property 'items' are incompatible.
Type 'IListItem[]' is not assignable to type 'IAnnouncementListItem[]'.
Type 'IListItem' is not assignable to type 'IAnnouncementListItem'.
Property 'announcementBody' is missing in type 'IListItem'.
I have a base interface and extended interfaces that represent list items in sharepoint depending on the list, a normal list has id, title, createdby, createddate, etc, other lists inherit the same fields, but add additional fields, so my design is like this:
export interface IListItem {
[key: string]: any;
id: string;
title: string;
modified: Date;
created: Date;
modifiedby: string;
createdby: string;
}
import {IListItem} from './IListItem';
export interface INewsListItem extends IListItem {
newsheader: string;
newsbody: string;
expiryDate: Date;
}
import {IListItem} from './IListItem';
export interface IDirectoryListItem extends IListItem {
firstName: string;
lastName: string;
mobileNumber: string;
internalNumber: string;
}
import {IListItem} from './IListItem';
export interface IAnnouncementListItem extends IListItem {
announcementBody: string;
expiryDate: Date;
}
Now, I have a factory method design pattern like this:
import { IListItem } from './models/IListItem';
import { SPHttpClient, SPHttpClientResponse } from '#microsoft/sp-http';
export interface IFactory{
getItems(requester: SPHttpClient, siteUrl: string, listName: string): IListItem[];
}
import { SPHttpClient, SPHttpClientResponse } from '#microsoft/sp-http';
import { IWebPartContext } from '#microsoft/sp-webpart-base';
import { IListItem} from './models/IListItem';
import { IFactory } from './IFactory';
import { INewsListItem } from './models/INewsListItem';
import { IDirectoryListItem } from './models/IDirectoryListItem';
import { IAnnouncementListItem } from './models/IAnnouncementListItem';
export class ListItemFactory implements IFactory{
getItems(requester: SPHttpClient, siteUrl: string, listName: string): IListItem[] {
switch(listName) {
case 'List':
let items: IListItem[];
requester.get(`${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id`,
SPHttpClient.configurations.v1,
{
headers: {
'Accept': 'application/json;odata=nometadata',
'odata-version': ''
}
})
.then((response: SPHttpClientResponse): Promise<{ value: IListItem[] }> => {
return response.json();
})
.then((response: { value: IListItem[] }): void => {
items= response.value;
});
return items;
case 'News':
let newsitems: INewsListItem[];
requester.get(`${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id`,
SPHttpClient.configurations.v1,
{
headers: {
'Accept': 'application/json;odata=nometadata',
'odata-version': ''
}
})
.then((response: SPHttpClientResponse): Promise<{ value: INewsListItem[] }> => {
return response.json();
})
.then((response: { value: INewsListItem[] }): void => {
newsitems= response.value;
});
return newsitems;
case 'Announcements':
let announcementitems: IAnnouncementListItem[];
requester.get(`${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id`,
SPHttpClient.configurations.v1,
{
headers: {
'Accept': 'application/json;odata=nometadata',
'odata-version': ''
}
})
.then((response: SPHttpClientResponse): Promise<{ value: IAnnouncementListItem[] }> => {
return response.json();
})
.then((response: { value: IAnnouncementListItem[] }): void => {
announcementitems= response.value;
});
return announcementitems;
case 'Directory':
let directoryitems: IDirectoryListItem[];
requester.get(`${siteUrl}/_api/web/lists/getbytitle('${listName}')/items?$select=Title,Id`,
SPHttpClient.configurations.v1,
{
headers: {
'Accept': 'application/json;odata=nometadata',
'odata-version': ''
}
})
.then((response: SPHttpClientResponse): Promise<{ value: IDirectoryListItem[] }> => {
return response.json();
})
.then((response: { value: IDirectoryListItem[] }): void => {
items= response.value;
});
return directoryitems;
default:
return null;
}
}
}
so far so god, however on my react component when I try to use it:
private readItems(): void {
this.setState({
status: 'Loading all items...'
});
let factory = new ListItemFactory();
//Here its where we actually use the pattern to make our coding easier.
switch(this.props.listName)
{
case "List":
let listItems = factory.getItems(this.props.spHttpClient, this.props.siteUrl, this.props.listName);
this.setState({
status: `Successfully loaded ${listItems.length} items`,
DetailsListItemState : {
items: listItems,
columns: [
]
}
});
break;
case "Announcements":
let announcementlistItems = factory.getItems(this.props.spHttpClient, this.props.siteUrl, this.props.listName);
this.setState({
status: `Successfully loaded ${listItems.length} items`,
DetailsAnnouncementListItemState : {
items: announcementlistItems,
columns: []
}
});
break;
case "News":
let newsFactory = new NewsListItemFactory();
let newsListItems = newsFactory._getItems(this.props.spHttpClient, this.props.siteUrl, this.props.listName);
this.setState({
status: `Successfully loaded ${listItems.length} items`,
DetailsNewsListItemState : {
items: newsListItems,
columns: []
}
});
break;
case "Directory":
let directoryFactory = new DirectoryListItemFactory();
let directoryListItems = directoryFactory._getItems(this.props.spHttpClient, this.props.siteUrl, this.props.listName);
this.setState({
status: `Successfully loaded ${listItems.length} items`,
DetailsDirectoryListItemState : {
items: directoryListItems,
columns: []
}
});
break;
default :
break;
}
}
This is the place where I get the exception, the first switch case works fine, the second one doesnt, and I understand whats happening, getitems return ListItem[], but I am trying to assign the value to an AnnouncementListItem[]
However I am not sure how to fix it, as you could see the newslisitem extends ListItem, and I want to make this solution as generic as possible
Update,
Forgot this file
import { IListItem } from './models/IListItem';
import { INewsListItem } from './models/INewsListItem';
import { IDirectoryListItem } from './models/IDirectoryListItem';
import { IAnnouncementListItem } from './models/IAnnouncementListItem';
import {
IColumn
} from 'office-ui-fabric-react/lib/DetailsList';
export interface IFactoryMethodState{
type: string;
status: string;
DetailsListItemState: IDetailsListItemState;
DetailsNewsListItemState: IDetailsNewsListItemState;
DetailsDirectoryListItemState : IDetailsDirectoryListItemState;
DetailsAnnouncementListItemState : IDetailsAnnouncementListItemState;
}
export interface IDetailsListItemState {
columns: IColumn[];
items: IListItem[];
}
export interface IDetailsNewsListItemState {
columns: IColumn[];
items: INewsListItem[];
}
export interface IDetailsDirectoryListItemState {
columns: IColumn[];
items: IDirectoryListItem[];
}
export interface IDetailsAnnouncementListItemState {
columns: IColumn[];
items: IAnnouncementListItem[];
}
In what follows I am only going to talk about IAnnouncementListItem but you can use the same analysis for each of your subtypes.
The error is correctly telling you that the compiler has no way to verify that factory.getItems() returns an array of IAnnouncementListItem. As you said, IAnnouncementListItem extends IListItem, which means that every IAnnouncementListItem is an IListItem, but not every IListItem is an IAnnouncementListItem. So it is warning you that you are doing something unsafe by treating an array of IListItem as an array of IAnnouncementListItem.
There are multiple ways to deal with it:
Type Assertions
One is to just tell the compiler that you know what you're doing and that it shouldn't worry, by asserting that the return value of factory.getItems() is the right type.
let announcementlistItems = factory.getItems(
this.props.spHttpClient, this.props.siteUrl, this.props.listName
) as IAnnouncementListItem[];
This silences the compiler, but you lose the benefit of the compiler's type checking.
Runtime type guards
Another way is to take the result from getItems() and perform a runtime check to see if the return value is actually an array of IAnnouncementListItem[]. Here's an example:
function isArrayOfIAnnouncementListItem(arr: IListItem[]): arr is IAnnouncementListItem[] {
return arr.every(listItem => 'announcementBody' in listItem);
}
And then
let announcementlistItems = factory.getItems(
this.props.spHttpClient, this.props.siteUrl, this.props.listName
);
if (!isArrayOfIAnnouncementListItem(announcementListItems)) {
throw new Error("Wrong Type or Something");
}
// now announcementListItems is known to be IAnnouncementListItem[]
This will make the compiler happy. It's safer than a bare assertion, but you're still doing runtime checks. If someone else implemented getItems(), this might be the best you could do. But since you're implementing it, you can actually make getItems() itself safer:
Overload signature
Probably the best way to deal with it is to change the signature of getItems() so that it knows that the listName parameter affects the output type. This can be done using overloads:
// overloads
getItems(requester: SPHttpClient, siteUrl: string, listName: "Announcements"): IAnnouncementListItem[];
// put other overloads for each allowable type here
// implementation
getItems(requester: SPHttpClient, siteUrl: string, listName: string): IListItem[] | null; { // .. implementation
(You can get this behavior with generics instead of overloads if you want; more info available upon request)
Now when you call getItems() you will be restricted to using a listName from the set of acceptable values, and the return type will be narrowed for you.
So, you can do any of those if you want. Hope that helps. Good luck!

Resources