React - accessing context in child components - react-context

I am using this react package - #spyna/react-store (link) in my project which is nothing but a wrapper for React Context API - createStore & withStore. But I am not able to access the react context api store values in my child component (which I set in the App component).
Actually when I try to access this.props.store.get("amount") I am getting an compilation error saying "props store does not exist". Please advise me on how to access the context in my child component. Thanks in advance.
App component:
import { createStore } from "#spyna/react-store";
class App extends React.Component<IAppProps> {
render() {
<Route component={this.ChildComponent} />
}
private ChildComponent= () => <Child1/>;
}
const initialValue = {
amount: 15,
username: {
name: "spyna",
url: "https://spyna.it"
}
};
export default createStore(App, initialValue);
Child component:
import { withStore } from "#spyna/react-store";
interface ICustomProps {
...
}
interface IAppProps {
...
}
class Child1 extends React.Component<ICustomProps & IAppProps, IAuthState> {
render() {
return (
<p>My Amount: {this.props.store.get('amount')}</p>
)
}
}
export default withStore(Child1)

Related

NestJS: present response content from URL on healthcheck

I'm trying to develop a healthcheck endpoint with NestJS (in which I have no experience). One of the dependencies I want to check is Twilio's SMS service. So far, the best URL I've found to gather this information is https://status.twilio.com/api/v2/status.json. The problem here is that I don't want to merely ping this address, but to gather it's JSON response and present some of the information it provides, namely these:
Is it possible, using (or not) the Terminus module? In the official docs I didn't find anything regarding this, only simpler examples using pingCheck / responseCheck: https://docs.nestjs.com/recipes/terminus
Yes, it is possible.
I have never used this, but HttpHealthIndicator has responseCheck method to check depends on the API response message. You can specify a callback function to analyze responses from the API. The callback function should return boolean represents the status of the API.
I couldn't find this in the documents, but you can see it here.
Although meanwhile the logic for this healthcheck has changed (and so this question became obsolete), this was the temporary solution I've found, before it happened (basically a regular endpoint using axios, as pointed out in one of the comments above):
Controller
import { Controller, Get } from '#nestjs/common';
import { TwilioStatusService } from './twilio-status.service';
#Controller('status')
export class TwilioStatusController {
constructor(private readonly twilioStatusService: TwilioStatusService) {}
#Get('twilio')
getTwilioStatus() {
const res = this.twilioStatusService.getTwilioStatus();
return res;
}
}
Service
import { HttpService } from '#nestjs/axios';
import { Injectable } from '#nestjs/common';
import { map } from 'rxjs/operators';
#Injectable()
export class TwilioStatusService {
constructor(private httpService: HttpService) {}
getTwilioStatus() {
return this.httpService
.get('https://status.twilio.com/api/v2/status.json')
.pipe(map((response) => response.data.status));
}
}
Of course this wasn't an optimal solution, since I had to do this endpoint + a separated one for checking MongoDB's availability (a regular NestJS healthcheck, using Terminus), the goal being an healthcheck that glued both endpoints together.
It is possible to merge in any property to the resulting object. You can see that in the TypeScript Interface
/**
* The result object of a health indicator
* #publicApi
*/
export declare type HealthIndicatorResult = {
/**
* The key of the health indicator which should be uniqe
*/
[key: string]: {
/**
* The status if the given health indicator was successful or not
*/
status: HealthIndicatorStatus;
/**
* Optional settings of the health indicator result
*/
[optionalKeys: string]: any;
};
};
And here is an example:
diagnostics/health/healthcheck.controller
import { Controller, Get } from '#nestjs/common'
import { ApiTags } from '#nestjs/swagger'
import { HttpService } from '#nestjs/axios'
import { HealthCheckService, HealthCheck, HealthIndicatorStatus, HealthCheckError } from '#nestjs/terminus'
#ApiTags('diagnostics')
#Controller('diagnostics/health')
export class HealthController {
constructor(
private health: HealthCheckService,
private httpService: HttpService,
) { }
#Get()
#HealthCheck()
check() {
return this.health.check([
() => this.httpService.get('http://localhost:9002/api/v1/diagnostics/health').toPromise().then(({ statusText, config: { url }, data }) => {
const status: HealthIndicatorStatus = statusText === 'OK' ? 'up' : 'down'
return { 'other-service': { status, url, data } }
}).catch(({ code, config: { url } }) => {
throw new HealthCheckError('Other service check failed', { 'other-service': { status: 'down', code, url } })
}),
])
}
}
diagnostics/diagnostics.module.ts
import { Module } from '#nestjs/common'
import { TerminusModule } from '#nestjs/terminus'
import { HttpModule } from '#nestjs/axios'
import { HealthController } from './health/health.controller'
#Module({
imports: [
HttpModule,
TerminusModule,
],
controllers: [HealthController],
})
export class DiagnosticsModule { }

How to use React DnD with styled component?

When wrapping my styled component in connectDragSource I get the following error:
Uncaught Error: Only native element nodes can now be passed to React
DnD connectors.You can either wrap PaneItemText__StyledItem into a
<div>, or turn it into a drag source or a drop target itself.
The first suggestion from this message is to wrap my styled component in a <div>, but this will mess with my layout and would prefer not to do this.
I'm not sure what the second option is suggesting - would anybody be able to clarify?
Below is an rough example what I am doing:
import React, { Component } from 'react';
import styled from 'styled-components';
import { DragSource } from 'react-dnd';
const StyledComponent = syled.div`
...
`;
class MyComponent extends Component {
...
render() {
const { connectDragSource } = this.props;
return connectDragSource(<StyledComponent />)
}
}
const itemSource = {
beginDrag(props) {
/* code here */
},
endDrag(props) {
/* code here */
}
};
function collect(connect, monitor) {
return {
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging()
}
}
export default DragSource('foo', itemSource, collect(MyComponent);
You should use Styled Component's innerRef to get the underlying DOM node, then you can call your connectDragSource to it.
In your case, it should be like this:
class MyComponent extends Component {
...
render() {
const { connectDragSource } = this.props;
return (
<StyledComponent
innerRef={instance => connectDragSource(instance)}
/>
)
}
}
You can also look at my implementation of Knight component for the official chess tutorial as a reference.
It is also accessible through CodeSandbox.
If you are using multiple connectors you can do the following:
<MyStyledComponent
innerRef={instance => {
connectDragSource(instance);
connectDropTarget(instance);
}}
/>
Source: https://github.com/react-dnd/react-dnd/issues/347#issuecomment-221703726

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 get GET paramater in Angular2?

By accessing myproject.dev/people?filter%5Bindustry%5D=finance&filter%5BstartWith%5D=a, Angular2 point the url to myproject.dev/people
Here is my RouteConfig:
#RouteConfig([
{
path: '/people',
name: config.route.main,
component: MainComponent,
useAsDefault: true
}
])
In MainComponent:
/// <reference path="../../../typings/angular2.d.ts" />
import {Component, Injector} from 'angular2/core';
import {ROUTER_DIRECTIVES, Router, RouteParams} from 'angular2/router';
import {BaseResourceComponent} from '../../Component/BaseResourceComponent';
import {Status as MainStatus} from '../../reusable/modules/status.svc';
import {Status} from '../../reusable/modules/status.svc';
import {Config} from "./Config";
import URI from 'urijs';
export class MainComponent extends BaseResourceComponent {
constructor(config: Config, status: Status, mainStatus: MainStatus, private router: Router, private routeParams: RouteParams) {
super(config, status, mainStatus);
}
onInit() {
var path = new URI(window.location.href);
path.setQuery('filter[industry]', 'fashion');
path.setQuery('filter[startWith]', 'a');
console.log(path);
console.log(this.router);
//this.router.root.lastNavigationAttempt = "/people?filter%5Bindustry%5D=finance&filter%5BstartWith%5D=a"
console.log(this.routeParams);
// this.routeParams returns {params: Object}
// this.routeParams.params.get('filter') return null
}
}
I still can get it from this.router.root.lastNavigationAttempt, but this is kind of tricky way to get it only. Any better way to get the GET parameters?
In the root component you can inject the router and subscribe, then on route events get the params from the router like
export class AppComponent {
constructor(private router:Router) {
router.subscribe(route => {
console.debug(this.router.currentInstruction.component.params);
});
}
}
On components added by the router you can inject RouteParams directly like
export class Other{
constructor(private routeParams: RouteParams) {
console.debug(this.routeParams);
console.log(this.routeParams.get('filter_industry'));
console.log(this.routeParams.get('filter_start_with'));
}
}
Plunker example
My solution : certainly not the best way t odo it but it work :
I assume that you have this kin of url :
http://localhost:8080/contextPath/index.html?login=true#token_type=Bearer&expires_in=9999&access_token=xxxXXXXXxxx
//get base url to get the token
if
(this.location == "")
{
console.log("traitement location");
this.location = location.href;
}
//extract all :
if (this.location != "") {
console.log("traitement token");
this.login = this.location.split("?")[0].split("=")[1];
this.token_type = this.location.split("?")[1].split("#")[1].split("&")[0].split("=")[1];
this.expire_in = +this.location.split("?")[1].split("#")[1].split("&")[1].split("=")[1];
this.setLocalDateValid((this.expire_in + this.nowDate()).toString());
this.token = this.location.split("?")[1].split("#")[1].split("&")[2].split("=")[1];
}
// then store it
this.setLocalToken(this.token);
Certainly not the best way to do it but it work perfectly well :)
#Günter Zöchbauer is correct. Child route can only use matrix parameter but not query parameter.

Angular 2 how to pass variable from parent component to router outlet

All the tutorials and answers that I have found show only how to pass a variable from parent component to child component using inputs but what is this child component is contained within the router outlet and not directly in the parent template ??
e.g:
Main component
#Component({
selector: 'my-app',
template: `
Main page
<router-outlet></router-outlet>
`,
directives: [ROUTER_DIRECTIVES]
})
#RouteConfig([
{ path: '/contact', name: 'Contact', component: ContactComponent},
])
export class AppComponent{
public num:Number = 123;
}
#Component({
selector: 'contact-page',
template: 'contact page'
})
export class ContactComponent{
public num:Number;
}
So in this example the main component template contain a router outlet where the child contact component will be rendered but how can I get variable "num" value in the child component evaluated inside a router outlet from the parent app component ??
I just stumbled over this question, here is how I have solved similar issues.
I would use a service to solve this. Then it is possible for all children and the parent to set the property, and the changes are propagated out for all subscribers. First I would create a service with a private BehaviorSubject which have a public getter and setter, to encapsulate ReplaySubject and only return Observable:
private _property$: BehaviorSubject<number> = new BehaviorSubject(1);
set property(value: number) {
this._property$.next(value);
}
get property$(): Observable<number> {
return this._property$.asObservable();
}
The reason for using new BehaviorSubject(1), is to set the initial value to 1, so there is something to subscribe to.
In the parents onInit, I would se the default value of property (num):
private _propertySubscribtion: Subscription
ngOnInit() {
// set default value to 5
this._componentService.property = 5;
// If property is updated outside parent
this._componentService.property$.subscribe(p => {
this.property = p;
});
}
ngOnDestroy() {
this._propertySubscribtion.unsubscribe();
}
In one or more of of the child components, it possible to subscribe for changes:
private _propertySubscribtion: Subscription
ngOnInit() {
this._propertySubscribtion = this._componentService.property$.subscribe(p => {
this.property = p;
});
}
ngOnDestroy() {
this._propertySubscribtion.unsubscribe();
}
And if some child or parent updates the property:
updateProperty() {
// update property
this._componentService.property = 8;
}
All subscribers will know about it.
Currently you can't bind to components added by the router. Use a shared service instead (there are tons of examples here on SO already) to pass data from or to the added component or to subscribe to events.
See also this issue https://github.com/angular/angular/issues/4452 especially this comment https://github.com/angular/angular/issues/4452#issuecomment-153889558
If your data is immutable, you can use RouteData.
#Component({...})
#RouteConfig([
{ path: '/contact', name: 'Contact', component: ContactComponent, data: {num: 123}},
])
export class AppComponent {
}
#Component({...})
export class ContactComponent {
public num:Number;
constructor(data: RouteData) {
this.num = data.get('num');
}
}
Could be useful to pass some configuration options you don't want to hardcode in child routes. But if you're expecting data to change, you'll need some other method.

Resources