I got this error:
vue.runtime.esm.js?2b0e:619 [Vue warn]: Avoid mutating a prop directly
since the value will be overwritten whenever the parent component
re-renders. Instead, use a data or computed property based on the
prop's value. Prop being mutated: "isOpen"
Here is my code
My child component:
<template>
<v-dialog v-model="isOpen" max-width="500px">
<v-card>
<v-card-title>Remove</v-card-title>
<v-card-text>Are you sure to delete?</v-card-text>
<v-card-actions>
<v-btn color="primary" text #click="$emit('closedialog')">Close</v-btn>
<!-- <v-btn color="primary" text #click="deleteItem">Delete</v-btn> -->
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
export default {
// name: 'confirmDelete',
props: {
isOpen: Boolean
// selected: Object
}
};
</script>
Parent Component:
<template>
<div class="container">
<div id="app">
<v-app id="inspire">
<v-data-table
:headers="headers"
:items="contracts"
sort-by="createdAt"
class="elevation-1"
>
<template v-slot:top>
<v-toolbar flat color="white">
<v-toolbar-title>CONTRACTS</v-toolbar-title>
<v-divider class="mx-4" inset vertical></v-divider>
<v-spacer></v-spacer>
<v-dialog v-model="dialog" max-width="500px">
<template v-slot:activator="{ on, attrs }">
<v-btn
color="primary"
dark
class="mb-2"
v-bind="attrs"
v-on="on"
>New Contract</v-btn>
</template>
<v-card>
<v-card-title>
<span class="headline">{{ formTitle }}</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12" sm="6" md="4">
<v-text-field
label="Start Contract"
name="name"
prepend-icon="person"
placeholder="YYYY-MM-DD"
type="text"
required
v-model="selectedItem.startDate"
:rules="nameErrors"
#input="$v.selectedItem.startDate.$touch()"
#blur="$v.selectedItem.startDate.$touch()"
#keyup="clearServerErrors('name')"
/>
</v-col>
<v-col cols="12" sm="6">
<v-select
v-model="selectedItem.duration"
:items="[1, 2, 3, 6, 12]"
label="Duration Contract."
required
/>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-select
v-model="selectedItem.leave"
:items="[20, 26]"
label="Days off"
/>
</v-col>
</v-row>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text #click="close">Cancel</v-btn>
<v-btn
color="blue darken-1"
text
#click="onSave"
:disabled="!isValid"
>Save</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-toolbar>
</template>
<template v-slot:item.actions="{ item }">
<v-icon small class="mr-2" #click="editItem(item)">mdi-pencil</v-icon>
<v-icon small #click="showDeleteDialog(item)">mdi-delete</v-icon>
<v-icon middle #click="goToRouteLeaves(item)">play_arrow</v-icon>
</template>
</v-data-table>
<CreateOrEditContract :is-open="isDialogDeleteVisible" #closedialog="close()" />
<!-- <v-dialog v-model="isDialogDeleteVisible" max-width="500px">
<v-card>
<v-card-title>Remove</v-card-title>
<v-card-text>Are you sure to delete?</v-card-text>
<v-card-actions>
<v-btn color="primary" text #click="isDialogDeleteVisible = false">Close</v-btn>
<v-btn color="primary" text #click="deleteItem">Delete</v-btn>
</v-card-actions>
</v-card>
</v-dialog>-->
</v-app>
</div>
</div>
</template>
<script>
import ContractService from '../services/ContractService';
import UserContractsService from '../services/UserContractsService';
import { validationMixin } from 'vuelidate';
import { required } from 'vuelidate/lib/validators';
import CreateOrEditContract from './CreateOrEditContract';
var moment = require('moment');
export default {
name: 'Admin',
components: {
CreateOrEditContract
},
mixins: [validationMixin],
validations: {
selectedItem: {
startDate: {
required,
isStartDate(value) {
return this.isStartDate(value);
}
}
}
},
data() {
return {
selectedItem: {
startDate: ''
},
serverErrors: {
startDate: ''
},
errorMessage: '',
error: null,
validationError: false,
contracts: [],
dialog: false,
isDialogDeleteVisible: false,
headers: [
{ text: 'Start', value: 'startDate' },
{ text: 'Duration', value: 'duration' },
{ text: 'Leave', value: 'leave' },
{ text: 'Actions', value: 'actions', sortable: false }
],
defaultItem: {
startDate: '',
duration: '',
leave: ''
}
};
},
created() {
this.selectedItem = { ...this.defaultItem };
},
async mounted() {
try {
const { userId } = this.$route.params;
const { data } = await UserContractsService.index(userId);
this.contracts = data;
} catch (error) {
this.errorMessage =
(error.response && error.response.data ? error.response.data : null) ||
error.message ||
error.toString();
}
},
computed: {
formTitle() {
return this.selectedItem.id ? 'Edit Contract' : 'New Contract';
},
nameErrors() {
const errors = [];
if (!this.$v.selectedItem.startDate.$dirty) return errors;
!this.$v.selectedItem.startDate.required && errors.push('Date is required');
!this.$v.selectedItem.startDate.isStartDate && errors.push('Enter valid date');
return errors;
},
isValid() {
return !this.$v.$invalid;
}
},
watch: {
dialog(val) {
val || this.close();
}
},
methods: {
editItem(item) {
this.selectedItem = { ...item };
this.dialog = true;
},
async deleteItem() {
const index = this.contracts.findIndex((contract) => contract.id === this.selectedItemlete.id);
this.contracts.splice(index, 1);
this.isDialogDeleteVisible = false;
await ContractService.delete(this.selectedItemlete.id);
this.selectedItemlete = { ...this.defaultItem };
},
showDeleteDialog(item) {
this.selectedItemlete = item;
this.isDialogDeleteVisible = true; //!this.isDialogDeleteVisible;
// this.$emit("clicked", !this.isDialogDeleteVisible)
},
close() {
this.isDialogDeleteVisible = false;
this.dialog = false;
this.selectedItem = { ...this.defaultItem };
},
async onSave() {
if (this.selectedItem.id) {
const index = this.contracts.findIndex((contract) => contract.id === this.selectedItem.id);
await ContractService.save(this.selectedItem);
this.$set(this.contracts, index, this.selectedItem);
} else {
this.selectedItem.userId = this.$route.params.userId;
const { data } = await ContractService.save(this.selectedItem);
this.contracts.push(data);
}
this.close();
},
goToRouteLeaves(item) {
this.$router.push(`/leaves/${item.id}`);
},
clearServerErrors(type) {
this.serverErrors[type] = [];
},
isStartDate(value) {
return moment(value, 'YYYY-MM-DD', true).isValid();
}
}
};
</script>
<style scoped>
v-btn {
position: absolute;
}
</style>
v-model="isOpen" your child component is trying to change the props isOpen.
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders.
make change like below:
Parent:
<CreateOrEditContract :is-open.sync="isDialogDeleteVisible" #closedialog="close()" />
Child:
computed: {
open: {
// getter
get: function () {
return this.isOpen
},
// setter
set: function (newValue) {
this.$emit('update:isOpen', newValue)
}
}
}
<v-dialog v-model="open" max-width="500px">
It's because props work from top to bottom pattern. isOpen passed by parent to child, now it's like data flowing from top to bottom. If your child tries to mutate that data, how will parents get informed about that change? The parent will never get informed this way that's why it's a warning to not change the value of prop passed in child. You need to find a way to communicate to parents and parents will update that prop, this way data flow will not break.
Here v-model is two-way binding which means it will set the value of the property which is isOpen prop.
<template>
<v-dialog v-model="isOpen" max-width="500px">
<v-card>
<v-card-title>Remove</v-card-title>
<v-card-text>Are you sure to delete?</v-card-text>
<v-card-actions>
<v-btn color="primary" text #click="$emit('closedialog')">Close</v-btn>
<!-- <v-btn color="primary" text #click="deleteItem">Delete</v-btn> -->
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
export default {
// name: 'confirmDelete',
props: {
isOpen: Boolean
// selected: Object
}
};
</script>
Hint: You can emit event to inform parent about the change and make parent change the value of isOpen. Try computed getter/setter to achieve this
Related
I am using adminjs for the first time and I have written my own custom component, but adding it to the resource keeps producing an error that says "You have to implement action component for your Action" Here is my resource:
const {Patient} = require('./../models/Patient')
const Components = require('./../components/components')
const { Patient } = require('./../models/Patient')
const Components = require('./../components/components')
const PatientResource = {
resource: Patient,
options: {
actions: {
consultation: {
actionType: 'record',
component: Components.MyCustomAction,
handler: (request, response, context) => {
const { record, currentAdmin } = context
return {
record: record.toJSON(currentAdmin),
msg: 'Hello world',
}
},
},
isAccessible: ({ currentAdmin }) => {
action: if (currentAdmin.role_id === 5) {
return false
} else {
return true
}
},
},
navigation: 'Manage Patients',
listProperties: [
'id',
'last_name',
'first_name',
'sex',
'marital_status',
'blood_group',
'genotype',
'existing_health_condition',
],
},
}
module.exports = { PatientResource }
And this is my custom component:
import React from 'react'
import { Box, H3 } from '#adminjs/design-system'
import { ActionProps } from 'adminjs'
const MyCustomActionComponent = (props: ActionProps) => {
const { record } = props
return (
<Box flex>
<Box
variant='white'
width={1 / 2}
boxShadow='card'
mr='xxl'
flexShrink={0}
>
<H3>Example of a simple page</H3>
<p>Where you can put almost everything</p>
<p>like this: </p>
<p>
<img
src='https://i.redd.it/rd39yuiy9ns21.jpg'
alt='stupid cat'
width={300}
/>
</p>
</Box>
<p> Or (more likely), operate on a returned record:</p>
<Box overflowX='auto'> {JSON.stringify(record)}</Box>
</Box>
)
}
module.exports = { MyCustomActionComponent }
I tried to add the component to my custom defined action "Consultation", Hence I expected to see a custom page which i have designed using react as in "mycustomAction" above
I want to use an xstate state machine in Nuxt 3 which is used over multiple components.
I created a small example of how I want this to look like.
I also use the nuxt-xstate module.
State Machine:
export default createMachine(
{
id: 'toggle',
initial: 'switched_off',
states: {
switched_on: {
on: {
SWITCH: {
target: 'switched_off'
}
}
},
switched_off: {
on: {
SWITCH: {
target: 'switched_on'
},
}
},
},
}
)
Composable:
const toggle = useMachine(toggleMachine)
export function useToggleMachine(){
return { toggle }
}
app.vue:
<template>
<div>
State: {{toggle.state.value.value}}
</div>
<br />
<button
#click="toggle.send('SWITCH')"
>
Switch
</button>
</template>
<script>
import { useToggleMachine } from '~/composables/toggle_machine'
export default {
setup(){
const { toggle } = useToggleMachine()
return { toggle }
}
}
</script>
The problem is, that I can have a look at the state of the machine {{state.value.value}} gives me the expected 'turned_off'. But I cannot call the events to transition between states. When clicking on the button, nothing happens.
Here is the console.log for the passed 'toggle' object:
Does anyone know a way how to fix this, or how to use xstate state machines over multiple components.
I am aware that props work, but I don't really want to have an hierarchical approach like that.
In Nuxt3, it's very simple:
in composables/states.ts
import { createMachine, assign } from 'xstate';
import { useMachine } from '#xstate/vue';
const toggleMachine = createMachine({
predictableActionArguments: true,
id: 'toggle',
initial: 'inactive',
context: {
count: 0,
},
states: {
inactive: {
on: { TOGGLE: 'active' },
},
active: {
// eslint-disable-next-line #typescript-eslint/no-explicit-any
entry: assign({ count: (ctx: any) => ctx.count + 1 }),
on: { TOGGLE: 'inactive' },
},
},
});
export const useToggleMachine = () => useMachine(toggleMachine);
In pages/counter.vue
<script setup>
const { state, send } = useToggleMachine()
</script>
<template>
<div>
<button #click="send('TOGGLE')">
Click me ({{ state.matches("active") ? "✅" : "❌" }})
</button>
<code>
Toggled
<strong>{{ state.context.count }}</strong> times
</code>
</div>
</template>
I am new to React and I load all the data from my database initially on page load but there is info I need to find in an array and apparently it isn't instant. What do I need to do to make sure the render method only renders the objects when the object promises have resolved?
I haven't tried much... I'm really stuck here.
This seems different than the other problems I've read here because I load a bunch on info in the beginning just fine but I need to call some team information every time a function is called so it isn't as simple as loading it once because the object i need is always different.
This code is the main issue. I also included the full file below:
I did some modification to the code in a edit: I realized that I just need to call the opponent team because I have the player team already.
if (team.id === game.team_1) {
var redTeam = team;
// set blueTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_2}')
} else {
var blueTeam = team;
// set redTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_1}')
}
Full file:
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import Async from 'react-promise'
import { withFirebase } from '../Firebase';
// import * as ROUTES from '../../constants/routes';
import { Container, Image, Spinner, Col, Row, Card, Accordion, Button } from 'react-bootstrap'
class PlayerGameList extends Component {
constructor(props) {
super(props);
this.state = {
loadingTeams: false,
loadingSchedule: false,
teams: [],
schedule: []
};
}
componentDidMount() {
this.setState({
loadingTeams: true,
loadingSchedule: true,
});
this.unsubscribe = this.props.firebase
.teams()
.where('players', 'array-contains', '-LXkkB7GNvYrU4UkUMle')
.onSnapshot(snapshot => {
let teams = [];
snapshot.forEach(doc =>
teams.push({ ...doc.data(), uid: doc.id }),
);
this.setState({
teams,
loadingTeams: false,
});
});
this.unsubscribe2 = this.props.firebase
.schedule()
.onSnapshot(snap => {
let schedule = [];
snap.forEach(doc =>
schedule.push({ ...doc.data(), uid: doc.id }),
);
this.setState({
schedule,
loadingSchedule: false,
});
});
}
componentWillUnmount() {
this.unsubscribe();
this.unsubscribe2();
}
render() {
const { teams, schedule, loadingTeams, loadingSchedule } = this.state;
return (
<div>
<h2>Games</h2>
{loadingTeams && loadingSchedule && <div colSpan="12"><Spinner animation="border" role="status">
<span className="sr-only">Loading...</span>
</Spinner></div>}
{/* CONTENT */}
<Container fluid>
<Row>
{getTeams({ teams, schedule })}
</Row>
</Container>
</div >
);
}
}
function getTeams({ teams, schedule }) {
if (!teams) {
return null;
}
if (!teams.length) {
return null;
} else {
return teams.map(team => getGames({ team, schedule }))
}
}
function getGames({ team, schedule }) {
schedule.sort((a, b) => (a.time > b.time) ? -1 : 1)
if (!schedule) {
return null;
}
if (!schedule.length) {
return null;
} else {
return schedule.map(game => guts({ team, game }));
}
}
function guts({ team, game }) {
const image = {
height: '25px',
width: '25px'
}
if (team.id === game.team_1) {
var redTeam = team;
// set blueTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_2}')
} else {
var blueTeam = team;
// set redTeam based on game.team_1
// firebase.teams().doc('teams/{game.team_1}')
}
if (game.team_1 === team.id || game.team_2 === team.id) {
var time = new Date(game.time.seconds * 1000);
var dateFormat = require('dateformat');
var finalTime = dateFormat(time, 'ddd mmm dd, h:MM tt')
return (
<Col lg='4' md='6' sm='12' key={game.uid} style={{ marginBottom: '15px' }}>
<Card>
<Card.Body>
<Row>
<Image src={team.logo} style={image} roundedCircle />
<p>{team.name}</p>
<div style={{ height: '25px', width: '25px', backgroundColor: 'red' }}></div>
</Row>
<Row>
<Image src={team.logo} style={image} roundedCircle />
<p>{team.name}</p>
<div style={{ height: '25px', width: '25px', backgroundColor: 'blue' }}></div>
</Row>
<Row>
<div>
{finalTime}
</div>
</Row>
</Card.Body>
<Accordion>
<Card style={{ margin: '0', padding: '0' }}>
<Card.Header>
<Accordion.Toggle as={Button} variant="link" eventKey="0">
Show Match IDs
</Accordion.Toggle>
</Card.Header>
<Accordion.Collapse eventKey="0">
<Card.Body>{game.match_id}</Card.Body>
</Accordion.Collapse>
</Card>
</Accordion>
</Card>
</Col>
);
}
}
export default withFirebase(PlayerGameList);
The items all load blank then a few seconds later all the console logs come through with the array objects. When I tell it to await the program just throws an error.
I have developed a blog with Gatsby JS and I managed to add categories to each markdown file so that I can create pages by querying a specific category and list all the posts related to that category.
Now, I'm trying to add pagination to avoid an infinite list of posts inside each category page.
I have been following the official guide here: https://www.gatsbyjs.org/docs/adding-pagination/
And this is the code I came up with:
gatsby-node.js
const path = require('path')
const _ = require("lodash")
const { createFilePath } = require("gatsby-source-filesystem")
exports.createPages = ({actions, graphql}) => {
const {createPage} = actions
const articleTemplate = path.resolve(`src/templates/article.js`)
const categoryTemplate = path.resolve("src/templates/category.js")
return new Promise((resolve, reject) => {
resolve(
graphql(
`
{
allMarkdownRemark(
sort: { order: DESC, fields: [frontmatter___date] }
limit: 2000
) {
edges {
node {
html
id
frontmatter {
path
title
categories
}
}
}
}
}
`).then(result => {
if (result.errors) {
reject(result.errors)
}
const articles = result.data.allMarkdownRemark.edges
const articlesPerPage = 6
const numPages = Math.ceil(articles.length / articlesPerPage)
//Creating a page for each article
articles.forEach(({ node }) => {
createPage({
path: node.frontmatter.path,
component: articleTemplate,
//context: {}, // additional data can be passed via context
})
})
// Categories pages:
let categories = []
// Iterate through each article, putting all found categories into `categories`
_.each(articles, edge => {
if (_.get(edge, "node.frontmatter.categories")) {
categories = categories.concat(edge.node.frontmatter.categories)
}
})
Array.from({ length: numPages }).forEach((category, _, i) => {
createPage({
path: i === 0 ? `/${_.kebabCase(category)}/` : `/${_.kebabCase(category)}/${i + 1}`,
component: categoryTemplate,
context: {
limit: articlesPerPage,
skip: i * articlesPerPage,
category,
},
})
})
})
)
})
/templates/categories.js
import React from "react"
import PropTypes from "prop-types"
import Layout from '../layouts/layout'
import ArticleCard from '../components/articles/articlecard'
// Components
import { Link, graphql } from "gatsby"
const _ = require("lodash")
const Categories = ({ pageContext, data }) => {
const { category } = pageContext
const { edges } = data.allMarkdownRemark
return (
<Layout>
<section class="hero is-info is-medium has-text-centered">
<div class="hero-body">
<div class="container">
<h1 class="title is-top">
{category}
</h1>
</div>
</div>
</section>
<div class="section">
<div class="container">
<div class="columns is-multiline">
{edges.map(({ node }) => {
const { path, title, date } = node.frontmatter
return (
<div class="column is-half">
<div class="card">
<div class="card-header">
<p class="card-header-content">{date}</p>
</div>
<div class="card-content">
<Link to={_.kebabCase(category)}><span class="tag is-success has-padding">{category}</span></Link>
<Link to={path}>
<h2 class="title is-4">{title}</h2>
</Link>
</div>
<div class="card-footer">
<div class="card-footer-item"><Link to={path}><div class="button is-success is-inverted is-fullwidth">Read</div></Link></div>
<div class="card-footer-item"><Link to={path}><div class="button is-info is-inverted is-fullwidth">Share on Linkedin</div></Link></div>
</div>
</div>
</div>
)
})}
</div>
</div>
</div>
</Layout>
)
}
Categories.propTypes = {
pageContext: PropTypes.shape({
category: PropTypes.string.isRequired,
}),
data: PropTypes.shape({
allMarkdownRemark: PropTypes.shape({
totalCount: PropTypes.number.isRequired,
edges: PropTypes.arrayOf(
PropTypes.shape({
node: PropTypes.shape({
frontmatter: PropTypes.shape({
path: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
}),
}),
}).isRequired
),
}),
}),
}
export default Categories
export const pageQuery = graphql`
query($skip: Int!, $limit: Int!, $category: String) {
allMarkdownRemark(
sort: { fields: [frontmatter___date], order: DESC }
filter: { frontmatter: { categories: { in: [$category] } } }
limit: $limit
skip: $skip
) {
totalCount
edges {
node {
frontmatter {
title
path
date(formatString: "MMMM DD, YYYY")
}
}
}
}
}
`
This does not work and it is now throwing the error: error gatsby-node.js returned an error, TypeError: _.kebabCase is not a function
However kebabCase was used smoothly before modifying the query to add pagination, so I don't think the problem is actually there.
Does anyone have any clue?
Thank you!
You are declaring the variable "underscore" twice:
1- from the lodash library
2- from the forEach function:
Array.from({ length: numPages }).forEach((category, _, i)
just change the the second variable to another arbitrary name like this:
Array.from({ length: numPages }).forEach((category, otherName, i)
I have parent and child component. I want the parent to render multiple child components with properties specified in an object. I cannot seem to make the loop in the render function work.
var Inputs = React.createClass({
propTypes: {
type: React.PropTypes.string,
xmltag: React.PropTypes.string,
class: React.PropTypes.string
},
getDefaultProps: function () {
return {
type: ' text'
};
},
render: function() {
return (
<div className={'form-element col-xs-6 col-sm-6 ' + this.props.class}>
<label className="col-xs-12">{this.props.text}</label>
<input className="col-xs-12" type={this.props.type} xmltag={this.props.xmltag}></input>
</div>
);
},
});
//OBJECT that needs to be rendered
var formTags = {
id: ["ID", "List ID", "text"],
title: ["TITLE", "List Title", "text"],
said: ["SAID", "SAID", "number"]
};
var InputList = React.createClass({
//PROBLEM STARTS HERE
render: function() {
for (var key in formTags) {
return (
//Not using everything from formTags
<Inputs type="number" text={key}>
);
};
},
//PROBLEM ENDS HERE
});
ReactDOM.render(<InputList />, document.getElementById('mainForm'));
React component must have only one root element, now you are trying render several elements, add one root element, like in example (you can use any elements <div> <p> etc.)
var InputList = React.createClass({
render: function() {
var inputs = Object.keys(this.props.tags).map(function (key) {
return <Inputs key={ key } type="number" text={ key } />;
});
return <div>
{ inputs }
</div>;
}
});
Example