rendering events from the database with Vue3 and Inertia - laravel

I'm having rather great difficulty getting my Laravel 9 FullCalendar 5 with Vue 3 app to pull events from the database though Vue3. I'm assuming I need some help formulating the axios script in the index.vue.
Here's my Index.vue
<template>
<head title="Dashboard" />
<BreezeAuthenticatedLayout>
<template #header>
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
Resource Calendar Timeline
</h2>
</template>
<div class="py-12">
<div class="max-w-10xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-12 bg-white border-b border-gray-200">
<div class='demo-app'>
<div class='demo-app-main'>
<FullCalendar
class='demo-app-calendar'
:options='calendarOptions'
:events='calendarEvents'>
<template v-slot:eventContent='arg'>
<b>{{ arg.timeText }}</b>
<i>{{ arg.event.title }}</i>
</template>
</FullCalendar>
</div>
</div>
</div>
</div>
</div>
</div>
</BreezeAuthenticatedLayout>
</template>
<!--start calendar-->
<script setup lang='ts'>
import BreezeAuthenticatedLayout from '#/Layouts/AuthenticatedLayout.vue';
import { head, Link } from '#inertiajs/inertia-vue3';
import '#fullcalendar/core/vdom'; // solves problem with Vite
import FullCalendar from '#fullcalendar/vue3';
import dayGridPlugin from '#fullcalendar/daygrid';
import timeGridPlugin from '#fullcalendar/timegrid';
import listPlugin from '#fullcalendar/list';
import resourceTimelinePlugin from '#fullcalendar/resource-timeline';
import interactionPlugin from '#fullcalendar/interaction';
import axios from 'axios';
</script>
<script lang='ts'>
export default {
components: {FullCalendar},
data() {
return {
calendarEvents:
{
url: 'show-events'
},
calendarOptions: {
plugins: [
dayGridPlugin,
timeGridPlugin,
resourceTimelinePlugin,
listPlugin,
interactionPlugin // needed for dateClick
],
headerToolbar: {
left: 'promptResource prev,next today',
center: 'title',
right: 'resourceMonth,dayGridMonth,timeGridWeek,timeGridDay,listMonth'
},
views: {
resourceMonth: {
type: 'resourceTimelineMonth',
buttonText: 'personnel'
}
},
customButtons: {
promptResource: {
text: '+ personnel',
click: function() {
var title = prompt('Name');
if (title) {
event.addResource({
title: title
});
}
}
}
},
initialView: 'dayGridMonth',
editable: true,
selectable: true,
selectMirror: true,
dayMaxEvents: true,
weekends: true,
select: this.handleDateSelect,
eventClick: this.handleEventClick,
eventsSet: this.handleEvents
} as CalendarOptions,
currentEvents: [] as EventApi[],
}
},
methods: {
handleWeekendsToggle() {
this.calendarOptions.weekends = !this.calendarOptions.weekends // update a property
},
handleDateSelect(selectInfo: DateSelectArg) {
let title = prompt('Please enter a new title for your event')
let calendarApi = selectInfo.view.calendar
calendarApi.unselect() // clear date selection
if (title) {
calendarApi.addEvent({
id: createEventId(),
title,
start: selectInfo.startStr,
end: selectInfo.endStr,
allDay: selectInfo.allDay
})
}
},
handleEventClick(clickInfo: EventClickArg) {
if (confirm(`Are you sure you want to delete the event '${clickInfo.event.title}'`)) {
clickInfo.event.remove()
}
},
handleEvents(events: EventApi[]) {
this.currentEvents = events
},
}
}
</script>
here's my controller
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Inertia\Inertia;
use App\Models\Appointment;
use App\Models\Booking;
use App\Models\Event;
use Illuminate\Support\Facades\Validator;
class AppointmentController extends Controller
{
/**
* Show the form for creating a new resource.
*
* #return Response
*/
public function index()
{
{
$events = array();
$bookings = Event::all();
foreach($bookings as $booking) {
$color = null;
if($booking->title == 'Test') {
$color = '#924ACE';
}
if($booking->title == 'Test 1') {
$color = '#68B01A';
}
$events[] = [
'id' => $booking->id,
'title' => $booking->title,
'start' => $booking->start_date,
'end' => $booking->end_date,
'color' => $color
];
}
//return view('calendar.index', ['events' => $events]);
return Inertia::render('Appointments/Index', ['events' => $events]);
}
}
public function showEvents(Request $request) {
$event = Event::get(['title','acronym','city','venue','value','start','end']);
return response()->json(["events" => $event]);
}
public function getEvents(Request $request) {
$events = array();
$events = Event::all();
foreach($events as $event) {
$color = null;
if($event->title == 'Test') {
$color = '#924ACE';
}
if($event->title == 'Test 1') {
$color = '#68B01A';
}
$events[] = [
'id' => $event->id,
'title' => $event->event_name,
'start' => $event->start_date,
'end' => $event->end_date,
'color' => $color
];
}
return Inertia::render('Events/Index', ['events' => $events]);
}
$events = array();
$events = Event::all();
foreach($events as $event) {
$color = null;
if($event->title == 'Test') {
$color = '#924ACE';
}
if($event->title == 'Test 1') {
$color = '#68B01A';
}
$events[] = [
'id' => $event->id,
'title' => $event->event_name,
'start' => $event->start_date,
'end' => $event->end_date,
'color' => $color
];
}
return Inertia::render('Events/Index', ['events' => $events]);
}
*/
public function store(Request $request)
{
$assignee =
($request->assignee && auth()->user()->isAdmin ?
$request->assignee :
$assignee = auth()->user()->id)->get();
$appointment = new Appointment([
'start_date' => $request->start,
'end_date' => $request->end,
'event_name' => $request->title,
'acronym' => $request->description,
'user_id' => $assignee
]);
$appointment->save();
return $event;
}
public function filter(Request $request)
{
if (auth()->user()->isAdmin) {
return Appointment::whereBetween('start', [$request->start, $request->end])
->with('user:id,name,lastname')
->get();
} else {
return Appointment::whereBetween('start', [$request->start, $request->end])
->where('user_id', auth()->user()->id)
->get();
}
}
public function update(Request $request, Appointment $appointment)
{
$user = auth()->user();
if ($appointment->user_id === $user->id || $user->isAdmin) {
$appointment->update($request->all());
} else {
abort(403);
}
}
public function destroy(Appointment $appointment)
{
$user = auth()->user();
if ($appointment->user_id === $user->id || $user->isAdmin) {
$appointment->delete();
} else {
abort(403);
}
}
}
and here's the JSON output of the events I'm trying to pull in from the 'show-events' endpoint
{"events":[{"title":"Test","acronym":"TST","city":"Denver","venue":"Ceavco","value":"$0","start":"2022-10-10 00:00:00","end":"2022-10-28 00:00:00"},{"title":"Test 2","acronym":"TST2","city":"boston","venue":"AVFX","value":"$0","start":"2022-10-05 00:00:00","end":"2022-10-07 00:00:00"}]}
What am I missing?
Something in the methods or the export on the Vue element?

Thanks to you both for steering me in the right direction. I managed to get it working with the following Index.vue:
<script setup lang='ts'>
import BreezeAuthenticatedLayout from '#/Layouts/AuthenticatedLayout.vue';
import { head, Link } from '#inertiajs/inertia-vue3';
import '#fullcalendar/core/vdom'; // solves problem with Vite
import FullCalendar from '#fullcalendar/vue3';
import dayGridPlugin from '#fullcalendar/daygrid';
import timeGridPlugin from '#fullcalendar/timegrid';
import listPlugin from '#fullcalendar/list';
import resourceTimelinePlugin from '#fullcalendar/resource-timeline';
import interactionPlugin from '#fullcalendar/interaction';
import axios from 'axios';
import { INITIAL_EVENTS, createEventId } from '#/components/event-utils.ts';
// import { INITIAL_RESOURCES, createResourceId } from '#/components/resource-utils.ts';
</script>
<script lang='ts'>
export default {
components: {FullCalendar},
data() {
return {
calendarOptions: {
plugins: [
dayGridPlugin,
timeGridPlugin,
resourceTimelinePlugin,
listPlugin,
interactionPlugin // needed for dateClick
],
events: [],
headerToolbar: {
left: 'promptResource prev,next today',
center: 'title',
right: 'resourceMonth,dayGridMonth,timeGridWeek,timeGridDay,listMonth'
},
views: {
resourceMonth: {
type: 'resourceTimelineMonth',
buttonText: 'personnel'
}
},
customButtons: {
promptResource: {
text: '+ personnel',
click: function() {
var title = prompt('Name');
if (title) {
event.addResource({
title: title
});
}
}
}
},
initialView: 'resourceTimelineMonth',
editable: true,
},
created() {
this.getEvents();
},
methods: {
getEvents() {
axios.get('show-events')
.then(response => {
this.calendarOptions.events = response.data.events;
});
},
}
</script>

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),
...

Unable to insert data in the database in laravel projet with vuejs and vuetify

I have setup a laravel project with Vuejs and vuetify to insert data in the database. But for some reason I not able to insert data in the database. When I compile my code I can see no error but when I inspect my code I can see error in my console saying "Internal service error" . I assuming that the error may be in my controller.
Here are m code:
Location.vue
<template>
<v-app id="inspire">
<v-data-table
item-key="code"
class="elevation-1"
color="error"
:loading = "loading"
loading-text="Loading... Please wait"
:headers="headers"
:options.sync="options"
:server-items-length="locations.total"
:items="locations.data"
show-select
#input="selectAll"
:footer-props="{
itemsPerPageOptions: [5,10,15],
itemsPerPageText: 'Roles Per Page',
'show-current-page': true,
'show-first-last-page': true
}"
>
<template v-slot:top>
<v-toolbar flat color="dark">
<v-toolbar-title>My Stage</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 }">
<v-btn color="error" dark class="mb-2" v-on="on">Add New Stage</v-btn>
<v-btn color="error" dark class="mb-2 mr-2" #click="deleteAll">Delete</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="12" >
<v-text-field autofocus color="error" v-model="editedItem.code" label="Code"></v-text-field>
</v-col>
<v-col cols="12" sm="12" >
<v-text-field autofocus color="error" v-model="editedItem.name" label="Name"></v-text-field>
</v-col>
<v-col cols="12" sm="12" >
<v-text-field autofocus color="error" v-model="editedItem.description" label="Description"></v-text-field>
</v-col>
</v-row>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="error darken-1" text #click="close">Cancel</v-btn>
<v-btn color="error darken-1" text #click="save">Save</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-toolbar>
<v-row>
<v-col cols="12">
<v-text-field #input="searchIt" label="Search..." class="mx-4" ></v-text-field>
</v-col>
</v-row>
</template>
<template v-slot:item.action="{ item }">
<v-icon
small
class="mr-2"
#click="editItem(item)"
>
mdi-content-save-edit-outline
</v-icon>
<v-icon
small
#click="deleteItem(item)"
>
mdi-delete
</v-icon>
</template>
<template v-slot:no-data>
<v-btn color="error" #click="initialize">Reset</v-btn>
</template>
</v-data-table>
<v-snackbar v-model="snackbar" >
{{text}}
<v-btn
color="error"
text
#click="snackbar = false"
>
Close
</v-btn>
</v-snackbar>
</v-app>
</template>
<script>
export default {
data: () => ({
dialog: false,
loading: false,
snackbar: false,
selected: [],
text: '',
options:{
itemsPerPage: 5,
sortBy:['id'],
sortDesc: [false]
},
headers: [
{text: '#',align: 'left', sortable: false,value: 'id'},
{ text: 'Code', value: 'code' },
{ text: 'Name', value: 'name' },
{ text: 'Description', value: 'description' },
{ text: 'Actions', value: 'action'},
],
locations: [],
editedIndex: -1,
editedItem: {
id: '',
code: '',
name: '',
description: '',
},
defaultItem: {
id: '',
code: '',
name: '',
description: '',
},
}),
computed: {
formTitle () {
return this.editedIndex === -1 ? 'New Stage' : 'Edit Stage'
},
},
watch: {
dialog (val) {
val || this.close()
},
options:{
handler(e){
console.dir(e);
const sortBy = e.sortBy.length>0 ? e.sortBy[0].trim() : 'id';
const orderBy = e.sortDesc[0] ? 'desc' : 'asc';
axios.get(`/api/locations`,{params:{'page':e.page, 'per_page':e.itemsPerPage, 'sort_by': sortBy, 'order_by': orderBy}})
.then(res => {
this.locations = res.data.locations
})
},
deep: true
}
},
created () {
this.initialize()
},
methods: {
selectAll(e){
this.selected = [];
if(e.length > 0){
this.selected = e.map(val => val.id)
}
},
deleteAll(){
let decide = confirm('Are you sure you want to delete these items?')
if(decide){
axios.post('/api/locations/delete', {'locations': this.selected})
.then(res => {
this.text = "Records Deleted Successfully!";
this.selected.map(val => {
const index = this.locations.data.indexOf(val)
this.locations.data.splice(index, 1)
})
this.snackbar = true
}).catch(err => {
console.log(err.response)
this.text = "Error Deleting Record"
this.snackbar=true
})
}
},
searchIt(e){
if(e.length > 3){
axios.get(`/api/locations/${e}`)
.then(res => this.locations = res.data.locations)
.catch(err => console.dir(err.response))
}
if(e.length<=0){
axios.get(`/api/locations`)
.then(res => this.locations = res.data.locations)
.catch(err => console.dir(err.response))
}
},
paginate(e){
const sortBy = e.sortBy.length>0 ? e.sortBy[0].trim() : 'code';
const orderBy = e.sortDesc[0] ? 'desc' : 'asc';
axios.get(`/api/locations`,{params:{'page':e.page, 'per_page':e.itemsPerPage, 'sort_by': sortBy, 'order_by': orderBy}})
.then(res => {
this.locations = res.data.locations
})
},
initialize () {
// Add a request interceptor
axios.interceptors.request.use((config) => {
this.loading = true;
return config;
}, (error) => {
this.loading = false;
return Promise.reject(error);
});
// Add a response interceptor
axios.interceptors.response.use( (response) => {
this.loading = false;
return response;
}, (error) => {
this.loading = false
return Promise.reject(error);
});
},
editItem (item) {
this.editedIndex = this.locations.data.indexOf(item)
this.editedItem = Object.assign({}, item)
this.dialog = true
},
deleteItem (item) {
const index = this.locations.data.indexOf(item)
let decide = confirm('Are you sure you want to delete this item?')
if(decide){
axios.delete('/api/locations/'+item.id)
.then(res => {
this.text = "Record Deleted Successfully!";
this.snackbar = true
this.locations.data.splice(index, 1)
}).catch(err => {
console.log(err.response)
this.text = "Error Deleting Record"
this.snackbar=true
})
}
},
close () {
this.dialog = false
setTimeout(() => {
this.editedItem = Object.assign({}, this.defaultItem)
this.editedIndex = -1
}, 300)
},
save () {
if (this.editedIndex > -1) {
const index = this.editedIndex
axios.put('/api/locations/'+this.editedItem.id, {'code': this.editedItem.code, 'name': this.editedItem.name, 'description': this.editedItem.description})
.then(res => {
this.text = "Record Updated Successfully!";
this.snackbar = true;
Object.assign(this.locations.data[index], res.data.location)
})
.catch(err => {
console.log(err.response)
this.text = "Error Updating Record"
this.snackbar=true
})
// Object.assign(this.locations[this.editedIndex], this.editedItem)
} else {
axios.post('/api/locations',{'code': this.editedItem.code, 'name': this.editedItem.name, 'description': this.editedItem.description})
.then(res => {
this.text = "Record Added Successfully!";
this.snackbar=true;
this.locations.data.push(res.data.location)
})
.catch(err => {
console.dir(err.response)
this.text = "Error Inserting Record"
this.snackbar=true
})
}
this.close()
},
},
}
</script>
<style scoped></style>
Location.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Location extends Model
{
protected $guarded = [];
}
LocationController.php
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Location;
class LocationController extends Controller
{
public function index(Request $request)
{
$per_page = $request->per_page ? $request->per_page : 5;
$sort_by = $request->sort_by;
$order_by = $request->order_by;
return response()->json(['locations' => Location::orderBy($sort_by, $order_by)->paginate($per_page)],200);
}
public function store(Request $request)
{
$location= Location::create([
'code' =>$request->code
'name' =>$request->name
'description' =>$request->description
]);
return response()->json(['location'=>$location],200);
}
public function show($id)
{
$locations = Location::where('code', 'name','description''LIKE', "%$id%")->paginate();
return response()->json(['locations' => $locations],200);
}
public function update(Request $request, $id)
{
$location = Location::find($id);
$location->code = $request->code;
$location->name = $request->name;
$location->description = $request->description;
$location->save();
return response()->json(['location'=>$location], 200);
}
public function destroy($id)
{
$location = Location::find($id)->delete();
return response()->json(['location'=>$location],200);
}
public function deleteAll(Request $request){
Location::whereIn('id', $request->locations)->delete();
return response()->json(['message', 'Records Deleted Successfully'], 200);
}
}
yeah, the issue in your LocationController.php file, you missed commas in Location::create . Also, show function has a problem. notice carefully.
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Location;
class LocationController extends Controller
{
public function index(Request $request)
{
$per_page = $request->per_page ? $request->per_page : 5;
$sort_by = $request->sort_by;
$order_by = $request->order_by;
return response()->json(['locations' => Location::orderBy($sort_by, $order_by)->paginate($per_page)],200);
}
public function store(Request $request)
{
$location= Location::create([
'code' =>$request->code,
'name' =>$request->name,
'description' =>$request->description
]);
return response()->json(['location'=>$location],200);
}
public function show($id)
{
$locations = Location::where('code','LIKE', "%$id%")->orWhere('name','LIKE', "%$id%")->orWhere('description', 'LIKE', "%$id%")->paginate();
return response()->json(['locations' => $locations],200);
}
public function update(Request $request, $id)
{
$location = Location::find($id);
$location->code = $request->code;
$location->name = $request->name;
$location->description = $request->description;
$location->save();
return response()->json(['location'=>$location], 200);
}
public function destroy($id)
{
$location = Location::where('id', $id)->delete();
return response()->json(['location'=>$location],200);
}
public function deleteAll(Request $request){
Location::whereIn('id', $request->locations)->delete();
return response()->json(['message', 'Records Deleted Successfully'], 200);
}
}

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
},
],

Use tab key not working on Prime-react editable Data table

I used an editable data Table of prime-react , and it worked mouse click. But Problem is tab key not work.I want to enable editable mode one cell to another cell using tab key. I use Prime react version 1.4.0
A full code are given below:-
index.js
where my editable table code contains
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { FormattedMessage } from 'react-intl';
import { createStructuredSelector } from 'reselect';
import { compose } from 'redux';
import injectSaga from 'utils/injectSaga';
import injectReducer from 'utils/injectReducer';
import reducer from './reducer';
import saga from './saga';
import messages from './messages';
import { Messages } from 'primereact/components/messages/Messages';
import { Growl } from 'primereact/components/growl/Growl';
import { Panel } from 'primereact/components/panel/Panel';
import { Button } from 'primereact/components/button/Button';
import { DataTable } from 'primereact/components/datatable/DataTable';
import { Column } from 'primereact/components/column/Column';
import { InputText } from 'primereact/components/inputtext/InputText';
import { Dropdown } from 'primereact/components/dropdown/Dropdown';
import CustomDataTable from 'components/CustomDataTable';
import { makeSelectSelectedStudent, makeSelectSectionName, makeSelectStudentUpdateBasicInformation, makeSelectSectionList, makeSelectStdBasicInfo, makeSelectEditorStudent, makeSelectSetMessage, makeSelectSetErrMessage, makeSelectLoaderOff, makeSelectLoaderOn } from './selectors';
import { selectStudent, setEditorData, submitUpdate, getMessage, getErrMessage, submitForm, changeSectionName, getLoaderOn, getLoaderOff } from './actions';
import AppPrivateLayout from '../AppPrivateLayout';
export class StudentUpdateBasicInformation extends React.Component {
componentDidUpdate() {
this.props.changeMessage('');
}
rollBody(rowData) {
return <InputText type="text" value={rowData.studentRoll} />;
}
nameBody(rowData) {
return <InputText type="text" value={rowData.studentName} />;
}
fatherNameBody(rowData) {
return <InputText type="text" value={rowData.fatherName} />;
}
motherNameBody(rowData) {
return <InputText type="text" value={rowData.motherName} />;
}
contactBody(rowData) {
return <InputText type="text" value={rowData.guardianMobile} />;
}
genderBody(rowData) {
let gender = [
{ label: 'Male', value: 'Male' },
{ label: 'Female', value: 'Female' },
{ label: 'Others', value: 'Others' }
];
return <Dropdown value={rowData.studentGender} options={gender} style={{ width: '100%' }} placeholder="Select" />
}
religionBody(rowData) {
let religion = [
{ label: 'Islam', value: 'Islam' },
{ label: 'Hindu', value: 'Hindu' },
{ label: 'Buddhist', value: 'Buddhist' },
{ label: 'Christian', value: 'Christian' },
{ label: 'Others', value: 'Others' }
];
return <Dropdown value={rowData.studentReligion} options={religion} style={{ width: '100%' }} placeholder="Select" />
}
bloodBody(rowData) {
let blood = [
{ label: 'A+', value: 'A+' },
{ label: 'A-', value: 'A-' },
{ label: 'B+', value: 'B+' },
{ label: 'B-', value: 'B-' },
{ label: 'O+', value: 'O+' },
{ label: 'O-', value: 'O-' }
];
return <Dropdown value={rowData.bloodGroup} options={blood} style={{ width: '100%' }} placeholder="Select" />
}
render() {
let rollEditor = (row) => {
return <InputText onChange={(evt) => { this.props.onEditorValueChange(row, evt.target.value); }} placeholder="Roll" value={row.rowData.studentRoll} />
}
let nameEditor = (row) => {
return <InputText onChange={(evt) => { this.props.onEditorValueChange(row, evt.target.value); }} placeholder="Student name" value={row.rowData.studentName} />
}
let fatherNameEditor = (row) => {
return <InputText onChange={(evt) => { this.props.onEditorValueChange(row, evt.target.value); }} placeholder="Father name" value={row.rowData.fatherName} />
}
let motherNameEditor = (row) => {
return <InputText onChange={(evt) => { this.props.onEditorValueChange(row, evt.target.value); }} placeholder="Mother name" value={row.rowData.motherName} />
}
let contactEditor = (row) => {
return <InputText onChange={(evt) => { this.props.onEditorValueChange(row, evt.target.value); }} placeholder="Contact Number" value={row.rowData.guardianMobile} />
}
let genderEditor = (row) => {
let gender = [
{ label: 'Male', value: 'Male' },
{ label: 'Female', value: 'Female' },
{ label: 'Others', value: 'Others' }
];
return <Dropdown value={row.rowData.gender} options={gender} onChange={(evt) => this.props.onEditorValueChange(row, evt.value)} style={{ width: '100%' }} placeholder="Select" />
}
let religionEditor = (row) => {
let religion = [
{ label: 'Islam', value: 'Islam' },
{ label: 'Hindu', value: 'Hindu' },
{ label: 'Buddhist', value: 'Buddhist' },
{ label: 'Christian', value: 'Christian' },
{ label: 'Others', value: 'Others' }
];
return <Dropdown value={row.rowData.religion} options={religion} onChange={(evt) => { this.props.onEditorValueChange(row, evt.value); }} style={{ width: '100%' }} placeholder="Select" />
}
let bloodEditor = (row) => {
let blood = [
{ label: 'A+', value: 'A+' },
{ label: 'A-', value: 'A-' },
{ label: 'B+', value: 'B+' },
{ label: 'B-', value: 'B-' },
{ label: 'O+', value: 'O+' },
{ label: 'O-', value: 'O-' }
];
return <Dropdown value={row.rowData.blood} options={blood} onChange={(evt) => { this.props.onEditorValueChange(row, evt.value); }} style={{ width: '100%' }} placeholder="Select" />
}
let msg = "";
if (this.props.setMessage) {
msg = { severity: 'success', detail: this.props.setMessage.message };
this.growl.show(msg);
}
else if (this.props.setErrMessage) {
msg = { severity: 'error', summary: 'Failed', detail: this.props.setErrMessage };
this.growl.show(msg);
}
if(this.props.loaderOn){
if(this.props.loaderOn === 'On') {
$('.loaderDiv').show();
} else if(this.props.loaderOn === 'Off'){
$('.loaderDiv').hide();
}
}
let content = '';
if (this.props.stdBasicInfo && this.props.stdBasicInfo.length) {
$('#UpdateBtnID').show();
let selectedStudentArr = [];
if (this.props.selectedStudent.length) {
Array.prototype.push.apply(selectedStudentArr, this.props.selectedStudent);
}
let columnData = [
<Column selectionMode="multiple" header="Mark" style={{ width: '3em' }} />,
<Column field="studentRoll" header="Roll No." editor={rollEditor} body={this.rollBody} style={{ width: '55px' }} />,
<Column field="studentName" header="Name" editor={nameEditor} body={this.nameBody} style={{ width: '170px' }} />,
<Column field="fatherName" header="Father Name" editor={fatherNameEditor} body={this.fatherNameBody} style={{ width: '145px' }} />,
<Column field="motherName" header="Mother Name" editor={motherNameEditor} body={this.motherNameBody} style={{ width: '145px' }} />,
<Column field="guardianMobile" header="Contact No." editor={contactEditor} style={{ width: '100px' }} body={this.contactBody} />,
<Column field="studentGender" header="Gender" editor={genderEditor} body={this.genderBody} style={{ width: '85px' }} />,
<Column field="studentReligion" header="Religion" editor={religionEditor} body={this.religionBody} style={{ width: '85px' }} />,
<Column field="bloodGroup" header="Blood Group" editor={bloodEditor} style={{ width: '80px' }} body={this.bloodBody} />
];
content = <CustomDataTable
info={this.props.stdBasicInfo}
onSelectionChange={this.props.onSelectionChange}
selectedData={selectedStudentArr}
columnData={columnData}
isSelectionOn={true}
editable={true}
header={'Student List'}
rows={10}
/>
}
//FOR SECTION LIST
let sectionListOptions = [];
if (this.props.sectionList && this.props.sectionList.length) {
sectionListOptions = this.props.sectionList.map((item) => ({
value: item.classConfigId,
label: item.classShiftSection,
}))
}
return (
<div>
<AppPrivateLayout>
<Panel header="Student Information Update">
<form method="post" onSubmit={this.props.onSubmitForm} >
<div className='ui-g form-group'>
<div className='ui-g-2 ui-lg-2 ui-md-2'></div>
<div className='ui-g-2 ui-lg-2 ui-md-2 ui-sm-12 netiLabel'>
<label> Section <span className="required"> * </span></label>
</div>
<div className='ui-g-3 ui-lg-3 ui-md-4 ui-sm-12 ui-fluid'>
<Dropdown value={this.props.sectionname} onChange={this.props.onChangeSectionList} options={sectionListOptions} placeholder="Select Section" autoWidth={false} />
</div>
<div className='ui-g-2 ui-lg-2 ui-md-3 ui-sm-12 ui-fluid'>
<Button icon="ui-icon-search" title="Search" label='Search'></Button>
</div>
<div className='ui-g-2 ui-lg-2 ui-fluid'></div>
</div>
</form>
{content}
<div className='ui-g'>
<Growl ref={(el) => this.growl = el} />
<div className='ui-g-4 ui-lg-4 ui-md-4 ui-sm-12 ui-fluid'>
</div>
<div className='ui-g-6 ui-lg-6 ui-md-5 ui-sm-12 ui-fluid'></div>
<div className='ui-g-2 ui-lg-2 ui-md-3 ui-sm-12 ui-fluid'>
<Button id="UpdateBtnID" style={{ display: 'none' }} onClick={this.props.onUpdate} icon='ui-icon-autorenew' label='Update'></Button>
</div>
</div>
</Panel>
<div class="loaderDiv" style={{display: 'none'}}>
<img className="sticky" src="https://loading.io/spinners/harmony/lg.harmony-taiji-spinner.gif" />
</div>
</AppPrivateLayout>
</div>
);
}
}
StudentUpdateBasicInformation.propTypes = {
stdBasicInfo: PropTypes.any,
onSubmitForm: PropTypes.func,
onEditorValueChange: PropTypes.func,
value: PropTypes.any,
onUpdate: PropTypes.func,
setMessage: PropTypes.any,
setErrMessage: PropTypes.any,
changeMessage: PropTypes.func,
sectionList: PropTypes.any,
onChangeSectionList: PropTypes.func,
sectionname: PropTypes.any,
loaderOn: PropTypes.any,
};
const mapStateToProps = createStructuredSelector({
stdBasicInfo: makeSelectStdBasicInfo(),
selectedStudent: makeSelectSelectedStudent(),
value: makeSelectEditorStudent(),
setMessage: makeSelectSetMessage(),
setErrMessage: makeSelectSetErrMessage(),
sectionList: makeSelectSectionList(),
sectionname: makeSelectSectionName(),
loaderOn: makeSelectLoaderOn(),
});
function mapDispatchToProps(dispatch) {
return {
changeMessage: (evt) => {
dispatch(getMessage());
dispatch(getErrMessage());
dispatch(getLoaderOn(evt));
},
onSubmitForm: (evt) => {
if (evt !== undefined && evt.preventDefault)
evt.preventDefault();
dispatch(submitForm());
},
onSelectionChange: (evt) => dispatch(selectStudent(evt.data)),
onEditorValueChange: (row, value) => {
dispatch(setEditorData(row, value));
},
onUpdate: (evt) => dispatch(submitUpdate()),
onChangeSectionList: (evt) => dispatch(changeSectionName(evt.value)),
};
}
const withConnect = connect(mapStateToProps, mapDispatchToProps);
const withReducer = injectReducer({ key: 'studentUpdateBasicInformation', reducer });
const withSaga = injectSaga({ key: 'studentUpdateBasicInformation', saga });
export default compose(
withReducer,
withSaga,
withConnect,
)(StudentUpdateBasicInformation);
constants.js
export const DEFAULT_ACTION = 'app/StudentUpdateBasicInformation/DEFAULT_ACTION';
export const SUBMIT_FORM = 'app/StudentUpdateBasicInformation/SUBMIT_FORM';
export const SET_STD_BASIC_INFO = 'app/StudentUpdateBasicInformation/SET_STD_BASIC_INFO';
export const SELECT_STUDENT = 'app/StudentUpdateBasicInformation/SELECT_STUDENT';
export const SET_EDITOR_DATA = 'app/StudentUpdateBasicInformation/SET_EDITOR_DATA';
export const GET_MESSAGE = 'app/StudentUpdateStudentId/GET_MESSAGE';
export const GET_ERR_MESSAGE = 'app/StudentUpdateStudentId/GET_ERR_MESSAGE';
export const SUBMIT_UPDATE = 'app/StudentUpdateBasicInformation/SUBMIT_UPDATE';
export const SET_SECTION_LIST = 'app/StudentUpdateBasicInformation/SET_SECTION_LIST';
export const CHANGE_SECTIONNAME = 'app/StudentUpdateBasicInformation/CHANGE_SECTIONNAME';
export const GET_LOADER_ON = 'app/StudentUpdateBasicInformation/GET_LOADER_ON';
actions.js
import {
DEFAULT_ACTION, SUBMIT_FORM, SET_STD_BASIC_INFO, SELECT_STUDENT, SET_EDITOR_DATA, SUBMIT_UPDATE, GET_MESSAGE, GET_ERR_MESSAGE, CHANGE_SECTIONNAME, SET_SECTION_LIST, GET_LOADER_OFF, GET_LOADER_ON
} from './constants';
export function defaultAction() {
return {
type: DEFAULT_ACTION,
};
}
export function setStdBasicInfo(item) {
return {
type: SET_STD_BASIC_INFO,
item,
}
}
export function selectStudent(data) {
return {
type: SELECT_STUDENT,
data,
};
}
export function setEditorData(row, value) {
return {
type: SET_EDITOR_DATA,
row,
value,
};
}
export function submitForm() {
return {
type: SUBMIT_FORM,
};
}
export function submitUpdate() {
return {
type: SUBMIT_UPDATE,
};
}
export function getMessage(message) {
return {
type: GET_MESSAGE,
message,
}
}
export function getErrMessage(errmessage) {
return {
type: GET_ERR_MESSAGE,
errmessage,
}
}
export function setSectionList(sectionList) {
return {
type: SET_SECTION_LIST,
sectionList,
};
}
export function changeSectionName(sectionname) {
return {
type: CHANGE_SECTIONNAME,
sectionname,
};
}
export function getLoaderOn(loaderOn){
return{
type: GET_LOADER_ON,
loaderOn,
}
}
reducer.js
import { fromJS } from 'immutable';
import {
DEFAULT_ACTION, SET_STD_BASIC_INFO, SELECT_STUDENT, SET_EDITOR_DATA, GET_MESSAGE, GET_ERR_MESSAGE, SET_SECTION_LIST, CHANGE_SECTIONNAME, GET_LOADER_ON, GET_LOADER_OFF
} from './constants';
const initialState = fromJS({
selectedStudent: [],
value: [],
sectionList: {},
sectionname: '',
});
function studentUpdateBasicInformationReducer(state = initialState, action) {
switch (action.type) {
case DEFAULT_ACTION:
return state;
case SET_STD_BASIC_INFO:
return state.set('stdBasicInfo', action.item);
case SELECT_STUDENT:
return state.set('selectedStudent', action.data);
case SET_EDITOR_DATA:
let updatedInfost = [...action.row.value];
updatedInfost = [...action.row.value];
updatedInfost[action.row.rowIndex][action.row.field] = action.value;
return state.set('stdBasicInfo', updatedInfost);
case GET_MESSAGE:
return state.set('setMessage', action.message);
case GET_ERR_MESSAGE:
return state.set('setErrMessage', action.errmessage);
case SET_SECTION_LIST:
return state.set('sectionList', action.sectionList);
case CHANGE_SECTIONNAME:
return state.set('sectionname', action.sectionname);
case GET_LOADER_ON:
return state.set('loaderOn', action.loaderOn);
// case GET_LOADER_OFF:
// return state.set('loaderOff', action.loaderOff);
default:
return state;
}
}
export default studentUpdateBasicInformationReducer;
selectors.js
import { createSelector } from 'reselect';
const selectStudentUpdateBasicInformationDomain = (state) => state.get('studentUpdateBasicInformation');
const makeSelectStudentUpdateBasicInformation = () => createSelector(
selectStudentUpdateBasicInformationDomain,
(substate) => substate.toJS()
);
const makeSelectStdBasicInfo = () => createSelector(selectStudentUpdateBasicInformationDomain, (abc) => abc.get('stdBasicInfo'));
const makeSelectSelectedStudent = () => createSelector(selectStudentUpdateBasicInformationDomain, (substate) => substate.get('selectedStudent'));
const makeSelectEditorStudent = () => createSelector(selectStudentUpdateBasicInformationDomain, (substate) => substate.get('value'));
const makeSelectSetMessage = () => createSelector(selectStudentUpdateBasicInformationDomain, (abc) => abc.get('setMessage'));
const makeSelectSetErrMessage = () => createSelector(selectStudentUpdateBasicInformationDomain, (abc) => abc.get('setErrMessage'));
const makeSelectSectionName = () => createSelector(selectStudentUpdateBasicInformationDomain, (abc) => abc.get('sectionname'));
const makeSelectSectionList = () => createSelector(selectStudentUpdateBasicInformationDomain,(substate) => substate.get('sectionList'));
const makeSelectLoaderOn = () => createSelector(selectStudentUpdateBasicInformationDomain, (substate) => substate.get('loaderOn'));
export {
selectStudentUpdateBasicInformationDomain,
makeSelectStdBasicInfo,
makeSelectSelectedStudent,
makeSelectEditorStudent,
makeSelectSetMessage,
makeSelectSetErrMessage, makeSelectSectionName, makeSelectSectionList, makeSelectLoaderOn,
//makeSelectLoaderOff
};
saga.js
import { take, call, put, select, takeLatest } from 'redux-saga/effects';
import { BASE_URL, FETCH_BASIC_INFO_LIST, UPDATE_ID, GET_CLASS_CONFIGURATION_URL, STUDENT_CONFIG_LIST } from '../../utils/serviceUrl';
import { setStdBasicInfo, getMessage, getErrMessage, selectStudent, setSectionList, changeSectionName, getLoaderOn } from './actions';
import request from '../../utils/request';
import { SUBMIT_FORM, SUBMIT_UPDATE } from './constants';
import { makeSelectEditorStudent, makeSelectSelectedStudent, makeSelectSectionList, makeSelectSectionName } from './selectors';
import { getTokenData } from '../../utils/authHelper';
//FOR SECTION LIST
export function* fetchSectionList() {
const tokenData = JSON.parse(getTokenData());
const requestUrl = BASE_URL.concat(GET_CLASS_CONFIGURATION_URL);
const options = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': tokenData.token_type+" "+tokenData.access_token,
},
};
try {
const response = yield call(request, requestUrl, options);
if (response.item) {
yield put(setSectionList(response.item));
}
} catch (err) {
console.dir(err);
}
}
//FOR STUDENT BASIC INFO LIST
export function* fetchStudentBasicInfoList() {
const tokenData = JSON.parse(getTokenData());
const classConfigId = yield select(makeSelectSectionName());
let msgg;
if (classConfigId == '') {
let msg = "An error has occured. Please fill up all required fields";
yield put(getErrMessage(msg));
}
else {
msgg = 'On';
yield put(getLoaderOn(msgg));
const requestURL = BASE_URL.concat(STUDENT_CONFIG_LIST).concat('?classConfigId=').concat(classConfigId);
const options = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': tokenData.token_type+" "+tokenData.access_token,
},
};
const info = yield call(request, requestURL,options);
yield put(setStdBasicInfo(info.item));
msgg = 'Off';
yield put(getLoaderOn(msgg));
}
}
//FOR UPDATE STUDENT INFORMATION
export function* updateStdBasicInfo() {
const tokenData = JSON.parse(getTokenData());
const selectedCheckData = yield select(makeSelectSelectedStudent());
let selectedData = [];
if (selectedCheckData.length === undefined || selectedCheckData.length === 0) {
const errresult = "An error has occured. Please fill up all required fields";
yield put(getErrMessage(errresult));
} else {
for (const i in selectedCheckData) {
const DataList = selectedCheckData[i];
selectedData.push(DataList);
}
const requestURL = BASE_URL.concat(UPDATE_ID);
const options = {
method: 'PUT',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': tokenData.token_type+" "+tokenData.access_token,
},
body: JSON.stringify(selectedData),
}
try {
const result = yield call(request, requestURL, options);
yield put(selectStudent([]));
yield fetchStudentBasicInfoList();
yield put(getMessage(result));
} catch (err) {
const errresult = "Something went wrong. Please try again.";
yield put(setStdBasicInfo(info.item));
yield put(getErrMessage(errresult));
}
}
}
export default function* defaultSaga() {
yield fetchSectionList();
yield takeLatest(SUBMIT_FORM, fetchStudentBasicInfoList);
yield takeLatest(SUBMIT_UPDATE, updateStdBasicInfo);
}
Dušan's answer worked for me but for 2 exceptions:
In onEditorKeyDown instead of
var table = this.dt.container.childNodes[1].childNodes[0].childNodes[1];
use
let table = this.dt.container.childNodes[0].childNodes[0].childNodes[1];
In addition, (event.which === 9) should be used instead of (event.keyCode == 9) and all the functions must be bound in the constructor:
this.inputTextEditor = this.inputTextEditor.bind(this)
this.xxxEditor = this.xxxEditor.bind(this)
this.onEditorKeyDown = this.onEditorKeyDown.bind(this)
I have an idea how you can do this:
while you are in editable cell, you need to intercept TAB key and then to simulate click on next cell to the right or, if current cell is the last one, on first cell in next row.
Steps in following example are based on PrimeReact's show case editable page. You can adopt them for your particular case.
FUNCTIONAL EXAMPLE
Step 1:
define onRowClickhandler and add it to DataTable component to be able to catch currently clicked rowIndex (we need to know what is the row index of the cell currently edited)
onRowClick(event) {
this.rowIndex = event.index;
}
...
<DataTable ref={(el) => this.dt = el} editable={true} onRowClick={(e) => this.onRowClick(e)} ...>
Step 2:
Define column index (ordinal number) to each of column editors: 1st column has index 0, 2nd column index 1, etc. For example
vinEditor(props) {
return this.inputTextEditor(props, 'vin', 0);
}
yearEditor(props) {
return this.inputTextEditor(props, 'year', 1);
}
brandEditor(props) {
return this.inputTextEditor(props, 'brand', 2);
}
where inputTextEditor now look like this
inputTextEditor(props, field, columnIndex) {
return <InputText type="text" value={props.rowData.year}
onKeyDown={(e) => this.onEditorKeyDown(e, columnIndex)} onChange={(e) => this.onEditorValueChange(props, e.target.value)} />;
}
Note that I've added onKeyDown handler and passed columnIndex arg to it so that we can recognize the column where some key is typed.
Step 3:
Finally we can define onEditorKeyDown to do the magic (check out code comments for additional explanations)
onEditorKeyDown(event, columnIndex) {
console.log("onKeyDown", event);
console.log("key code", event.keyCode);
console.log("columnIndex", columnIndex);
console.log("rowIndex", this.rowIndex);
//change following 2 constants to to fit your case
const columnCount = 4;
const rowCount = 10;
//check if TAB (key code is 9) is pressed on InputText
if (event.keyCode == 9) {
//prevent default behaviour on TAB press
event.preventDefault();
//this.dt is reference to DataTable element
//get reference to `table` node only
var table = this.dt.container.childNodes[1].childNodes[0].childNodes[1];
//check if we are not in last column
if (columnIndex < columnCount - 1) {
//simulate click on next column
table.childNodes[this.rowIndex].childNodes[columnIndex + 1].click();
} else {
//we are in the last column, check if we are not in last row
if (this.rowIndex < rowCount - 1) {
//we are not in the last row
// select next row
this.rowIndex += 1;
//simulate click on first column in next row
table.childNodes[this.rowIndex].childNodes[0].click();
}
}
}
}

Ionic 2 Validating username at signup

I'm new to Ionic/Angular 2..
Try to use custom validators to validate the username but at server but i´m stack into this problem...
Property 'getUserByLogin' does not exist on type 'typeof
UserValidator'.
I have an sigunp.html page ...
<ion-header>
<ion-navbar>
<button ion-button menuToggle>
<ion-icon name="menu"></ion-icon>
</button>
<ion-title>{{ "SIGNUP" | translate }}</ion-title>
</ion-navbar>
</ion-header>
<ion-content class="login-page">
<div class="logo">
<img src="assets/img/appicon.svg" alt="Ionic Logo">
</div>
<ion-card>
<p ion-text>Teste</p>
<ion-card-content *ngFor="let user of users">
<h1>{{user.nome}}</h1>
<p>{{user.password}}</p>
</ion-card-content>
</ion-card>
<form [formGroup]="signupForm" (ngSubmit)="postSignupForm()" >
<ion-list no-lines>
<ion-item>
<ion-label floating>{{ "USER_NAME" | translate }}</ion-label>
<ion-input type="text" formControlName="login" clearInput></ion-input>
</ion-item>
<ion-item text-wrap *ngIf="!signupForm.controls.login.valid && (signupForm.controls.login.dirty || submitAttempt)">
<p ion-text color="danger">{{ "INVALID_USER_NAME" | translate }}</p>
</ion-item>
<ion-item *ngIf="signupForm.controls.login.pending">
<p ion-text color="danger">{{ "VALIDATION_IN_PROGRESS" | translate }}</p>
</ion-item>
<ion-item *ngIf="!signupForm.controls.login.valid && !signupForm.controls.login.pending && (signupForm.controls.login.dirty || submitAttempt)">
<p ion-text color="danger">{{ "USER_NAME_IN_USE" | translate }}</p>
</ion-item>
<ion-item>
<ion-label floating>{{ "PASSWORD" | translate }}</ion-label>
<ion-input type="password" formControlName="password" clearInput></ion-input>
</ion-item>
<ion-item text-wrap *ngIf="!signupForm.controls.password.valid && (signupForm.controls.password.dirty || submitAttempt)">
<p ion-text color="danger">{{ "INVALID_PASSWORD" | translate }}</p>
</ion-item>
<ion-item>
<ion-buttons end>
<ion-row responsive-sm>
<ion-col>
<button [disabled]="signupForm.invalid" type="submit" ion-button icon-left block>
<ion-icon name="add"></ion-icon>
<label>{{ "CREATE" | translate }}</label>
</button>
</ion-col>
<ion-col>
<button (click)="onFacebook(signupForm)" ion-button icon-left block>
<ion-icon name="logo-facebook"></ion-icon>
<label>{{ "FACEBOOK" | translate }}</label>
</button>
</ion-col>
</ion-row>
</ion-buttons>
</ion-item>
</ion-list>
</form>
</ion-content>
And a signup.ts with
import { Component } from '#angular/core';
import {Validators, FormBuilder } from '#angular/forms';
import { NgForm } from '#angular/forms';
import { NavController } from 'ionic-angular';
import { TabsPage } from '../tabs/tabs';
import { UserData } from '../../providers/user-data';
import { UserProvider } from '../../providers/user-provider';
import { UserValidator } from '../../validators/user-validator';
#Component({
selector: 'page-user',
templateUrl: 'signup.html'
})
export class SignupPage {
signupForm : any = {};
users :any [];
id : number = 0;
signup: {username?: string, password?: string} = {};
submitted = false;
constructor(public navCtrl: NavController, public formBuilder : FormBuilder, public userData: UserData, public userService : UserProvider ) {
this.signupForm = this.formBuilder.group({
login : ['', Validators.compose( [ Validators.minLength(6)
, Validators.required
, Validators.pattern('[a-zA-Z]*') ] )
, UserValidator.checkUsername
],
password : ['', Validators.compose( [ Validators.minLength(6)
, Validators.required
] )
]
});
}
getAllUsers() {
return this.userService.getAllUsers().subscribe(
data=>this.users=data,
err=>console.log(err)
)
}
getUser( id : number ) {
return this.userService.getUser( id ).subscribe(
data=>this.users=data,
err=>console.log(err)
)
}
getUserByLogin( login : string ) {
return this.userService.getUserByLogin( login ).subscribe(
data=>this.users=data,
err=>console.log(err)
)
}
postSignupForm (){
console.log( this.signupForm.value );
}
onSignup(form: NgForm) {
this.submitted = true;
if (form.valid) {
this.userData.signup(this.signup.username);
this.navCtrl.push(TabsPage);
}
}
onFacebook(form: NgForm) {
this.getUser(2);
console.log( form.value );
}
}
Where i call an UserValidator.checkUsername that is created at...
import { FormControl } from '#angular/forms';
import { UserProvider } from '../providers/user-provider';
export class UserValidator {
wsreturn : any = [];
constructor( public userService : UserProvider ) {
}
getUserByLogin( login : string ) {
return this.userService.getUserByLogin( login ).subscribe(
data=>this.wsreturn=data,
err=>console.log(err)
)
};
static checkUsername(control: FormControl): any {
return new Promise(resolve => {
//console.log ( '------->' ); console.log ( control.value );
this.getUserByLogin( control.value ); /// I NEED HELP!
// Fake a slow response from server
setTimeout(() => {
if(control.value.toLowerCase() === "luciano"){
resolve({
"username taken": true
});
} else {
resolve(null);
}
}, 2000);
});
}
}
My user-privider is this one...
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/map';
import { Observable } from 'rxjs/Observable';
import { GlobalProvider } from '../providers/global-provider';
#Injectable()
export class UserProvider {
constructor(public http: Http, public global: GlobalProvider) {
}
getAllUsers() {
return this.http.get(this.global.serverAdress + '/usuario', { headers: this.global.headers } ).map(res=>res.json())
}
getUser( id : number ) {
return this.http.get(this.global.serverAdress + '/usuario/'+ id, { headers: this.global.headers } ).map(res=>res.json())
}
getUserByLogin( login : string ) {
return this.http.get(this.global.serverAdress + '/usuario/porlogin/'+ login, { headers: this.global.headers } ).map(res=>res.json())
}
postUser( id : number ) {
return this.http.get(this.global.serverAdress + '/usuario/'+ id, { headers: this.global.headers } ).map(res=>res.json())
}
}
you can not call instance method in static method with this keyword. create directive instead
import { Directive, forwardRef } from '#angular/core';
import { NG_VALIDATORS, FormControl, Validator } from '#angular/forms';
#Directive({
selector: '[checkUsername][ngModel],[checkUsername][formControl]',
providers: [
{ provide: NG_VALIDATORS, useExisting: forwardRef(() => CheckUsernameValidator), multi: true }
]
})
export class CheckUsernameValidator implements Validator {
constructor(public userService: UserProvider) {
}
getUserByLogin(login : string) {
return this.userService.getUserByLogin(login);
};
validate(c: FormControl) {
return new Promise(resolve => { //
this.getUserByLogin( control.value ).subscribe(
data => {
if(control.value.toLowerCase() === "luciano"){
resolve({
"username taken": true
});
} else {
resolve(null);
}
},
err => {
console.log(err);
resolve(null);
}
);
}
}
in your html:
<ion-input type="text" formControlName="login" checkUsername clearInput></ion-input>
I move checkUsername to signup.ts with and made some changes to "bind(this)"... its working as i want but i´m still reading more about "creating directives"...
thanks #tiep-phan and #suraj.
....
constructor(public navCtrl: NavController, public formBuilder : FormBuilder, public userData: UserData, public userService : UserProvider ) {
this.signupForm = this.formBuilder.group({
login : ['', Validators.compose( [ Validators.minLength(6)
, Validators.required
, Validators.pattern('[a-zA-Z]*') ] )
, this.checkUsername.bind(this)
],
password : ['', Validators.compose( [ Validators.minLength(6)
, Validators.required
] )
]
});
}
checkUsername(control: FormControl): any {
return new Promise( resolve => {
this.userService.getUserByLogin( control.value ).subscribe(
data=>{
// console.log('Sucesso:'); console.log(data[0]);
if ( typeof data[0] !== "undefined") {
if ( typeof data[0].login === "undefined") {
resolve(null);
} else {
// username encontrado no banco de dados
resolve( { "INVALID_USER_NAME": true } );
}
} else {
resolve(null);
}
},
err=>{ console.log('Erro:'); console.log(err); }
)
});
}
....

Resources