Render named children in React component? - javascript

I have a modal component which take a trigger to open the modal, and the content to go inside the modal.
<Modal>
<button className="btn btn--primary">Open modal</button>
<div>
<p>Modal content</p>
<p>More modal content</p>
</div>
</Modal>
And in the Modal component:
return (
<div className="Modal">
{props.children[0]}
<div className="Modal__container">
<div className="Modal__header">
<button className="Modal__close btn btn--secondary btn--small">
Close
</button>
<h1 className="Modal__heading">Here is my modal</h1>
</div>
<div className="Modal__content">{props.children[1]}</div>
</div>
</div>
);
This is working but very fragile as Im using an index on props.children. Can I instead name the components I pass to Modal? So something like:
<Modal>
<Modal.Trigger>
<button className="btn btn--primary">Open modal</button>
</Modal.Trigger>
<Modal.Content>
<p>Modal content</p>
<p>More modal content</p>
</Modal.Content>
</Modal>

I suggest you use props instead:
declare your Modal like so:
<Modal action={<button className="btn btn--primary">Open modal</button>}>
<div>
<p>Modal content</p>
<p>More modal content</p>
</div>
</Modal>
and your render method will then look something like
return (
<div className="Modal">
{props.action}
<div className="Modal__container">
<div className="Modal__header">
<button className="Modal__close btn btn--secondary btn--small">
Close
</button>
<h1 className="Modal__heading">Here is my modal</h1>
</div>
<div className="Modal__content">{props.children[1]}</div>
</div>
</div>
);

You can mark your children with a boolean flag that will serve as a name.
Within Modal you find the child marked as trigger and the child marked as content:
// within Modal
render() {
let trigger, content;
React.Children.forEach(this.props.children, child => {
if (!child) return;
if (child.props.trigger) { trigger = child }
if (child.props.content) { content = child }
})
return (
<div className="Modal">
{trigger}
<div className="Modal__container">
<div className="Modal__header">
<button className="Modal__close btn btn--secondary btn--small">
Close
</button>
<h1 className="Modal__heading">Here is my modal</h1>
</div>
<div className="Modal__content">
{content}
</div>
</div>
)
}
And you mark the appropriate children when rendering Modal:
<Modal>
<button trigger className="btn btn--primary">Open modal</button>
<div content>
<p>Modal content</p>
<p>More modal content</p>
</div>
</Modal>
We wrote a library called seapig that you might find useful for this use case.

Related

Error message when trying to link external JS file to react Component

When I try to link my main.js folder into my react component, I get this error message
"TypeError: Cannot read property 'addEventListener' of null"
I want implement the JS to the modals into my work section, I am having trouble doing that. I am very new to React so I am not totally sure what the rules and best practices are. I am tryint turn my old portfolio site into a new SPA site.
Code:
// select the open-btn button
let openBtn = document.getElementById('open-btn');
// select the modal-background
let modalBackground = document.getElementById('modal-background');
// select the close-btn
let closeBtn = document.getElementById('close-btn');
// shows the modal when the user clicks open-btn
openBtn.addEventListener('click', function () {
modalBackground.style.display = 'block';
});
// hides the modal when the user clicks close-btn
closeBtn.addEventListener('click', function () {
modalBackground.style.display = 'none';
});
// hides the modal when the user clicks outside the modal
window.addEventListener('click', function (event) {
// check if the event happened on the modal-background
if (event.target === modalBackground) {
// hides the modal
modalBackground.style.display = 'none';
}
});
// select the open-btn button
let openBtn2 = document.getElementById('open-btn2');
// select the modal-background
let modalBackground2 = document.getElementById('modal-background2');
// select the close-btn
let closeBtn2 = document.getElementById('close-btn2');
// shows the modal when the user clicks open-btn
openBtn2.addEventListener('click', function () {
modalBackground2.style.display = 'block';
});
// hides the modal when the user clicks close-btn
closeBtn2.addEventListener('click', function () {
modalBackground2.style.display = 'none';
});
// hides the modal when the user clicks outside the modal
window.addEventListener('click', function (event) {
// check if the event happened on the modal-background
if (event.target === modalBackground2) {
// hides the modal
modalBackground2.style.display = 'none';
}
});
React Code:
import React, { Component } from 'react';
import toDoListApp from '../mockups/to-do-list-img..jpg';
import pokeMockup from '../mockups/mockup-poke.jpg';
import ExpatColMock from '../img/colombia_logo.png';
import DogAPIMock from '../mockups/mockup-doggie.gif';
import modals from '../js/main.js';
class Work extends Component {
render() {
return (
<>
<section>
<div>
{/* Modal Background and Modal */}
<div className="mCustomScrollbar" data-mcs-theme="dark" id="modal-background">
<div id="modal">
<span id="close-btn">×</span>
<img className="modal-img" src="mockups/mockup.png" alt="expatcolombia" />
<h2 className="modal-title">Expat Colombia</h2>
<div className="modal-text">
<h3>Description</h3>
<p>Expat Service for people moving to Colombia. They provide consultations, tours, and assist in housing.</p>
<h3>Dependencies</h3>
<p>HTML, CSS, Javascript</p>
</div>
<div className="buttons">
Visit
Github
</div>
</div>
</div>
{/* Modal Background and Modal */}
<div className="mCustomScrollbar2" data-mcs-theme="dark" id="modal-background2">
<div id="modal2">
<span id="close-btn2">×</span>
<img className="modal-img2" src="mockups/poke-mockup.jpg" alt="expatcolombia" />
<h2 className="modal-title">PokemonAPI</h2>
<div className="modal-text2">
<h3>Description</h3>
<p>Pokemon-themed App with a that includes names, images, and stats of different Pokemons.</p>
<h3>Dependencies</h3>
<p>Javascript, JQuery, Ajax</p>
</div>
<div className="buttons2">
Demo
Github
</div>
</div>
</div>
</div>
<div>
<header className="ScriptHeader">
<div>
<h1 className="work-title">My Work</h1>
</div>
</header>
<section id="work">
{/*****Image size (height) is paramount to keep the photos formatted properly*****/}
{/* Image row 1 */}
<div className="row">
<div className="column">
<div className="container">
<img src={toDoListApp} className="image" alt="to-do-list-api" />
<a href="https://github.com/Drxl95/To-Do-List" target="_blank">
<div className="overlay">
<div className="text">Simple jQuery To Do List App</div>
</div>
</a>
</div>
</div>
<div className="column">
<a id="open-btn2">
<div className="container">
<img src={pokeMockup} className="image" alt="PokemanAPI" />
<div className="overlay">
<div className="text">Pokemon-themed API app <br /></div>
</div>
</div>
</a>
</div>
<div className="column">
<a id="open-btn">
<div className="container">
<img src={ExpatColMock} className="image" alt="expatcolombia.com" />
<div className="overlay">
<div className="text">Expat Colombia</div>
</div>
</div>
</a>
</div>
<div className="column">
<a href="https://github.com/Drxl95/DogAlbum" target="_blank">
<div className="container">
<img src={DogAPIMock} className="image" alt="dogAPI-mockup" />
<div className="overlay">
<div className="text">Dog Album API</div>
</div>
</div>
</a>
</div>
</div>
{/* Image row 2 */}
{/* <div class="row">
<div class="column">
<a href="#">
<div class="container">
<img src="img/image-4.jpg" class="image">
<div class="overlay">
<div class="text">Example 4</div>
</div>
</div>
</a>
</div>
<div class="column">
<a href="#">
<div class="container">
<img src="img/image-5.jpg" class="image">
<div class="overlay">
<div class="text">Example 5</div>
</div>
</div>
</a>
</div>
<div class="column">
<a href="#">
<div class="container">
<img src="img/image-6.jpg" class="image">
<div class="overlay">
<div class="text">Example 6</div>
</div>
</div>
</a>
</div>
</div> */}
{/* partial */}
</section>
</div>
</section>
<script src={modals}>
</script>
</>
)
}
}
export default Work

card transition in react.js when click on prev and next button

hi, how can I have a transition-changing card when I clicked on perv and the next buttons in react.js?
I want to change my card with smooth transition or fade it
card image
return (
<article className="review">
<div className="img-container">
<img src={image} alt={name} className="person-img" />
<span className="quote-icon">
<FaQuoteRight />
</span>
</div>
<h4 className="author">{name}</h4>
<p className="job">{job}</p>
<p className="info">{text}</p>
<div className="button-container">
<button className="prev-btn" onClick={prevPerson}>
<FaChevronLeft />
</button>
<button className="next-btn" onClick={nextPerson}>
<FaChevronRight />
</button>
</div>
<button className="random-btn" onClick={randomPerson}>
Random Member
</button>
</article>
);
};
I have tried a small POC with fade effect but you can use the same logic to add other transition effect as well.
https://codesandbox.io/s/condescending-microservice-vhsvq

Add VueJs modal component to Laravel template

Question about simple situation, I want add vue modal component to default Laravel project.
When I registered in this way :
Vue.component('modal', {
template: '#modal-template'
})
new Vue({
el: '#app',
data: {
showModal: false
}
})
And named component like this :
ModalComponent.vue
Browser give me next errors.
All details in the Github repository
By the way, maybe better add modal window from bootstrap with jQuery or it will be messy?
you should have :
Vue.component('modal', require('./components/ModalComponent.vue'));
and inside your ModalComponent.vue file:
<template>
<transition name="modal">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header">
default header
</slot>
</div>
<div class="modal-body">
<slot name="body">
default body
</slot>
</div>
<div class="modal-footer">
<slot name="footer">
default footer
<button class="modal-default-button" #click="$emit('close')">
OK
</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
</template>
<script>
export default {
}
</script>
view.blade.php
<div id="app">
<button id="show-modal" #click="showModal = true">Show Modal</button>
<!-- use the modal component, pass in the prop -->
<modal v-if="showModal" #close="showModal = false">
<!--
you can use custom content here to overwrite
default content
-->
<h3 slot="header">custom header</h3>
</modal>
</div>

Creating multiple modals on one page with VUE

As the title implies I want to add two modal dialog's on one page.
I figured out how to create on but the second one I am having troubles with.
HTML:
<body>
<h1>-projects-</h1>
<ul id="button-container">
<li>
<a id="html-modal-button" #click="showModal = true">
<img class="htmllogo" src="images/HTMLLogo.png">
<div class="html-text-block">
<h2>HTML</h2>
<p>My web projects</p>
</div>
</a>
<htmlmodal v-if="showModal" #close="showModal = false"></htmlmodal>
</li>
<li>
<a id="cs-modal-button" #click="showModal = true">
<img class="csharplogo" src="images/CSHARPLogo.png">
<div class="cs-text-block">
<h2>C#</h2>
<p>My windows projects</p>
</div>
</a>
<csmodal v-if="showModal" #close="showModal = false"></csmodal>
</li>
</ul>
<!-- MODAL SECTION -->
<script type="text/x-template" id="html-modal-template">
<transition name="html-modal">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header">HTML HEADER</slot>
</div>
<div class="modal-body">
<slot name="body">HTML BODY</slot>
</div>
<div class="modal-footer">
<slot name="footer">HTML FOOTER
<button class="modal-default-button" #click="$emit('close')">OK</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
</script>
<script type="text/x-template" id="cs-modal-template">
<transition name="cs-modal">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header">CS HEAD</slot>
</div>
<div class="modal-body">
<slot name="body">CS BODY</slot>
</div>
<div class="modal-footer">
<slot name="footer">CS FOOTER
<button class="modal-default-button" #click="$emit('close')">OK</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
</script>
</body>
</html>
VUE.JS:
$(window).load(function(){
// CREATE THE DOM COMPONENT
Vue.component('htmlmodal', {
template: '#html-modal-template',
});
Vue.component('csmodal', {
template: '#cs-modal-template',
});
// START THE APP
new Vue({
el:'#button-container',
data: {
showModal: false
}
})
});
**The Fiddle: ** https://jsfiddle.net/oz053uzj/
As you can see I have separated the modal section, so essentially I could simply create and edit a modal, create a vue.component and reference it.
The problem comes when I try to open the modal, it seem's to only open the second or last modal for each of the buttons, I presume its due to #click="showModal = <>" is same for both the modals, but I'm left clueless..
because their v-if is based on the same data showModal.
If showModal is true, both will be displayed. And the last one will be on top.
So how about differentiating them based on a string instead of true/false ?
<a id="html-modal-button" #click="showModal = 'html-modal'">
and
<htmlmodal v-if="showModal === 'html-modal'" ...
?
demo: https://jsfiddle.net/jacobgoh101/oz053uzj/1/
You should use two different variable for two modal.
HTML:
<body>
<ul id="button-container">
<li>
<a id="html-modal-button" #click="showModal = true">
First Modal
</a>
<htmlmodal v-if="showModal" #close="showModal = false"></htmlmodal>
</li>
<li>
<a id="cs-modal-button" #click="showCsModal = true">
Second Modal
</a>
<csmodal v-if="showCsModal" #close="showCsModal = false"></csmodal>
</li>
</ul>
<!-- MODAL SECTION -->
<script type="text/x-template" id="html-modal-template">
<transition name="html-modal">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header">HTML HEADER</slot>
</div>
<div class="modal-body">
<slot name="body">HTML BODY</slot>
</div>
<div class="modal-footer">
<slot name="footer">HTML FOOTER
<button class="modal-default-button" #click="$emit('close')">OK</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
</script>
<script type="text/x-template" id="cs-modal-template">
<transition name="cs-modal">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header">CS HEAD</slot>
</div>
<div class="modal-body">
<slot name="body">CS BODY</slot>
</div>
<div class="modal-footer">
<slot name="footer">CS FOOTER
<button class="modal-default-button" #click="$emit('close')">OK</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
</script>
</body>
VUE.JS:
new Vue({
el:'#button-container',
data: {
showModal: false,
showCsModal: false,
}
})
**The Fiddle: ** https://jsfiddle.net/tanvir86/od2cjtkc/

AngularJS Issues

I have DIVs generated by ng-repeat and inside them I have inner DIVs. I would like the inner DIVs to be visible when a user click on the outer DIVs. An inner DIV must be visible only when its outer DIV is clicked. I implemented it with $scope.bot variable and it's not working as I want since when one outer DIV is clicked, all the inner DIVs of the other outer DIVs become visible (this is because they all depend on the $scope.bot variable).
I would like to also click on the outer div again and the inner DIV if it is visible then it will disappear.
<div>
<div>Course</div>
<div ng-repeat="course in courses" ng-click=" tog()">
{{course .name}}
<div ng-show="bot== true">
<div class="pull-right"><span>X</span></div>
<button class="btn btn-primary">Stop</button>
<button class="btn btn-danger">Start</button>
</div>
</div>
</div>
$scope.bot = false;
$scope.tog = function(){
if(!$scope.bot ){
$scope.bot = true;
}
}
This is an option:
<div>
<div>Course</div>
<div ng-repeat="course in courses" ng-click="tog($index)">
{{course .name}}
<div ng-show="bot[$index]== true">
<div class="pull-right"><span>X</span></div>
<button class="btn btn-primary">Pause/Resume</button>
<button class="btn btn-danger">Abort</button>
<button class="btn btn-success">Detail</button>
</div>
</div>
</div>
$scope.bot = [];
$scope.tog = function(index){
$scope.bot[index] = !$scope.bot[index];
}
Just place the visibility flag on the course object itself, so every course will have it's own flag:
<div>
<div>Course</div>
<div ng-repeat="course in courses" ng-click=" tog(course)">
{{course .name}}
<div ng-show="course.bot== true">
<div class="pull-right"><span>X</span></div>
<button class="btn btn-primary">Pause/Resume</button>
<button class="btn btn-danger">Abort</button>
<button class="btn btn-success">Detail</button>
</div>
</div>
</div>
$scope.tog = function(course){
if(!course.bot ){
course.bot = true;
}
}
Try this
<div>
<div>Course</div>
<div ng-repeat="course in courses" ng-click="course.bot = !course.bot">
{{course .name}}
<div ng-show="course.bot === true">
<div class="pull-right"><span>X</span></div>
<button class="btn btn-primary">Stop</button>
<button class="btn btn-danger">Start</button>
</div>
</div>
A simple way to do this is to remove everything regarding divs appearing and disappearing from controller and handle everything in the template.
<div>
<div>Course</div>
<div ng-repeat="course in courses" ng-init="bot=false" ng-click="bot = !bot">
{{course .name}}
<div ng-show="bot">
<div class="pull-right"><span>X</span></div>
<button class="btn btn-primary">Pause/Resume</button>
<button class="btn btn-danger">Abort</button>
<button class="btn btn-success">Detail</button>
</div>
</div>
</div>
You can initialize the bot variable at each parent div level because ng-repeat creates a new scope for every element.
This issue you are having because you are using one bot variable which is associated to all divs.
<div>
<div>Course</div>
<div ng-repeat="course in courses" ng-click=" tog($index)">
{{course .name}}
<div ng-show="course.bot== true">
<div class="pull-right"><span>X</span></div>
<button class="btn btn-primary">Pause/Resume</button>
<button class="btn btn-danger">Abort</button>
<button class="btn btn-success">Detail</button>
</div>
</div>
</div>
$scope.tog = function(index){
$scope.courses[index].bot = !$scope.courses[index].bot;
}

Categories