Passing a function as a parameter to child component polymer 3 - javascript

I'm new to Polymer and stared learning it using a course called Polymer 3 - Code like a Google developer.
I would like to call the function: addContactClick() from the child component: side-menu.
To do that I have passed the function as an argument. I have defined it in the side-menu and called it using on-click
start-polymer3.js:
import {
PolymerElement,
html
} from '#polymer/polymer';
import SideMenu from './side-menu.js'
import ContentArea from './content-area.js'
class StartPolymer3 extends PolymerElement {
static get properties() {
return {
addContactClick: {
type: Function,
}
};
}
constructor() {
super();
this.addContactClick = this.addContactClick.bind(this);
}
static get template() {
return html `
<style>
.main-page {
display: grid;
grid-template-columns: 250px 1fr;
}
</style>
<div class="main-page">
<side-menu addContactClick="addContactClick"></side-menu>
<content-area></content-area>
</div>
`;
}
addContactClick() {
console.log('Button Clicked');
}
}
customElements.define('start-polymer3', StartPolymer3);
side-menu.js:
import {
PolymerElement,
html
} from '#polymer/polymer';
export default class SideMenu extends PolymerElement {
constructor() {
super();
}
static get properties() {
return {
addContactClick: {
type: Function
},
};
}
static get template() {
return html `
<style>
#side-menu {
background: #323759;
height: 100vh;
padding: 50px 25px;
}
.logo {
text-align: center;
}
.logo img {
width: 50px;
}
.title {
color: #ccced7;
font-weight: 700;
margin: 1rem 0;
}
#side-menu nav a {
color: #ccced7;
text-decoration: none;
text-transform: capitalize;
display: block;
padding: 10px 10px 10px 0;
}
#side-menu nav a span.icon{
padding: 10px 10px 10px 0;
}
</style>
<section id="side-menu">
<div class="logo">
<img src="https://www.dexterousgroup.com.au/wp-content/uploads/2018/03/google-logo-icon-PNG-Transparent-Background.png">
</div>
<div class="menu">
<div class="title">Contacts</div>
<nav>
<span class="icon"> + </span>Add Contacts
</nav>
</div>
</section>
`;
}
}
customElements.define('side-menu', SideMenu);
When I execute it, I get the following error:listener method addContactClick not defined
handler # template-stamp.js:92

Here are changes you should do and try.
start-polymer3.js
Removed property addContactClick.
Changed definition of addContactClick() function
Added Binding instead of just name of function, while passing our own function in side-menu element.
import {
PolymerElement,
html
} from '#polymer/polymer';
import SideMenu from './side-menu.js'
import ContentArea from './content-area.js'
class StartPolymer3 extends PolymerElement {
static get properties() {
return {
};
}
constructor() {
super();
// this.addContactClick = this.addContactClick.bind(this);
}
static get template() {
return html `
<style>
.main-page {
display: grid;
grid-template-columns: 250px 1fr;
}
</style>
<div class="main-page">
<side-menu add-contact-click="[[addContactClick()]]"></side-menu>
<content-area></content-area>
</div>
`;
}
addContactClick() {
return function() {console.log('Button Clicked')};
}
}
customElements.define('start-polymer3', StartPolymer3);
side-menu.js
Changed type of addContactClick from Function to String.
import {
PolymerElement,
html
} from '#polymer/polymer';
export default class SideMenu extends PolymerElement {
constructor() {
super();
}
static get properties() {
return {
addContactClick: {
type: String
},
};
}
static get template() {
return html `
<style>
#side-menu {
background: #323759;
height: 100vh;
padding: 50px 25px;
}
.logo {
text-align: center;
}
.logo img {
width: 50px;
}
.title {
color: #ccced7;
font-weight: 700;
margin: 1rem 0;
}
#side-menu nav a {
color: #ccced7;
text-decoration: none;
text-transform: capitalize;
display: block;
padding: 10px 10px 10px 0;
}
#side-menu nav a span.icon{
padding: 10px 10px 10px 0;
}
</style>
<section id="side-menu">
<div class="logo">
<img src="https://www.dexterousgroup.com.au/wp-content/uploads/2018/03/google-logo-icon-PNG-Transparent-Background.png">
</div>
<div class="menu">
<div class="title">Contacts</div>
<nav>
<span class="icon"> + </span>Add Contacts
</nav>
</div>
</section>
`;
}
}
customElements.define('side-menu', SideMenu);
If still, you have an issue let me know.
Here is another link you can visit which is for similar question:
Passing a function as an attribute value

Related

Console.log is telling me my div is null when I try and add a class to it

I'm adding a keydown eventlistener that'll simply make my box div show. However, when I press a key, nothing happens and instead the console tells me, "Uncaught TypeError: box is null".
No idea what the problem is, please help
Here's my App.js:
import "./styles.css"
const box = document.querySelector(".box");
const notification = document.querySelector(".notif");
window.onload = document.addEventListener("keydown", e => {
box.classList.add("active");
notification.classList.add("inactive");
})
function App() {
return (
<>
<div className='notif'>Press Any Key</div>
<div className='box'>
<h2>20</h2>
<h1>Shift</h1>
</div>
</>
);
}
export default App;
and the css:
#import url('https://fonts.googleapis.com/css2?family=Poppins&display=swap');
:root {
--teal: #00ccff;
--pink: #d400d4;
}
*, *::before, *::after {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
body {
display: flex;
min-height: 100vh;
align-items: center;
justify-content: center;
background: #0e1538;
}
.box {
position: relative;
width: 300px;
height: 300px;
display: flex;
justify-content: center;
align-items: center;
background: rgba(0,0,0,.5);
overflow: hidden;
border-radius: 20px;
flex-direction: column;
display: none;
}
.box.active {
display: flex;
}
.box::before {
content: '';
position: absolute;
width: 150px;
height: 140%;
background: linear-gradient(var(--teal), var(--pink));
animation: animate 4s linear infinite;
}
#keyframes animate {
0%{
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.box::after {
content: '';
position: absolute;
background: #0e1538;
inset: 4px;
border-radius: 16px;
}
.box h2 {
position: relative;
color: white;
font-size: 5em;
z-index: 10;
}
.box h1 {
position: relative;
color: white;
z-index: 10;
background: rgba(0,0,0,.5);
width: 97.5%;
text-align: center;
letter-spacing: 5px;
}
.notif {
font-size: 3em;
background: var(--teal);
padding: 0 20px;
border-radius: 20px;
}
.notif.inactive {
display: none;
}
You should be maintaining/updating state with the new class information when you press a key. Add the listener within the useEffect (along with a cleanup function). When a key is pressed the listener calls a function that updates the state (in this case switching the active/inactive classes). The component is re-rendered, and the elements' class names are changed using a little helper function.
const { useEffect, useState } = React;
function Example() {
// Initialise state
const [ active, setActive ] = useState({
box: false,
notification: true
});
// A function to update the state
function toggleActive() {
setActive(prev => ({
box: !prev.box,
notification: !prev.notification
}));
}
// An effect that adds a listener, w/ a cleanup function
useEffect(() => {
window.addEventListener('keydown', toggleActive);
return () => {
window.removeEventListener('keydown', toggleActive);
}
}, []);
// Returns the active status of the element from state
function isActive(type) {
return active[type] ? 'active' : 'inactive';
}
return (
<div>
<div className={isActive('notification')}>
Press Any Key
</div>
<div className={isActive('box')}>
<h2>20</h2>
<h1>Shift</h1>
</div>
</div>
);
}
ReactDOM.render(
<Example />,
document.getElementById('react')
);
.active { color: red; }
.inactive { color: black; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
import "./styles.css"
import {useEffect} from 'react'
function App() {
useEffect(()=>{
const box = document.querySelector(".box");
const notification = document.querySelector(".notif");
document.addEventListener("keydown", e => {
box.classList.add("active");
notification.classList.add("inactive");
})
},[])
return (
<>
<div className='notif'>Press Any Key</div>
<div className='box'>
<h2>20</h2>
<h1>Shift</h1>
</div>
</>
);
}
export default App;

I am calling an event listener on a button and it wont create the html element in my DOM

I have some <li> tags under a <ul> in my HTML code. These <li> are
styled with CSS to create cards with a title and description.
I have created a button with an event listener that I wish to create a
new <li> element under the <ul> tag in the HTML when I press the
button.
I retrieve the <ul> and attempt to appendChild() with a new <li>.
This is all within a class in JS. I am not sure why the new <li> cards are not being displayed when I press the button.
```
class DOMHelper {
static clearEventListeners(element) {
const clonedElement = element.cloneNode(true);
element.replaceWith(clonedElement);
return clonedElement;
}
//receives element ID and a New destination in the DOM and sends the
//card to that destination Via Append.
static moveElement(elementId, newDestinationSelector) {
const element = document.getElementById(elementId);
const destinationElement =
document.querySelector(newDestinationSelector);
destinationElement.append(element);
}
}
class Tooltip {}
class ProjectItem {
constructor(id, updateProjectListsFunction, type) {
this.id = id;
this.updateProjectListsHandler = updateProjectListsFunction;
this.connectMoreInfoButton();
this.connectSwitchButton(type);
}
connectMoreInfoButton() {}
//retreives the button of the given DOM element and adds an event
//listener to it that executes updateProjectListHandler
//recieves from: update () {}
//runs updateProjectListsHandler --> this.switchProject -->
connectSwitchButton(type) {
const projectItemElement = document.getElementById(this.id);
let switchBtn = projectItemElement.querySelector('button:last-of-
type');
switchBtn = DOMHelper.clearEventListeners(switchBtn);
switchBtn.textContent = type === 'active' ? 'Finish' : 'Activate';
switchBtn.addEventListener(
'click',
this.updateProjectListsHandler.bind(null, this.id)
);
}
update(updateProjectListsFn, type) {
this.updateProjectListsHandler = updateProjectListsFn;
this.connectSwitchButton(type);
}
}
class ProjectList {
projects = [];
constructor(type) {
//selects all of the li elements under the `#${type}-projects DOM
//element depending on the element input type
//and creates a new ProjectItem for every li element it found.
this.type = type;
const prjItems = document.querySelectorAll(`#${type}-projects
li`);
for (const prjItem of prjItems) {
this.projects.push(
new ProjectItem(prjItem.id, this.switchProject.bind(this),
this.type)
);
}
//new DayOfWeek('Monday');
console.log(this.projects);
}
setSwitchHandlerFunction(switchHandlerFunction) {
this.switchHandler = switchHandlerFunction;
}
//pushes project to the projects array. Project input is "new
//ProjectList('finished');"
addProject(project) {
this.projects.push(project);
DOMHelper.moveElement(project.id, `#${this.type}-projects ul`);
project.update(this.switchProject.bind(this), this.type);
}
//finds every project id in projects with the id matching constant
//projectId
//filters out every dom object with id that does not equal the
//projected input to the function
switchProject(projectId) {
// const projectIndex = this.projects.findIndex(p => p.id ===
projectId);
// this.projects.splice(projectIndex, 1);
this.switchHandler(this.projects.find(p => p.id === projectId));
this.projects = this.projects.filter(p => p.id !== projectId);
console.log('Testing this button')
}
switchDayHandler() {
const selectDayOfWeek = document.getElementById('p4');
//const dayOfWeek = selectDayOfWeek.querySelector('li')
selectDayOfWeek.addEventListener(
'click',
this.updateDayInfoHandler()
);
console.log(selectDayOfWeek);
}
updateDayInfoHandler () {
console.log('TEST TEST TEST');
}
}
class DayOfWeek {
projectList = {
monday: [
{
title: 'test',
description: "test"
}
]
}
constructor(day) {
this.createNewButton;
switch (day) {
case ( 'Monday' ):
//this.addNewListItemHandler();
break;
case ( 'Tuesday' ):
console.log('Tuesday');
break;
case ( 'Wednesday' ):
console.log('Wednesday');
break;
case ( 'Thursday' ):
console.log('Thursday');
break;
case ( 'Friday' ):
console.log('Friday');
break;
//default:
// ingredient = null;
}
}
addNewListItemHandler() {
const projectItem = document.getElementById('active');
const newListItem = document.createElement('li');
newListItem.innerHTML = `id="p2"
data-extra-info="Not really a business topic but still
important."
class="card"
>
<h2>MONDAY ITEM</h2>
<p>Don't forget to pick up groceries today.</p>
<button class="alt">More Info</button>
<button>Finish</button>
`;
projectItem.appendChild(newListItem);
}
createNewButton() {
const newButton = document.getElementById('addButton');
newButton.addEventListener( 'click', this.addNewListItemHandler);
}
}
class App {
static init() {
const activeProjectsList = new ProjectList('active');
const finishedProjectsList = new ProjectList('finished');
activeProjectsList.setSwitchHandlerFunction(
finishedProjectsList.addProject.bind(finishedProjectsList)
);
finishedProjectsList.setSwitchHandlerFunction(
activeProjectsList.addProject.bind(activeProjectsList)
);
}
}
App.init();
```
<body>
<header id="main-header">
<h1>Project Planner</h1>
</header>
<div>
<section id="days-projects">
<header>
<h2>Days</h2>
</header>
<ul>
<li
id="p4"
data-extra-info="Super important conference! Fictional but
still!"
class="card"
>
<h2>Monday</h2>
</li>
<li
id="p5"
data-extra-info="Super important conference! Fictional but
still!"
class="card"
>
<h2>Tuesday</h2>
</li>
<li
id="p6"
data-extra-info="Super important conference! Fictional but
still!"
class="card"
>
<h2>Wednesday</h2>
</li>
<li
id="p7"
data-extra-info="Super important conference! Fictional but
still!"
class="card"
>
<h2>Thursday</h2>
</li>
<li
id="p8"
data-extra-info="Super important conference! Fictional but
still!"
class="card"
>
<h2>Friday</h2>
</li>
</ul>
</section>
<section id="active-projects">
<header>
<h2>Active Projects</h2>
</header>
<ul id = 'active'>
<li
id="p1"
data-extra-info="Got lifetime access, but would be nice to
finish it soon!"
class="card"
>
<h2>Finish the Course</h2>
<p>Finish the course within the next two weeks.</p>
<button class="alt">More Info</button>
<button>Finish</button>
</li>
<li
id="p2"
data-extra-info="Not really a business topic but still
important."
class="card"
>
<h2>Buy Groceries</h2>
<p>Don't forget to pick up groceries today.</p>
<button class="alt">More Info</button>
<button>Finish</button>
</li>
</ul>
</section>
<section id="finished-projects">
<header>
<h2>Finished Projects</h2>
</header>
<ul>
<li
id="p3"
data-extra-info="Super important conference! Fictional but
still!"
class="card"
>
<h2>Book Hotel</h2>
<p>
Academind conference takes place in December, don't forget to
book a hotel.
</p>
<button class="alt">More Info</button>
<button>Activate</button>
</li>
</ul>
</section>
</div>
<button id = "addButton" class='add_Button'>ADD NEW</button>
< /body>
</html>
```css
* {
box-sizing: border-box;
}
html {
font-family: sans-serif;
}
body {
margin: 0;
}
#main-header {
width: 100%;
height: 6rem;
display: flex;
justify-content: center;
align-items: center;
background: #a3d2ca;
}
#main-header h1 {
color: white;
margin: 0;
}
footer {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
text-align: center;
}
ul {
list-style: none;
margin: 0;
padding: 0;
}
li {
margin: 1rem 0;
}
section {
margin: 1rem auto;
width: 40rem;
max-width: 90%;
height: 100%;
border-radius: 10px;
}
section ul {
padding: 1rem;
max-height: 30rem;
overflow: scroll;
}
.Days {
padding: 1rem;
max-height: 20rem;
background: #d49a89;
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
/*border: 1px solid #ff0062;*/
margin: 0;
}
section > h2 {
color: white;
margin: 0;
border-radius: 10px;
}
button {
font: inherit;
background: #f7d1ba;
color: white;
border: 1px solid #f7d1ba;
padding: 0.5rem 1.5rem;
cursor: pointer;
}
button.alt {
background: white;
color: #ff0062;
}
button:focus {
outline: none;
}
button:hover,
button:active {
background: #f7d1ba;
border-color: #5eaaa8;
color: white;
}
.card {
border-radius: 10px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.26);
padding: 1rem;
background: white;
}
#active-projects {
border: 2px solid #d49a89;
margin: 10px;
width: 30%;
border-radius: 10px;
}
#active-projects > header {
background: #d49a89;
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
#active-projects header h2 {
color: white;
margin: 0;
}
#finished-projects {
border: 2px solid #d49a89;
margin: 10px;
width: 30%;
border-radius: 10px;
}
#finished-projects > header {
background: #d49a89;
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
#finished-projects header h2 {
color: white;
margin: 0;
border-radius: 10px;
}
div {
display: flex;
flex-direction: row;
align-items: stretch;
justify-content: center;
height: 35rem;
}
#days-projects {
border: 2px solid #d49a89;
margin: 10px;
width: 30%;
border-radius: 10px;
}
#days-projects > header {
background: #d49a89;
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
#days-projects header h2 {
color: white;
margin: 0;
}
#days-projects li:hover {
background: #5eaaa8;
}
.add_Button {
align-items: center;
border: solid red;
}
```
It seems like there may be multiple problems (as #CodeCreate mentions, you are adding an li inside an li, and I'm not sure why you would call createNewButton twice), but I suspect the main issue you're asking about is with your addEventListener call.
You need to pass addEventListener a function to run when the event fires.
You're trying to pass this.addNewListItemHandler, but because you have added parentheses at its end, you are invoking the function immediately, rather than attaching it to run when the button is clicked.
Try rewriting your createNewButton method as:
newButton.addEventListener(
'click',
this.addNewListItemHandler
);
Try instantiating the class after it's defined:
let daysOfWeek = new DaysOfWeek
It's probable that this doesn't need to be a class to begin with, but since that's more a design decision, I'll leave that for you to research and decide.

Convert Class from single Js web page to ReactJs

I'm new to react, and I'm working on a small project that uses a search bar in Single js Web page to find data from API.
The main code for this component is:
const main = () => {
const searchElement = document.querySelector("search-bar");
const clubListElement = document.querySelector("club-list");
const onButtonSearchClicked = async() => {
try{
const result = await DataSource.searchClub(searchElement.value);
renderResult(result);
} catch (message) {
fallbackResult(message)
}
};
const renderResult = (results) => {
clubListElement.clubs = results;
};
const fallbackResult = message => {
clubListElement.renderError(message);
};
searchElement.clickEvent = onButtonSearchClicked;
};
export default main;
also my Search-Bar component:
class SearchBar extends HTMLElement{
constructor(){
super();
this.shadowDOM = this.attachShadow({mode: "open"});
}
connectedCallback(){
this.render();
}
set clickEvent(event){
this._clickEvent = event;
this.render();
}
get value(){
//return this.querySelector("#searchElement").value;
return this.shadowDOM.querySelector("#searchElement").value;
}
render(){
//this.innerHTML = `
this.shadowDOM.innerHTML = `
<style>
.search-container {
max-width: 800px;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
padding: 16px;
border-radius: 5px;
display: flex;
position: sticky;
top: 10px;
background-color: white;
}
.search-container > input {
width: 75%;
padding: 16px;
border: 0;
border-bottom: 1px solid cornflowerblue;
font-weight: bold;
}
.search-container > input:focus {
outline: 0;
border-bottom: 2px solid cornflowerblue;
}
.search-container > input:focus::placeholder {
font-weight: bold;
}
.search-container > input::placeholder {
color: cornflowerblue;
font-weight: normal;
}
.search-container > button {
width: 23%;
cursor: pointer;
margin-left: auto;
padding: 16px;
background-color: cornflowerblue;
color: white;
border: 0;
text-transform: uppercase;
}
#media screen and (max-width: 550px){
.search-container {
flex-direction: column;
position: static;
}
.search-container > input {
width: 100%;
margin-bottom: 12px;
}
.search-container > button {
width: 100%;
}
}
</style>
<div id="search-container" class="search-container">
<input placeholder="Search football club" id="searchElement" type="search">
<button id="searchButtonElement" type="submit">Search</button>
</div>
`;
this.shadowDOM.querySelector("#searchButtonElement").addEventListener("click", this._clickEvent);
}
}
customElements.define("search-bar", SearchBar);
furthermore, can i convert this method to ReactJS? because we know if we can't declare a const in render() from React.
I have been through a bunch of flustered, and I'm not exactly sure how to go about doing that.
Can anyone help me with this please? Any other comments on the code are also welcome because i'm new in Reacts
Thank you before!
First and foremost, you should remove all that CSS declaration within the render method, and abstract them into a separate CSS file (you can consider CSS modules, which is supported by React), use CSS-in-JavaScript libraries(such as styled-components), or inline styles.
Next, instead of using event listeners, you should bind the button element's onClick event with the onButtonSearchClicked method, which is similar to what you have defined.
From that method, you will make the API request, and update your component's state with the response.
class SearchBar extends React.Component {
async onButtonSearchClicked() {
const result = await DataSource.searchClub(searchElement.value);
// set state with return result.
}
render() {
return (
<div id="search-container" class="search-container">
<input placeholder="Search football club" id="searchElement" type="search">
<button id="searchButtonElement" type="submit" onClick={() => this.onButtonSearchClicked}>Search</button>
</div>
);
}
}
One of many variants how could SearchBar component look like in React
class SearchBar extends React.Component {
constructor() {
this.ref = React.createRef(null);
}
componentDidMount() {
this.shadowDOM = this.ref.current.attachShadow({mode: "open"});
this.shadowDOM.innerHTML = getStyle();
}
onClick(event) {
}
render() {
return <div ref={this.ref} id="search-container" class="search-container">
<input placeholder="Search football club" id="searchElement" type="search">
<button onClick={this.onClick} id="searchButtonElement"
type="submit">Search</button>
</div>
}

React JS map array for infinite scroll

I have a simple React App, where I fetch the Flickr Public Feed API and display it. Unfortunately it is mapping the array several times, where I can see repeated photos. The request always returns an array with 20 items with the same pictures, explaining the repetition.
Check the code below:
import React, { Component } from 'react';
import $ from 'jquery';
import PhotoListItem from '../../components/photoListItem';
import Searchbar from '../../components/searchBar';
import ScrollButton from '../../components/scrollButton';
import '../app/index.css';
export default class PhotoApp extends Component {
constructor(props) {
super(props);
this.state = {
photoList: [],
searchTerm: 'cyanotype',
items: 10,
loadingState: false,
}
}
componentDidMount() {
this.getPhotoList();
this.onInfiniteScroll();
}
/* get data from Flickr public feed */
getPhotoList = () => {
const flickrApiPoint = "https://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?&tags=" + this.state.searchTerm;
try {
$.ajax({
url: flickrApiPoint,
dataType: 'jsonp',
data: { format: "json" },
success: function (data) {
this.setState({ photoList: data.items });
}.bind(this)
});
}
catch (err) {
console.log(err);
}
}
/* code for infinite scroll */
onInfiniteScroll = () => {
this.refs.iScroll.addEventListener("scroll", () => {
if (this.refs.iScroll.scrollTop + this.refs.iScroll.clientHeight >= this.refs.iScroll.scrollHeight - 20) {
this.loadMoreItems();
}
});
}
/* */
displayItems = () => {
var items = [];
for (var i = 0; i < this.state.items; i++) {
items.push(
this.state.photoList.map((photo, index) => {
const author = photo.author.split(/"/)[1];
const authorLink = photo.description.split(/"/)[1]
const description = photo.description.split(/"/)[13]
return (
<PhotoListItem
key={index}
url={photo.media.m}
photoLink={photo.link}
title={photo.title}
author={author}
authorLink={authorLink}
description={description}
tags={photo.tags} />
)
})
);
}
return items;
}
/* */
loadMoreItems = () => {
if (this.state.loadingState) {
return;
}
this.setState({ loadingState: true });
setTimeout(() => {
this.setState({ items: this.state.items + 10, loadingState: false });
}, 1000);
}
render() {
return (
<div className='appContainer' ref="iScroll">
<div className='appHeader'>
<h1 className='headerTitle'>Welcome to Flickr Alternative Photography Feed!</h1>
</div>
<div className='gridContainer'>
{this.displayItems()}
</div>
{this.state.loadingState ? <p className='loading'>Loading items...</p> : ""}
</div>
);
}
}
HERE IS THE LIVE EXAMPLE
The problem is around this.displayItems(), but how can I fix this?
Any help is appreciated. Thank you!
You can achieve this by slicing your array by the amount of items you want to show within your JSX :
this.state.photoList.slice(0, this.state.items).map(
You will then have to use the callback version of setState to use the old values of your state and increment what you want to show :
this.setState(old => ({ items: old.items + 2, loadingState: false }));
Fully functional example (using the "full page" option is recommended) :
class PhotoListItem extends React.Component {
render() {
return (
<div className="image-card">
<img className="image-card__image" alt="" src={this.props.url} />
<div className="image-card__body">
<div className="image-title">
<a href={this.props.photoLink}>{this.props.title}</a>
<span className="image-author">
{" "}
by <a href={this.props.authorLink}>{this.props.author}</a>
</span>
</div>
<div className="image-description">
<span className="description">Description:</span>{" "}
{this.props.description}
</div>
<div className="image-tags">
<span className="tags">Tags:</span> {this.props.tags}
</div>
</div>
</div>
);
}
}
class PhotoApp extends React.Component {
constructor(props) {
super(props);
this.state = {
photoList: [],
items: 2,
searchTerm: "cyanotype",
loadingState: false
};
}
componentDidMount() {
this.getPhotoList();
this.onInfiniteScroll();
}
/* get data from Flickr public feed */
getPhotoList = () => {
const flickrApiPoint =
"https://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?&tags=" +
this.state.searchTerm;
try {
$.ajax({
url: flickrApiPoint,
dataType: "jsonp",
data: { format: "json" },
success: function(data) {
this.setState({ photoList: data.items });
}.bind(this)
});
} catch (err) {
console.log(err);
}
};
/* code for infinite scroll */
onInfiniteScroll = () => {
this.refs.iScroll.addEventListener("scroll", () => {
if (
this.refs.iScroll.scrollTop + this.refs.iScroll.clientHeight >=
this.refs.iScroll.scrollHeight - 20
) {
this.loadMoreItems();
}
});
};
/* */
loadMoreItems = () => {
if (this.state.loadingState) {
return;
}
this.setState({ loadingState: true });
setTimeout(() => {
this.setState(old => ({ items: old.items + 2, loadingState: false }));
}, 1000);
this.getPhotoList();
};
render() {
return (
<div className="appContainer" ref="iScroll">
<div className="appHeader">
<h1 className="headerTitle">
Welcome to Flickr Alternative Photography Feed!
</h1>
</div>
<div className="gridContainer">
{this.state.photoList.slice(0, this.state.items).map((photo, index) => {
const author = photo.author.split(/"/)[1];
const authorLink = photo.description.split(/"/)[1];
const description = photo.description.split(/"/)[13];
return (
<PhotoListItem
key={index}
url={photo.media.m}
photoLink={photo.link}
title={photo.title}
author={author}
authorLink={authorLink}
description={description}
tags={photo.tags}
/>
);
})}
</div>
{this.state.loadingState ? (
<p className="loading">Loading items...</p>
) : (
""
)}
</div>
);
}
}
ReactDOM.render(<PhotoApp />, document.getElementById("root"));
body,
html {
margin: 0;
min-height: 100%;
}
.appContainer {
font-family: "Helvetica", sans-serif;
width: 100%;
height: 100vh;
overflow: auto;
}
.appHeader {
text-align: center;
background-color: #033666;
padding: 1rem;
}
.headerTitle {
color: #fff;
}
.gridContainer {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
padding: 1rem;
grid-gap: 1rem 1rem;
}
.loading {
text-align: center;
color: #033666;
}
#media only screen and (max-width: 320px) {
.appHeader>h1 {
font-size: 1.2rem;
}
}
a,
a:visited {
color: #000;
text-decoration: none;
}
a:hover {
color: #033666;
text-decoration: underline;
}
.image-card {
display: flex;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
flex-direction: column;
width: auto;
height: auto;
margin: .5rem;
border-radius: 5px;
box-shadow: 0 5px 15px rgba(0, 0, 0, .15);
background: #fff;
}
.image-card__image {
border-radius: 5px 5px 0 0;
width: 100%;
height: 200px;
object-fit: cover;
}
.image-card__body {
padding: .5rem 1rem 1rem;
}
.image-title {
font-weight: 600;
margin: 0;
word-wrap: break-word;
padding-bottom: .7rem;
cursor: pointer;
}
.image-author {
font-weight: 100;
font-size: .8rem;
cursor: pointer;
}
.image-owner {
margin-top: 0;
font-size: .8rem;
}
.image-date-view-wrapper {
display: flex;
align-items: center;
justify-content: space-between;
}
.image-description {
padding-bottom: .7rem;
font-size: .9rem;
word-wrap: break-word;
}
.tags,
.description {
font-weight: 600;
}
.image-tags {
font-size: .8rem;
word-wrap: break-word;
}
.App {
font-family: sans-serif;
text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
<div id="root" />

How to change an element’s CSS class and remove all other classes on click

How do I handle a case in AngularJS 2 where on click of an element it needs to change its own style, and if other elements have that style they need to have it removed — preferably in one function.
Similar to Angular.js How to change an elements css class on click and to remove all others, only in AngularJS 2, using TypeScript.
Component
https://plnkr.co/edit/Q2BoU4sNnXdaJB5vrZ8h?p=preview
//our root app component
import { NgModule} from '#angular/core'
import {BrowserModule} from '#angular/platform-browser'
import {Component} from '#angular/core';
#Component({
selector: 'my-app',
providers: [],
template: `
<div>
<div (click)="isClassVisible = !isClassVisible;" [ngClass]="{'my-class': isClassVisible }">
> I'm a div that gets styled on click
</div>
<div (click)="isClassVisible = !isClassVisible;" [ngClass]="{'my-class': isClassVisible }">
> I also want to be styled but only when Im clicked
</div>
<div (click)="isClassVisible = !isClassVisible;" [ngClass]="{'my-class': isClassVisible }">
> When one of us gets clicked the we want to be the only one with the style all others go back to normal
</div>
<div (click)="isClassVisible = !isClassVisible;" [ngClass]="{'my-class': isClassVisible }">
> I'm just here cause my creator likes even numbers
</div>
</div>
`,
styles: [
`
.my-class {
background-color: yellow;
}
`
]
})
class App {
isClassVisible: false;
constructor() {
}
}
#NgModule({
imports: [ BrowserModule ],
declarations: [ App ],
bootstrap: [ App ]
})
export class AppModule {}
The easiest fix to your problem is to assign a unique ID to each included element together with employing another variable to hold selected ID. The logic to turn on my-class CSS class will now be based on the selected ID.
Your new HTML template:
<div (click)="toggleHighlight(1);" [ngClass]="{'my-class': highlightedDiv === 1}">
> I'm a div that gets styled on click
</div>
Your toggleHighlight function:
highlightedDiv: number;
toggleHighlight(newValue: number) {
if (this.highlightedDiv === newValue) {
this.highlightedDiv = 0;
}
else {
this.highlightedDiv = newValue;
}
}
Working Plnk: https://plnkr.co/edit/fNoXWhUhMaUoeMihbGYd?p=preview
One solution which worked for me in showing the active menu is using typescript variable name in class as in
class="{{ text1Class }}"
and assign the class name in typescript.
this.text1Class = "active";
or
this.text1Class = "inactive";
You need to have different style class like this
.inactive {
background-color : gray;
padding : 10px;
}
.active {
background-color : green;
padding : 10px;
}
Assign the class name inside the function
textOneClicked() : void {
this.text1Class = "active"; // set the active class
this.text2Class = this.text3Class = this.text4Class = "inactive"; // reset other classes
}
A working Plunker here
I have one hard solution to this problem:
<div (click)="onclick($event);" >
> I'm a div that gets styled on click
</div>
app:
class App {
constructor() {
}
onclick(event){
var l = event.target.parentElement.getElementsByClassName("my-class");
var count = l.length;
for(var i=0;i<count;i++){
l[i].className = "";
}
event.target.className = "my-class";
}
}
Plink: https://plnkr.co/edit/RHqL56GrTiV9olYE1Ars?p=preview
Html code
<div [ngClass]="'first-div'">
<h2 [ngClass]="'heading'">Log In</h2>
<div [ngClass]="content">
<input type="text" placeholder="Enter User Name" name="username" id="username"#username class="in-cl">
<i class="fa fa-user fa-edited" aria-hidden="true"></i>
<input type="{{isPassword ? 'password':'text'}}" placeholder="Password" name="password" id="mypassword" #password class="in-cl">
<i class="fa fa-lock fa-lock-edited" aria-hidden="true" id="lock-id" (click)="onclick()"></i>
<button type="button" class="in-cl" (click)="credential(username.value,password.value)" >SIGN IN</button>
</div>
<div class="forgot">
Forgot Password?
</div>
</div>
css code
.first-div{
width: 350px;
border: 2px solid black;
margin-left: auto;
margin-right: auto;
margin-top: 130px;
border-radius: 5px;
height: 400px;
background-color: black;
color: white;
}
.heading{
color: white;
text-align: center;
font-weight: 500;
/* background-color: white; */
}
.in-cl{
margin: 20px 20px 0px 20px;
border: 2px solid white;
border-radius: 15px;
height: 40px;
padding: 5px;
width: 300px;
outline: none;
color: black;
/* padding-right: 60px; */
}
::placeholder{
color: black;
}
div button{
background-color: #3aafa9;
color:black;
text-align: center;
border: none;
}
.forgot{
margin: 15px;
text-align: center;
font-weight: 550;
color: white;
}
/* .image{
position: absolute;
left: 825px;
top: 222px;
}
.lock-image{
position: absolute;
left: 825px;
top: 282px;
} */
/* edited */
.fa-user:before{
color: black;
}
.fa-lock:before{
color: black;
}
.fa-unlock:before{
color: black;
}
.fa-edited{
top: 228px;
left: 770px;
position: absolute;
width: 28px;
}
.fa-lock-edited{
top: 287px;
left: 772px;
position: absolute;
}
a{
color: white;
}
ts code
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import swal from 'sweetalert2';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent{
username:string="pavithra";
password:string="8792415337abcd";
p = document.getElementById('mypassword');
constructor(private router:Router) {}
credential(username:string,password:string){
if(this.username==username && this.password==password ){
this.router.navigate(['/home'])
swal.fire({title:'Signed in successfully', confirmButtonColor:'#3aafa9', type:'success'})
}
else{
this.router.navigate(['/login'])
swal.fire({title:'Invalid Username or Password',type:'warning',position:'top-end'})
}
}
isPassword = true;
onclick(){
let body = document.getElementById('lock-id')
if (body.classList) {
body.classList.toggle("fa-unlock");
this.isPassword = !(this.isPassword);
} else {
var classes = body.className.split(" ");
console.log(classes)
var i = classes.indexOf("fa-lock");
if (i >= 0)
classes.splice(i, 1);
else
classes.push("fa-unlock");
body.className = classes.join(" ");
}
}
}

Categories

Resources