Check checkboxes in CheckboxGroupInput component while editing - admin-on-rest

I have a form that display a Many-to-Many relationship between two entities Brand and Distribution.
I display the distributions through a CheckboxGroupInput component in a brand page. During the form edition (Edit component), I managed to pre-check distributions that have been checked previously during the creation (Create) (this is not specified in the documentation) but I can't check or uncheck any distributions.
Here's the edition form :
export class BrandEdit extends Component {
constructor(props) {
super(props)
this.state = {
isLoading: true
}
}
componentDidMount() {
//Find all distributions
restClient(GET_MANY, 'distribution', {
sort: {field: 'name', order: 'ASC'}
})
.then(response => {
return restClient(GET_ONE, 'brand', {
id: this.props.match.params.id
}).then(brandResult => {
let distributionsIds = []
brandResult.data.distributions.forEach(distribution => {
distributionsIds.push(distribution.id)
})
this.setState({
distributions: response.data,
distribution_checked_ids: distributionsIds,
isLoading: false,
})
});
})
}
render() {
const { isLoading, distributions, distribution_checked } = this.state
return (
<div>
{
<Edit {...this.props}>
<SimpleForm>
<DisabledInput source="id"/>
<TextInput label="Nom" source="name"/>
<CheckboxGroupInput
source="distributions"
input={{
value: this.state.distribution_checked_ids,
onChange: (checkedDistributionIds) => {
this.setState({ distribution_checked_ids: checkedDistributionIds });
}
}}
choices={distributions}
optionValue="id"
optionText="name"
/>
</SimpleForm>
</Edit>
}
</div>
);
}
}
Any ideas ?
Thanks

We need to pass an array of Distribution id to the component and not and array of Distribution objects.
Here's the component :
<CheckboxGroupInput
source="distributions"
choices={distributions}
optionValue="id"
optionText="name"
/>
Data should look like :
{
"id": 2115,
"name": "Test",
"distributions": [12, 13, 14]
}

Please remove the input prop. Why would you handle form state by yourself? AOR uses redux-form to handle that.

Related

Vue-select not loading more when searching

So I made a simple v-select where i put an infinite scroll. This works good I can load all the users and when i scroll down 10 more users are added to the array. When I typ i can filter in the select and i get to see 10 users filtered but when I scroll down there are no 10 users added. I only see loading more options. I have been searching for this quit some time but haven't found an answer for this problem so I thought I try to ask it here...
The only thing I noticed debugging is when I console.log(this.$refs.load)
I see :
<li data-v-299e239e class="loader"> Loading more options...</li>
But when i search nothing is logged so i guess it must be something with the observer or so...
If u need more info please ask.
my code
vue component:
<template>
<v-select
:options="users"
label="name"
:filterable="false"
#open="onOpen"
#close="onClose"
#search="inputSearch"
class="form-control"
:loading="loading"
>
<template #list-footer>
<li v-show="hasNextPage" ref="load" class="loader">
Loading more options...
</li>
</template>
</v-select>
</template>
<script>
import 'vue-select/dist/vue-select.css';
import _ from "lodash";
export default {
name: 'InfiniteScroll',
data: () => ({
observer: null,
limit: 10,
search: '',
users: [],
total: 0,
page: 0,
loading: false,
}),
computed: {
hasNextPage() {
return this.users.length < this.total
},
},
mounted() {
this.observer = new IntersectionObserver(this.infiniteScroll)
},
created() {
this.getUsers();
},
methods: {
getUsers(search) {
this.page++;
axios
.get('users', {
params: {
search: search,
page: this.page,
}
})
.then((response) => {
this.users = this.users.concat(response.data.data);
this.total = response.data.total;
})
.catch()
.then(() => {
this.loading = false;
})
},
async onOpen() {
if (this.hasNextPage) {
await this.$nextTick()
console.log(this.$refs.load)
this.observer.observe(this.$refs.load)
}
},
onClose() {
this.observer.disconnect()
},
async infiniteScroll([{isIntersecting, target}]) {
if (isIntersecting) {
const ul = target.offsetParent
const scrollTop = target.offsetParent.scrollTop
// this.limit += 10
this.getUsers();
await this.$nextTick()
ul.scrollTop = scrollTop
}
},
inputSearch: _.debounce( async function (search, loading) {
if (search.length) {
this.users = []
this.loading = true
this.page = 0
this.getUsers(search, loading)
//await this.$nextTick()
}
}, 500),
},
}
</script>
<style scoped>
.loader {
text-align: center;
color: #bbbbbb;
}
</style>
UserController :
public function users(Request $request){
return User::query()
->when($request->search,function ($q) use ($request) {
$q->where('name', 'like', '%' . $request->search . '%');
})
->orderBy('name', 'ASC')->paginate(10);
}
I've fixed the issue with adding two code lines simply.
...
inputSearch: _.debounce( async function (search, loading) {
if (search.length) {
this.users = []
this.loading = true
this.page = 0
this.getUsers(search, loading)
//await this.$nextTick()
// Add following code lines for reloading the infiniteScroll observer
this.onClose()
await this.onOpen()
}
}, 500),
...

React Redux Material-UI autocomplete

I am struggling to get the value out of the Material-ui Autocomplete when using redux-form. Has anyone solved this? I am using the exact example from the material-ui Autocomplete https://material-ui.com/components/autocomplete/ I am able to see the list options and it populates after clicking it twice, but I am unable to extract the real value, instead I am returning ({ title : 0 }) instead of the value.
import React from "react";
import TextField from "#material-ui/core/TextField";
import Autocomplete from "#material-ui/lab/Autocomplete";
import { Field, reduxForm } from "redux-form";
import { connect } from "react-redux";
class Form extends React.Component {
onSubmit = formValues => {
console.log(formValues);
};
renderTextField = ({
label,
input,
meta: { touched, invalid, error },
...custom
}) => (
<Autocomplete
label={label}
options={this.props.movies}
placeholder={label}
getOptionLabel={option => option.title}
onChange={input.onChange}
{...input}
{...custom}
renderInput={params => (
<TextField {...params} label={label} variant="outlined" fullWidth />
)}
/>
);
render() {
const { handleSubmit } = this.props;
return (
<div>
<form onSubmit={handleSubmit(this.onSubmit)}>
<Field
name="propertySelector"
component={this.renderTextField}
label="Select Property"
type="text"
/>
</form>
</div>
);
}
}
const mapStateToProps = state => {
console.log(state);
return {
movies: [
{ title: "The Shawshank Redemption", year: 1994 },
{ title: "The Godfather", year: 1972 },
{ title: "Schindler's List", year: 1993 }
]
};
};
Form = reduxForm({
form: "auto_complete"
});
export default connect(mapStateToProps, null)(Form);
Solved by passing in the (event, value) to the onChange props.
onChange={(event, value) => console.log(value)}
From the docs;
Callback fired when the value changes.
Signature:
function(event: object, value: T) => void
event: The event source of the callback.
value: null

data not stored in laravel back end from axios post of vue js front end

I have some data that i need to store in my database coming from localStorage. One is an input field where a user can change the quantity of an item, the other is a radio box where the user has to select an address to deliver to. The issue is that it just won't store the address_id in my database nor update the qty field of the input in my cart. Could someone take a look at it and explain why this doesn't work? I'd appreciate it so much thanks!
Note: The data object is reactive, I checked in my vue dev tools and it changes the data correclty so no issue there.
Vue add to cart button component:
<template>
<form #submit.prevent="addToCart">
<button>Add to cart</button>
</form>
</template>
<script>
export default {
props: ['productId', 'categoryId'],
data() {
return {
product: {},
cart: []
}
},
methods: {
fetchData() {
axios.get(`http://localhost:8000/api/product/${this.productId}/category/${this.categoryId}`)
.then(res => {
const { product } = res.data;
this.product.id = product.id
this.product.title = product.title;
this.product.fitSize = product.fit_size;
this.product.jeansSize = product.jeans_size;
this.product.price = product.price;
this.product.img = product.images[0].images;
this.product.quantity = 1;
})
.catch(err => console.log(err))
},
addToCart() {
if(localStorage.getItem('cart')) {
// Little note to myself:
// Grab data from local storage and turn it into a js array of objects
// Push new product to that array every time the addToCart method gets fired
// get that array with the additional items and store back in local storage as a JSON string
let cart = JSON.parse(localStorage.getItem('cart'));
cart.push(this.product)
return localStorage.setItem('cart', JSON.stringify(cart))
} else {
this.cart.push(this.product)
localStorage.setItem('cart', JSON.stringify(this.cart))
}
}
},
mounted() {
this.fetchData()
}
}
</script>
Vue checkout component:
<template>
<div>
<div v-for="product in products" v-bind:key="product.id">
<p>Img: {{product.img}}</p>
<p>Title: {{product.title}}</p>
<p>Price: {{product.price}}</p>
<input v-model="product.quantity" type="number" name="qty" id="qty">
<br>
</div>
<form method="POST" #submit.prevent="placeOrder">
<div v-for="address in user.addresses" v-bind:key="address.id">
<input type="radio" v-model="addressId" v-bind:value="address.id" name="address_id" id="address_id">{{address.street}}
</div>
<button>Place order</button>
</form>
</div>
</template>
<script>
export default {
props: ['user'],
data() {
return {
products: null,
addressId: null,
orders: [],
}
},
methods: {
fetchDataFromLocalStorage() {
this.products = JSON.parse(localStorage.getItem('cart'))
},
createOrders() {
this.products.forEach(product => {
this.orders.push({
qty: product.quantity,
user_id: this.user.id,
product_id: product.id,
address_id: this.addressId
})
})
},
placeOrder() {
const sendOrders = { orders: this.orders }
axios.post(`http://localhost:8000/api/user/${this.user.id}/checkout`, sendOrders)
.then(res => console.log(res.data))
.catch(err => console.log(err))
},
},
mounted() {
this.fetchDataFromLocalStorage();
this.createOrders();
}
}
</script>
Laravel store method:
public function store(Request $request, $id)
{
foreach ($request->orders as $order) {
Order::create([
'qty' => $order['qty'],
'product_id' => $order['product_id'],
'user_id' => $order['user_id'],
'address_id' => $order['address_id']
]);
}
return response()->json('Order created');
}
Order model:
class Order extends Model
{
protected $guarded = [];
public function product()
{
return $this->hasOne(Product::class);
}
public function user()
{
return $this->belongsTo(Order::class);
}
public function addresses()
{
return $this->hasOne(Addresses::class);
}
}
Laravel api route:
Route::post('/user/{id}/checkout', 'CheckoutController#store');
Tinker when I make a post request:
App\Order {#3072
id: "127",
qty: "1", // Default value just won't change whenever I try to update it!
delivered: "0",
product_id: "3", // This works
created_at: "2019-12-23 10:08:09",
updated_at: "2019-12-23 10:08:09",
user_id: "1", // This works
address_id: null, // This doesn't
},
],

Auto fill input field after Select dropdown list [ Laravel, Vuejs ]

I want to create something that search in Product table and i select in dropdown its will display data in order table like pic below?
how can i archieve that? Anyone give me any hint?
thanks in advances...
In my Vuetify
i used vue-select package
<v-col md="6" cols="12">
<label class="font-weight-bold">Select Product</label>
<v-select v-model="search" label="name" :options="purchases"></v-select>
</v-col>
In my script file
<script>
export default {
created() {
this.fetchData()
},
data() {
return {
form: {},
search: '',
items: [],
purchase_status: ['Received', 'Partial', 'Pending', 'Ordered'],
}
},
methods: {
fetchData() {
this.$axios.$get(`api/product`)
.then(res => {
this.items = res.data;
console.log(this.items)
})
.catch(err => {
console.log(err)
})
},
uploadFile(event) {
const url = 'http://127.0.0.1:3000/product/add_adjustment';
let data = new FormData();
data.append('file', event.target.files[0]);
let config = {
header: {
'content-Type' : 'image/*, application/pdf'
}
}
this.$axios.$post(url,data,config)
.then(res => {
console.log(res);
})
}
}
}
</script>
When you select a product in the dropdown, you'll get product_id. Use this product_id and call API to get data for the orders table.

Can't get Calendar to work with Redux Forms

Trying to get Calendar to work with a Redux Form
Using a Redux Form Field:
<Field name={name} component={this.renderCal}/>
which is leveraging a stateless function:
renderCal({input, ...rest}) {
input.value = new Date();
return <Calendar {...input}
onChange={() => input.onChange(input.value)}
value={input.value}
{...rest}/>
}
When I submit the form, the value is still null. I appears like the value is not bound to the component. This is my request payload from the Chrome Developer Tools > Network ...
inputs : [{name: "fromDate", title: "From Date", dataType: "date", format: "mm/dd/yyyy", value: null}] 0 : {name: "fromDate", title: "From Date", dataType: "date", format: "mm/dd/yyyy", value: null} dataType : "date" format : "mm/dd/yyyy" name : "fromDate" title : "From Date" value : null
Does anyone have Calendar working with Redux Form as a stateless function?
Thanks,
Steve
Steve and I found a solution. Posting back our findings for everyone else...
To clarify, the initial state/shape of the form is actually an array of dates:
{
myDates: [
{myDate: null},
{myDate: null},
{myDate: null}
]
}
If you just need to tie DateTimePicker up to a Redux Form field then you can drop renderDynamicFields and use renderDynamicField directly:
<Field name={name} component={renderDynamicField}/>
Points of Note:
Changed directions slightly and are now using DateTimePicker as opposed to Calendar because we also needed the time portion. That being said the solution should work with Calendar too.
Used Redux Form FieldArray
DateTimePicker needed a "defaultValue"
Solution
import React from "react";
import {Button, Col, Form, FormGroup, ControlLabel} from "react-bootstrap";
import {reduxForm, Field, FieldArray} from "redux-form";
import {connect} from "react-redux";
import {DateTimePicker} from "react-widgets";
class ReactWidgets extends React.Component {
constructor(props) {
super(props);
this.renderDynamicFields = this.renderDynamicFields.bind(this);
}
submit(form) {
console.log('form', form);
}
renderDynamicFields({fields = []}) {
let collection = [];
fields.map((name, index) => {
collection.push(<Field key={index} name={`${name}.myDate`} component={renderDynamicField}/>);
});
return <div>{collection}</div>;
function renderDynamicField(props) {
const {input} = props;
let component = null;
if (true) { // going to support different types...
component = <DateTimePicker
defaultValue={input.value || new Date()}
onChange={(value) => {
input.onChange(value);
}}/>;
}
return component;
}
}
render() {
const {error, handleSubmit, pristine, reset, submitting} = this.props;
return (
<Form horizontal onSubmit={handleSubmit(this.submit)}>
<FormGroup><Col componentClass={ControlLabel} xs={2}>
My Date2</Col>
<Col xs={4}>
<FieldArray name="myDates" component={this.renderDynamicFields}/>
</Col>
</FormGroup>
<FormGroup>
<Col smOffset={1} xs={11}>
<Button type="submit" bsStyle="primary"
disabled={pristine || submitting}>Save</Button>
<Button type="button" disabled={pristine || submitting}
onClick={reset}>Reset</Button>
</Col>
</FormGroup>
</Form>
);
}
}
ReactWidgets = reduxForm({
form: 'reactWidgets'
})(ReactWidgets);
ReactWidgets = connect(
state => {
return {
initialValues: {
myDates: [
{myDate: null},
{myDate: null},
{myDate: null}
]
},
}
})(ReactWidgets)
export default ReactWidgets

Resources