Angular2 Component listen when parent's resize change - resize

I have a requirement in which I want to change properties of a child component depending on the size of its parent component though a method call. The issue I am running into is that the only resize event I can listen to is that of the window, which doesn't help as the window size is not changing, only the parent component is due to a side panel div opening and closing.
The only possibility I can see at the moment is to have some sort of polling in which we within the child component itself that checks if its width has changed every x amount of time.
Thanks for your help!

You are correct that you can't get the resize event on a div (without installing some js extension). But something like this works.
The Parent Component:
import {Component, AfterContentInit, ElementRef} from '#angular/core';
import { ChildComponent } from "./ChildComponent";
export interface IParentProps {
width: number;
height: number;
}
#Component({
selector: 'theParent',
template: `text text text text text text
text text text text text text
<the-child [parentProps]="parentProps"></the-child>`,
directives: [ChildComponent]
})
export class ParentComponent implements AfterContentInit {
sizeCheckInterval = null;
parentProps: IParentProps = {
width: 0,
height: 0
}
constructor(private el: ElementRef) {
}
ngAfterContentInit() {
this.sizeCheckInterval = setInterval(() => {
let h = this.el.nativeElement.offsetHeight;
let w = this.el.nativeElement.offsetWidth;
if ((h !== this.parentProps.height) || (w !== this.parentProps.width)) {
this.parentProps = {
width: w,
height: h
}
}
}, 100);
}
ngOnDestroy() {
if (this.sizeCheckInterval !== null) {
clearInterval(this.sizeCheckInterval);
}
}
}
The Child Component:
import {Component, Input, SimpleChange} from "#angular/core";
import { IParentProps } from "./ParentComponent";
#Component({
directives: [],
selector: "the-child",
template: `child`
})
export class ChildComponent {
#Input() parentProps: IParentProps;
constructor() {
this.parentProps = {
width: 0,
height: 0
}
}
ngOnChanges(changes: { [propName: string]: SimpleChange }) {
this.parentProps = changes["parentProps"].currentValue;
console.log("parent dims changed");
}
}

Related

Render children of ViroARImageMarker onAnchorFound

I'm currently working with the ViroReact Community Package in React Native to display a video in AR when a specific image is found. However the onTargetFound function of the ViroARImageMarker is not triggered, and the children of the ViroARImageMarker are not displayed.
When I added the onAnchorFound function to the ARScene (parent) the onAnchorFound method was triggered, however the children of the ViroARImageMarker still weren't rendered. Why is the function not triggered and therefore the children not displayed? How do I fix this?
The image is a 12x12cm black card with a bright orange logo (about 3cm) in the center. Neither of the targets are found in the ViroARImageMarker.
Here's my code:
Image Recognition Class
import React, { useEffect, useState } from 'react';
const {
ViroARScene,
ViroARImageMarker,
ViroARTrackingTargets,
ViroAnimations,
ViroVideo,
ViroMaterials,
ViroBox
} = require('#viro-community/react-viro');
const NewViroTracker = () => {
const videoPath = require('#assets/videos/wham.mp4');
const [videoAnimationName] = useState('showVideo');
const [playAnim, setPlayAnim] = useState(false);
function _onAnchorFound(evt: any) {
console.log('Anchor found in Marker :', evt);
setPlayAnim(true);
}
return (
<ViroARScene>
<ViroARImageMarker
target={'inviteCard'}
onAnchorFound={_onAnchorFound}>
<ViroVideo source={videoPath} />
</ViroARImageMarker>
<ViroARImageMarker
target={'logo'}>
<ViroBox position={[0, 0.25, 0]} scale={[0.5, 0.5, 0.5]} />
</ViroARImageMarker>
</ViroARScene>
);
};
ViroARTrackingTargets.createTargets({
inviteCard: {
source: require('#assets/images/invite-card.png'),
orientation: 'Up',
physicalWidth: 0.12 // real world width in meters
},
logo: {
source: require('#assets/images/logo-empty.png'),
orientation: 'Up',
physicalWidth: 0.0287 // real world width in meters
}
});
ViroMaterials.createMaterials({
chromaKeyFilteredVideo: {
chromaKeyFilteringColor: '#00FF00'
}
});
ViroAnimations.registerAnimations({
showVideo: {
properties: { scaleX: 1, scaleY: 1, scaleZ: 1 },
duration: 1000
},
closeVideo: {
properties: { scaleX: 0, scaleY: 0, scaleZ: 0 },
duration: 1
}
});
export default NewViroTracker;
App
import React from 'react';
const { ViroARSceneNavigator } = require('#viro-community/react-viro');
import styled from 'styled-components/native';
import NewViroTracker from 'components/NewViroTracker';
const App = () => {
return (
<ViroWrapper
autofocus={true}
initialScene={{
scene: NewViroTracker
}}
/>
);
};
export default App;
const ViroWrapper = styled(ViroARSceneNavigator)`
flex: 1;
`;
Dependencies:
"#viro-community/react-viro": "^2.21.1",
"react": "17.0.2",
"react-native": "0.66.3",

pass value from React Native picker component back to VueJS caller

I have the following React Native picker component that works -
file name: dynamic-picker.js
import React, { Component } from "react";
import { Container, Content, Picker } from "native-base";
export default class DynamicPicker extends Component {
constructor(props) {
super(props);
this.state = {
selected: this.props.selected
}
}
onValueChange(value) {
this.setState({
selected: value
});
}
itemsList = () => {
return (this.props.items.map( (item, index) => {
return (<Picker.Item label={item} key={index} value={item} />)
}));
}
render() {
return (
<Container>
<Content>
<Picker
mode="dropdown"
selectedValue={this.state.selected}
onValueChange={this.onValueChange.bind(this)}
>
{ this.itemsList() }
</Picker>
</Content>
</Container>
);
}
}
It is being called by a Vue JS file as follows -
file name: distance.vue
<template>
<dynamic-picker :items="items" :selected="selected" ></dynamic-picker>
</template>
<script>
import DynamicPicker from './dynamic-picker';
export default {
components: {
DynamicPicker
},
data() {
return {
selected: 'yards',
items: ["yards", "feet", "meters"]
}
}
}
</script>
The picker component is being displayed correctly. When the user selects a different option, that change is displayed in the picker component. However, I am stuck on how to get the selected property in the distance.vue file to update when the user selects a different option. That value needs to be captured so it can be passed on to the caller of the distance.vue file and used for a calculation.
Figured it out - added a callback to props so the child can call that function to pass data back to the parent when the value is changed.
Here is the updated distance.vue file (parent) -
<template>
<dynamic-picker :items="items" :selected="selected" :onValueChange="onValueChange" ></dynamic-picker>
</template>
<script>
import DynamicPicker from './dynamic-picker';
export default {
components: {
DynamicPicker
},
data() {
return {
selected: 'yards',
items: ["yards", "feet", "meters"]
}
},
methods: {
onValueChange(value) {
this.selected = value;
}
}
}
</script>
Then in dynamic-picker.js (child) the only change necessary was here -
onValueChange(value) {
this.setState({
selected: value
});
this.props.onValueChange(value); // added this line
}

Error: Cannot read property of undefined (Vue)

I want to give data from one component to another. Therefore the first component should emit the BusEvent when Data is updated. The updated function is called, but I always get an Error in my console: Error in updated hook: "TypeError: Cannot read property '$emit' of undefined"
The other component also doesn´t receive height/width. And I get the same error here: TypeError: Cannot read property '$emit' of undefined.
I just started learning vue and I don't understand whats wrong. Thanks a lot for your help!
height and width are getting initialized in component one, so that can't be the problem.
//Definition of EventBus in main.js file
export const EventBus = new Vue();
//First Component
import EventBus from '#/main.js'
export default {
data: function () {
return {
height: '',
width: ''
}
},
updated() {
EventBus.$emit('emited', this.height, this.width);
}
}
//Second Component
import EventBus from '#/main.js'
export default {
data: function () {
return {
height: '',
width: ''
}
},
mounted() {
const self = this
EventBus.$on('emited', function (height, width) {
alert('WORKS!')
self.height = height
self.width = width
})
}
}
EventBus should be imported like this import { EventBus } from '#/main.js'
If you want to pass multiple values via $emit you should use object.
Replace
EventBus.$emit('emited', this.height, this.width);
with
EventBus.$emit('emited', {height: this.height, width: this.width});
and then for listener:
EventBus.$on('emited', function ({height, width}) ...

Combine react-native component with vue-native

Is there any way I could combine react native component like this one https://github.com/archriss/react-native-snap-carousel/tree/master/example with a vue native?
I have a project in vue-native and want to use react-native component inside it, but I have an erro I do not understand: The console says: Invariant Violation: expected a string (for built-in components) or a class/function (for composite components) but got: undefined
<template>
<nb-container>
<nb-content>
<carousel
:data="similarEventsData"
:renderItem="_renderItem"
:sliderWidth="sliderWidth"
:itemWidth="itemWidth"
:inactiveSlideScale="0.95"
:inactiveSlideOpacity="1"
:enableMomentum="true"
:activeSlideAlignment="'start'"
:containerCustomStyle="stylesObj.slider"
:contentContainerCustomStyle="stylesObj.sliderContentContainer"
:activeAnimationType="'spring'"
:activeAnimationOptions="{ friction: 4, tension: 40 }"
/>
</nb-content>
</nb-container>
</template>
<script>
import { Dimensions, Platform, Share } from "react-native";
import Carousel from 'react-native-snap-carousel';
import { scrollInterpolators, animatedStyles } from '../../utils/animations';
const { width: viewportWidth, height: viewportHeight } = Dimensions.get('window');
const slideHeight = viewportHeight * 0.36;
const slideWidth = wp(75);
const itemHorizontalMargin = wp(2);
export default {
components: { carousel: Carousel },
computed: {
similarEventsData () {
return [1, 2, 3]
}
},
data: function() {
return {
sliderWidth: viewportWidth,
itemWidth: slideWidth + itemHorizontalMargin * 2,
stylesObj: {
slider: {
marginTop: 15,
overflow: 'visible'
},
sliderContentContainer: {
paddingVertical: 10
},
}
};
},
methods: {
_renderItem ({item, index}) {
return <Text>fsd</Text>;
},
},
};
</script>
I expect to render a component but with no luck
this question is about 2 years old and I think in that time the devs added the functionality to do so, if somehow anyone is experiencing this question and ran into this post, here's what to do:
Example with Entypo icon pack from expo vector icons:
<script>
import { Entypo } from '#expo/vector-icons';
export default {
data(){
components: { Entype }
}
}
</script>
and then in template:
<template>
<entypo />
</template>

ReactNative and NativeBase Radio

I've tried to change the radio value in ReactNative App with NativeBase template. I want to get or set value from the radio after click it, exactly checked or not. But couldn't find a way to get or set value to it. Even the radio button never changed on the screen after click. The codes are like as below:
import React, { Component } from 'react';
import { TouchableOpacity, Image, View } from 'react-native';
import { connect } from 'react-redux';
import { actions } from 'react-native-navigation-redux-helpers';
import {
Container,
Header,
Title,
Content,
Text,
Button,
Icon,
InputGroup,
Input,
List,
ListItem,
Radio, } from 'native-base';
import { openDrawer } from '../../actions/drawer';
import { Col, Row, Grid } from 'react-native-easy-grid';
import styles from './styles';
import dimension from './global';
import Swiper from 'react-native-swiper';
const imgBoy = require('../../../images/icon_boy.png');
const imgGirl = require('../../../images/icon_girl.png');
const {
popRoute,
} = actions;
class SessionPage extends Component {
static propTypes = {
name: React.PropTypes.string,
index: React.PropTypes.number,
list: React.PropTypes.arrayOf(React.PropTypes.string),
openDrawer: React.PropTypes.func,
popRoute: React.PropTypes.func,
navigation: React.PropTypes.shape({
key: React.PropTypes.string,
}),
}
popRoute() {
this.props.popRoute(this.props.navigation.key);
}
constructor(props) {
super(props);
// console.log(this.props.navigation);
this.state = {
sliderCount : parseInt(this.props.navigation.behavior.length / 5) + 1,
sliderArray : [],
selected : false,
}
this.getSliderArray();
console.log(this.state);
}
getSliderArray() {
for (var i = 0; i < this.state.sliderCount; i++) {
var childArray = [];
for (var j = i * 5; j < 5 * (i + 1); j++) {
if (this.props.navigation.behavior[j] != null){
var unit = this.props.navigation.behavior[j];
unit.selected = true;
childArray.push(unit);
}
}
this.state.sliderArray.push({
index : i,
behaviors : childArray
})
}
}
selectRadio(i, j){
this.state.sliderArray[i].behaviors[j].selected = true;
}
render() {
const { props: { name, index, list } } = this;
return (
<Container style={styles.container}>
<Swiper style={styles.wrapper}
height={dimension.Height - 400}
width={dimension.Width - 40}
showsButtons={false}
showsPagination={true}>
{this.state.sliderArray.map((item, i) =>
<View style={styles.slide1} key={i}>
{item.behaviors.map((subitem, j) =>
<ListItem key={i + "-" + j} style={styles.cardradio}>
<Radio selected={this.state.sliderArray[i].behaviors[j].selected} onPress={() => this.selectRadio(i, j)} />
<Text>{subitem.behaviorName}</Text>
</ListItem>
)}
</View>
)}
</Swiper>
</Content>
</Container>
);
}
}
function bindAction(dispatch) {
return {
openDrawer: () => dispatch(openDrawer()),
popRoute: key => dispatch(popRoute(key)),
};
}
const mapStateToProps = state => ({
navigation: state.cardNavigation,
name: state.user.name,
index: state.list.selectedIndex,
list: state.list.list,
});
export default connect(mapStateToProps, bindAction)(SessionPage);
selectRadio(i, j){
this.state.sliderArray[i].behaviors[j].selected = true; <== This is the problem
}
When you call this.state = something after the component has mounted, it doesn't trigger update method of component life cycle. Hence view will not be updated.
You should be using this.setState() to update your views
this.setState({
slider = something
})
For more info, refer docs
this.setState() is an async method. After you make changes in getSliderArray(), it may not be reflected in immediate console.log
this.getSliderArray();
console.log(this.state);
You can pass callback to this.setState() to perform any action only after state is changed
this.setState({
// new values
}, function() {
// Will be called only after switching to new state
})