laravel vue getting info by hidden field - laravel

I need to pass logged user id to back-end and I have vuex store so I can get my user info like {{currentUser.id}} the problem is i cannot pass it to back-end it gives me validation error that user_id is required while i have this hidden input in my form
<input type="hidden" name="user_id" :value="currentUser.id">
for normal inputs i have v-model like v-model="project.title" which is not possible to use on hidden fields.
The question here is how can I pass my user_id to back-end?
Code
<script>
import validate from 'validate.js';
export default {
data: function () {
return {
project: {
title: '',
body: '',
attachment: '',
projectclass: '',
deadline: '',
user_id: '',
csrf: document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
},
errors: null
}
},
computed: {
currentUser() {
return this.$store.getters.currentUser;
}
},
methods: {
add() {
this.errors = null;
const errors = validate(this.$data.project);
if(errors) {
this.errors = errors;
return;
}
axios.post('/api/projects/new', this.$data.project)
.then((response) => {
this.$router.push('/projects');
});
}
}
}
</script>

This happens because user_id in this.$data.project dosn't get updated.
Instead of having hidden input you can just do
add() {
this.errors = null;
const errors = validate(Object.assign(this.$data.project, {user_id: this.currentUser.id}));
if(errors) {
this.errors = errors;
return;
}
axios.post('/api/projects/new', Object.assign(this.$data.project, {user_id: this.currentUser.id}))
.then((response) => {
this.$router.push('/projects');
});
}

Related

Why does RTK query response handling not work?

I tried to use the RTK query on my login request, but I got some trouble when printing out the result. Here is my code.
authRTK.ts
import { createApi, fetchBaseQuery } from "#reduxjs/toolkit/query/react";
import { loginForm, UserResponse } from "../type/type";
import { RootState } from "./store";
export const api = createApi({
baseQuery: fetchBaseQuery({
baseUrl: 'http://localhost:3001',
prepareHeaders: (headers, { getState }) => {
// By default, if we have a token in the store, let's use that for authenticated requests
const token = (getState() as RootState).auth.token;
if (token) {
headers.set("authentication", `Bearer ${token}`);
}
return headers;
}
}),
endpoints: (build) => ({
login: build.mutation<UserResponse, loginForm>({
query: (credentials) => ({
url: "login",
method: "POST",
body: credentials
}),
transformResponse: (response: { data: UserResponse }) => {
return response.data
},
}),
protected: build.mutation({
query: () => "protected"
})
})
});
export const { useLoginMutation,useProtectedMutation } = api;
store.ts
import { configureStore } from '#reduxjs/toolkit'
import cartReducer from './cartRedux';
import userReducer from './authRedux';
import { api } from './authRTK';
export const store = configureStore({
reducer:{
cart: cartReducer,
auth: userReducer,
[api.reducerPath]: api.reducer,
},
middleware: (gDM) => gDM().concat(api.middleware),//getDefaultMiddleware
})
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
Login.tsx
const Login = () => {
const [login, { isLoading,error,isError}] = useLoginMutation();
const [showPassword,setShowPassword] = useState<boolean>(false);
return (
<Container>
<Wrapper>
{/* <button onClick={()=>testCookie()}>測試一下cookie</button> */}
<Title>SIGN IN</Title>
<Formik
initialValues={{ email: "", password: "" }}
validationSchema={Yup.object({
password: Yup.string()
.min(8, 'Must be 8 characters or higher')
.required(),
email: Yup.string().email('Invalid email address').required(),
})}
onSubmit = { async (values, actions) => {
try{
const result = await login(values);
if("data" in result){
console.log(result.data)
}else{
console.log((result.error as RequestError).data) ////this will printout the expected result , but I have to cast error to RequestError type to print the nested data inside , and I can't use this data else where like error above
console.log(error) //This printout undefined,mean there's no error data inside,but not supposed to happen
console.log(isError) //print out false , but supposed to be true
}
}catch(err){
console.log(err)
}
}}>
{({
errors,
values,
handleChange,
handleBlur,
handleSubmit,
validateField
}) => (
<Form onSubmit={handleSubmit}>
<InputContainer>
<Input
onChange={handleChange}
onBlur={handleBlur}
value={values.email}
type="text"
name="email"
placeholder="Email"
data-testid="email"
/>
</InputContainer>
{errors.email && <Error data-testid="emailError">{errors.email}</Error>}
<InputContainer>
<Input
onChange={handleChange}
onBlur={handleBlur}
value={values.password}
type={showPassword ? "text" : "password"}
name="password"
placeholder="Password"
data-testid="password"
/>
{showPassword ? <VisibilityOff onClick={()=>setShowPassword(false) }/> : <Visibility onClick={()=>setShowPassword(true) }/> }
</InputContainer>
{errors.password && <Error data-testid="passwordError">{errors.password}</Error>}
<Button
data-testid="submit"
type="submit">Submit</Button>
</Form>
)}
</Formik>
</Wrapper>
</Container>
);
};
export default Login;
So My main problems are with the login.tsx,Error didn't work as expected, and my response data have to determine if "data" is in it, even though I used transformResponse.
BTW my response type looks like below
RequestError:
{
data:string;
status:string
}
data is not the data from your response. It is the data property of the trigger function result.
trigger always returns an object in the form { data: ... } or { error: ... }.
So without your transformResult you would end up with result.data.data instead of result.data.
You can also unwrap that, to directly get the data and throw an error in the error case, but that's not the default as it might lead to uncaught promise rejection errors if you don't handle it.
async (values, actions) => {
try{
const result = await login(values).unwrap();
console.log(result.data)
} catch(err){
console.log(err)
}
}

How to delete data using Vue js?

I'm trying delete data but I'm getting this error:
this.jobPosts.filter is not a function
PostJobIndex.vue file:
deleteJobPost: async function(jobPost) {
if (!window.confirm('Are you sure you want to delete this Job Post?')) {
return;
}
try {
await employerService.deleteJobPost(jobPost.id);
this.jobPosts = this.jobPosts.filter(obj => {
return obj.id != jobPost.id;
});
console.log(this.jobPosts);
this.$toast.success("Job Post deleted Successfully!");
} catch (error) {
console.log(error);
this.$toast.error(error.response.data.message);
}
},
I had this same issue with my Update method and I beleive it was because I was trying to map through an object or something instead of an array. In the end I used Object.keys(this.jobPosts).map for my update method and it worked:
Object.keys(this.jobPosts).map(jobPost => {
if (jobPost.id == response.data.id) {
for (let key in response.data) {
jobPost[key] = response.data[key];
}
}
});
But when I do this for Update it doesn't work:
this.jobPosts = Object.keys(this.jobPosts).filter(obj => {
return obj.id != jobPost.id;
});
UPDATED
Here is the code for loading the job posts:
loadJobPosts: async function() {
try {
const response = await employerService.loadJobPosts();
this.jobPosts = response.data;
console.log(this.jobPosts);
} catch (error) {
this.$toast.error('Some error occurred, please refresh!');
}
},
Im using Vuex for state management and I'm using services, that simply contain the axios http requests. That's where this line comes from employerService.loadJobPosts() loadJobPosts() is a function inside my employerService.js file.
I'm also using Laravel for my back end. Here is my JobPostsController.php file:
public function index()
{
$jobPosts = JobPost::all()->where('user_id', Auth::user()->id);
return response()->json($jobPosts, 200);
}
From what I've understood from your code,
this should work for removing jobPost from jobPosts
this.jobPosts = this.jobPosts.filter(obj => {
return obj.id != jobPost.id;
});
I don't know what you're expecting this to do, but it won't do anything useful and will either error or return false for everything.
this.jobPosts = Object.keys(this.jobPosts).filter(obj => {
return obj.id != jobPost.id;
});
filter exists on array types, so I would check where it's getting set and make sure it's an array.
I've included a small snippet in case it's any help.
Vue.config.devtools = false;
Vue.config.productionTip = false;
new Vue({
el: "#app",
data: () => {
return {
jobPosts: [],
deleteJobId: 1
};
},
methods: {
getJobPosts() {
this.jobPosts = [{
id: 1
}, {
id: 2
}, {
id: 3
}, {
id: 4
}, {
id: 5
}];
},
deleteJob() {
if (!this.deleteJobId)
return;
this.jobPosts = this.jobPosts.filter(x => x.id !== this.deleteJobId);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button type="button" #click="getJobPosts">Get Jobs</button>
<div>
<button type="button" #click="deleteJob">Delete Job #</button>
<input type="number" v-model.number="deleteJobId" />
</div>
<ul>
<li v-for="jobPost in jobPosts">
Job Post #{{jobPost.id}}
</li>
</ul>
</div>
You have already answered your own question:
in my data() object, I have this jobPosts: [], but in the console it says Object
As for your second question:
I don't know how to return the data as an array
There are similiar topics here on SO.
I am not familiar with Laravel but assuming you have an eloquent model with JobPost in your index-function according to the docs you should use the .toArray-method:
$jobPosts = JobPost::all()->where('user_id', Auth::user()->id).toArray();
When working with plain collections the values method should do the trick of returning an array instead of an object:
$collection = collect([
10 => ['product' => 'Desk', 'price' => 200],
11 => ['product' => 'Desk', 'price' => 200]
]);
$values = $collection->values();
$values->all();
UPDATE
I just realized that your result is just a stringified JSON object that needs to be converted into an array. Just parse it before processing (take out the JSON.parse(...) if you are already taking care of it in your service), return the object properties as an array and you are good to go:)
this.jobPosts = Object.values(JSON.parse(this.jobPosts)).filter(obj => {
return obj.id != jobPost.id;
});

Correct way to upload and save image in Laravel with vue.js and Element-ui

I'm making a recipe book and I want the recipes to have the choice to upload images, I'm using Element-UI in this project and they have an upload component. However, I'm not super sure how to use it correctly. I'm basing this on some code I found but it's not really working the $request I receive in the controller always has the image: null. I'm using $intertia.post but I could change to $http.post if needed.
This is what I'm trying
<el-upload
class="avatar-uploader"
action="/api/vendors/fake-upload"
accept="image/*"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload">
<img v-if="form.image" :src="form.image" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
<div class="buttonImage">
<el-button v-if="form.image" class="img-button mt-1" type="warning">
Change Picture
</el-button>
</div>
</el-upload>
Relevant data() in my vue <script>
loadingImage: false,
imageFile: null,
form: {
name: '',
description: '',
image: ''
},
These are the methods that go with the <el-upload>
handleAvatarSuccess(res, file) {
this.form.image = URL.createObjectURL(file.raw);
this.loadingImage = false;
},
beforeAvatarUpload(file) {
this.imageFile = file;
const isJPG = file.type === 'image/jpeg';
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isJPG) {
this.$message.error('This picture must be a JPG!');
}
if (!isLt2M) {
this.$message.error('This image is bigger than 2MB!');
}
this.loadingImage = true;
return isLt2M && isJPG;
},
This is how I'm sending it to the controller
submit() {
this.$refs.form.validate((valid) => {
if (valid) {
this.loading = true;
if (!this.form.id) {
this.$inertia.post(this.baseUrl, {
name: this.form.name,
description: this.form.description,
category: this.category,
steps: this.steps,
ingredient: this.ingredient,
measurements: this.measurements,
image: this.imageFile
}).then(
() => {
this.recipe = this.$page.recipe;
this.$message({
type: 'success',
message: 'Created correctly.'
});
this.loading = false
},
(res) => {
this.$message.error(parseError(res)[0]);
this.loading = false;
})
}
} else {
return false;
}
this.reset();
});
},
What's the correct way to do this, or is there an easier way?

issue with slowly geting data from api to vue view

I have issue with very slowly getting data from laravel api to vue view, I did tutorial where I have:
import axios from 'axios';
const client = axios.create({
baseURL: '/api',
});
export default {
all(params) {
return client.get('users', params);
},
find(id) {
return client.get(`users/${id}`);
},
update(id, data) {
return client.put(`users/${id}`, data);
},
delete(id) {
return client.delete(`users/${id}`);
},
};
<script>
import api from "../api/users";
export default {
data() {
return {
message: null,
loaded: false,
saving: false,
user: {
id: null,
name: "",
email: ""
}
};
},
methods: {
onDelete() {
this.saving = true;
api.delete(this.user.id).then(response => {
this.message = "User Deleted";
setTimeout(() => this.$router.push({ name: "users.index" }), 1000);
});
},
onSubmit(event) {
this.saving = true;
api
.update(this.user.id, {
name: this.user.name,
email: this.user.email
})
.then(response => {
this.message = "User updated";
setTimeout(() => (this.message = null), 10000);
this.user = response.data.data;
})
.catch(error => {
console.log(error);
})
.then(_ => (this.saving = false));
}
},
created() {
api.find(this.$route.params.id).then(response => {
this.loaded = true;
this.user = response.data.data;
});
}
};
</script>
It's load data from api very slowly I see firstly empty inputs in view and after some short time I see data, if I open api data from laravel I see data immediately, so my question is How speed up it? Or maby I did something wrong?
Whenever I am using an API with Vue, I usually make most of my API calls before opening the Vue then passing it in like this.
<vue-component :user="'{!! $user_data !!}'"></vue-component>
But if you have to do it in the Vue component, I am not sure if this will show improvement over your method but I would set it up with the "mounted" like so.
export default {
mounted() {
api.find(this.$route.params.id).then(response => {
this.loaded = true;
this.user = response.data.data;
});
}
}
Also heres a good tutorial on Axios and how to use HTTP Requets with Vue.
Hopefully this answered your question, good luck!

I need changed values on handclick from the Edit Form using custom action. How can I get it?

In the code below I want to get the form values from Edit form and sent using fetch on handleClick.
class GenerateButton extends Component {
handleClick = () => {
const { push, record, showNotification, values } = this.props;
const updatedRecord = { ...record, is_approved: true };
fetch(`api/reports/${record.id}`, { method: 'GET', body: updatedRecord })
.then((response) => {
return response.blob();
}).then(function(blob) {
console.log(blob);
})
.catch((e) => {
showNotification('Error: report generation failed.', 'warning')
});
}
render() {
return <RaisedButton label="Generate" onClick={this.handleClick} />;
}
}

Resources