In react I can arbitrarily pass props down like so:
function SomeComponent(props) {
const {takeOutProp, ...restOfProps} = props;
return <div {...restOfProps}/>;
}
How can I do the same thing in Angular?
--
More specifically, I want to write a custom dropdown component and pass props down to a select box.
As opposed to React components, Angular components aren't recompiled on input changes and use #Input property decorators to enable change detection. All properties that are expected to be passed should be explicitly defined as component inputs.
There are no better options than this one for custom select component. It's possible to read static attributes from current component element and set them on nested component element, but this won't set up bindings.
As for React recipe for deep props in wrapped components:
const Baz = props => <p>{props.baz}</p>;
const Bar = props => <Baz {...props} />;
const Foo = props => <Bar {...props} />;
This is usually handled by Angular DI and a hierarchy of injectors. A provider can be defined on respective injector in order to make data and behaviour available to nested components.
Actually it is not the answer on your question but perhaps it helps you.
You can add one custom directive with all params you need.
import { Directive, ElementRef } from '#angular/core';
#Directive({
selector: '[defaultConfig]'
})
export class DefaultDropdownConfigDirective {
constructor(el: ElementRef) {
el.nativeElement.style.backgroundColor = 'yellow';
// your default config
}
}
<my-dropdown defaultConfig></my-dropdown>
For more details you can read this
Related
In Vue 3, I'm creating a function that will accept an instance of a component and props to pass through. I'm also using TypeScript and was wondering if I can type those parameters. For example, the function would be something like:
const example = (component, props) => {
//
};
So my questions would be:
How can I specify a type for a component instance? These are not always going to be the same component, but would at least be components that are used for a similar purpose.
Would there be a way for me to specify the type for the props and confine it to the props that would be for the first parameter (the component)?
You could use many feature provided by typescript and the Component type from vue to achieve your proper typing, make a generic type that extends the Component then infers the component options/props using infer, use Partial to make them optional :
import type { Component } from "vue";
function example<T extends Component>
(Comp: T, props: T extends Component<infer P> ? Partial<P> : never) {
//....
}
example(Alert, { variant: "success"})
Note: this also infers the attributes and component instance utilities
You can get, reference here in this StackBlitz Link
I have one folder file todoState.ts in models folder of project. This todoState.ts used for managing global state of application using custom hooks. I need to perform following tasks on global todoState.ts..
Add new State
Remove state
update state
here is code of todoState.ts.
import { useState, useEffect } from "react";
const todoState = (todo?, callback?) => {
const [todos, setTodos] = useState([todo]);
useEffect(()=>{
setTodos(todo);
},[])
return [{todos, setTodos}];
}
Now, I have Two user defined components which are dependent on globalState. which are 1. <TodoForm /> and <TodoListLineItem />
Both of above component is rendered inside index.tsx.
How can I manage global state from all the components including index.tsx , <TodoForm /> , <TodoListLineItem />. Here...
<TodoForm /> , is used to add to tasks to list.
<TodoListLineItem />, is used to display all added tasks from TodoForm component. when user hover over to each todo list item then user can able to remove perticular tasks-item from global management state.
Which one is best and reusable way to implement this global management state feature?
How one component changed state from A to B is reflected on related component by just manipulating global state object of application. If I put useState([]) into index.tsx then it will works well, But I want to manage state from TodoState.ts file. Thank You.
export default todoState;
Finally , I found and learned New concepts for React-Context API. You can checkout here StackBlitz Link
To manage global states React provides context-api. Use only when you have multiple level of component properties pass down in component tree from Top to bottom. I used very small example to understand context api for my different use-case.
First of all we need to use two context..
createContext [ used to create global state context ]
useContext [ used to get states from context from child component ].
To work with Context I created one <context.Provider> component. and all state management tasks are done with only this component level only. all child component just send events of what to do. and global context of provider component changes accordingly.
firstly, Create context..
export interface ItodoContext{
todoState? : Itodo[];
addNewTodoState?: (state?: string) => void;
removeTodoItemByIndex? : (index?: number) => void;
}
export const todoContext = createContext<ItodoContext[]>([{}]);
As, I am using React-Typescript functional component, as per Interface of context i defined all tasks of states. I passed all TodoState, function to manipulate TodoState like addNewTodoState, removeTodoItemByIndex.
then create Provider of context..
const allTodoStates = {
todoState,
addNewTodoState,
removeTodoItemByIndex
}
return(
<todoContext.Provider value = { [allTodoStates]}>
{props.children}
</todoContext.Provider>
)
Then I set Provider as parent component in tree in index.tsx component like this..
const App: FC = () => {
return (
<div className="h-100 w-100">
<TodoStateProvider>
<Navbar />
<div className="container">
<TodoForm />
<TodoListLineItem />
</div>
</TodoStateProvider>
</div>
);
}
See above all components now child of <TodoStateProvider> parent component.
When I need to add new state to context is inside <TodoForm> and how we can add state is as below code...
const [{addNewTodoState}] = useContext(todoContext);
const sendTodoItem = (e) => {
addNewTodoState(todoInput.trim());
}
and so on.. provider component has value property, and we can get those properties from child using useContext() hook. as we used above. See full working demo I have attached in above StackBlitz Link.
To make e2e testing easier I would like to add to each react component data-component= attribute with component name. I would like to have it done "automatically" (without adjusting render() functions everywhere).
Anyone knows how to do it reliably for both class and function based components?
Component name is set via static property displayName for each component. You need to set it manually.
Create hoc (higher order component), to wrap component with div (or any other html tag) which will have required attribute.
const withComponentName(WrappedComponent) => {
const displayName = WrappedComponent.displayName || WrappedComponent.name || 'UnnamedComponent';
return props => (
<div data-component={displayName}><WrappedComponent {...props} /><div>
)
}
Wrap all component export statements with created hoc.
export default withComponentName(YourShinyComponent)
Another option is to use this Webpack plugin to do it for you:
https://github.com/lemonmade/babel-plugin-react-component-data-attribute
If you're using create-react-app:
If you've ejected already then just follow the docs on the plugin's page ☝️
However, if you have not ejected already, then you can use either of these solutions to customize your build for a create-react-app project:
https://github.com/arackaf/customize-cra
https://github.com/timarney/react-app-rewired/
I need to convert a functional component to a class based component. I am fairly new to React but I have tried to convert the component. I get the error - ReferenceError: Cant find variable: Props
My question is, where do I add props which was present in the class based component to make the conversion work ?
The class based component which is a modal with a form triggered from a parent component,this works well. The form uses state variables which dont work in a class based component so I need to convert the current functional component to a class based component. I'm using version 16.6.3 of React because other packages do not work with newer versions of React-Native so I cant use hooks with this version.
Functional component
const ShowModal = props => (
<Modal
visible={props.display}
animationType="slide"
onRequestClose={() => console.log("closed")}
>
...Other stuff in here.
</Modal>
);
export default ShowModal;
Class based component
export default class ShowModal extends Component {
state = {
};
render() {
return (
...Other stuff in here
);
}
}
I get the error - ReferenceError: Cant find variable: Props
In class based components props is exposed in the main scope of the class. You should read it using this keyword
class Component extends React.Component{
render(){return this.props.value}
}
I presume you want to use State, as the reason for moving to a Class component. Instead I suggest to use React Hooks which is the newest and elegant approach.
const ShowModal = props => (
const [state, setState] = React.useState({});
<Modal
visible={props.display}
animationType="slide"
onRequestClose={() => console.log("closed")}
>
...Other stuff in here.
</Modal>
);
React Hooks: https://medium.com/frontmen/react-hooks-why-and-how-e4d2a5f0347
I am learning to test React stateless components using the ReactTestUtils library. This is my simple component:
import React from 'react';
const Greeter = ({name,place}) => (
<h1>Hello,{name}. Welcome to the {place}.</h1>
);
export default Greeter;
This is my test spec, to get the renderIntoDocument working, I wrapped my Greeter component in a div as suggested here:
import {expect} from 'chai';
import React from 'react';
import ReactTestUtils from 'react-addons-test-utils';
import Greeter from '../Greeter';
describe('Greeter Components',() => {
it('renders correctly',() => {
var component = ReactTestUtils.renderIntoDocument(<div>
<Greeter name="Vamsi" place="Hotel California"/>
</div>);
var hasH1 = ReactTestUtils.findRenderedDOMComponentWithTag(component,'h1');
expect(hasH1).to.be.ok;
});
});
I get the error
findAllInRenderedTree(...): instance must be a composite component.
I am providing my code as jsbin here.
Since function components don't have an instance associated with them, you can't use them directly with render or renderIntoDocument. Attempting to wrap the function component is a good idea, unfortunately using a div doesn't work for a similar reason. DOM components also don't return a component instance, instead they return the underlying DOM node.
Which is all to say that you can't use the test utils function or native components as the "root" component you are rendering. Instead you will want to wrap your function components in a wrapper component that uses createClass or extends React.Component.
class Wrapper extends React.Component {
render() {
return this.props.children
}
}
let component = renderIntoDocument(<Wrapper><Greeter /></wrapper>
Gotcha's like this may be reason enough to make use of a third-party testing library like the popular enzyme, or my own take: teaspoon. Both abstract over issues like this by seamlessly wrapping and unwrapping function components for you, so you don't need to worry about what type of component you are trying to render.
Wrapping functional components in a <div> works for me. You just have to search for the component you want to test a little differently, i.e.
const props = { p1: "1" }
test('Foo renders correctly classed div', () => {
const cpt = TestUtils.renderIntoDocument(<div><Foo {...props} /></div>);
const myNode = ReactDOM.findDOMNode(cpt.childNodes[0]);
expect(myNode.className).toBe('my-class');
});
notice that you can target myNode for testing using cpt.childNodes[0]
In order to improve #monastic-panic's answer, my two cents:
You don't have to create a class for that. Do it dynamically:
import createReactClass from 'create-react-class';
// need to be a class component
const Clazz = createReactClass({
render: () => {
return <YourFunctionalComponentName {...props} />;
},
});
ReactTestUtils.renderIntoDocument(<Clazz />);