In Chapter Seven, we had created a form to submit portfolio data and used it to submit the portfolio data. At the end of the chapter, we saw that we still need to do the following:
The validations are related to the form we are using to add data. Let’s tackle the validations.
We can copy the validationErrors() computed method from ProfileEditor.vue and remove the ‘please fill at least one field’ rule.
NAME CHANGE: The name of the method has been changed from
_validationErrors()_
to_validation()_
.
The front-end validation is going to be similar to ProfileEditor’s front-end validation. The difference is that instead of requiring at least one field, we now require image and project name. The description is optional.
Validation Rules: We need a file and we need a name but the description is optional; so there will be a validation rule for file and a validation rule for name but none for description.
computed: {
validation(){
this.disabled = true;
i̶f̶ ̶(̶t̶h̶i̶s̶.̶n̶a̶m̶e̶ ̶=̶=̶=̶ ̶'̶'̶ ̶&̶&̶ ̶t̶h̶i̶s̶.̶f̶i̶l̶e̶.̶l̶e̶n̶g̶t̶h̶ ̶<̶=̶ ̶0̶)̶ ̶{̶
̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶r̶e̶t̶u̶r̶n̶ ̶'̶p̶l̶e̶a̶s̶e̶ ̶f̶i̶l̶l̶ ̶a̶t̶ ̶l̶e̶a̶s̶t̶ ̶o̶n̶e̶ ̶f̶i̶e̶l̶d̶'̶;̶
̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶}̶
if (this.name === '') {
return 'please fill in the project name';
}
if (!this.file) {
return 'please select an image';
}
if (this.file && !this.file.type.startsWith("image/")) {
return 'file must be an image';
}
else {
this.disabled = false;
return ':)'
}
}
}
For the submit button, have a disabled attribute and bind the disabled attribute to **disabled**
<button @click.prevent="onSubmit" :disabled="disabled">Submit</button>
Insert **disabled**
in data()
data() {
return {
file: '',
name: '',
description: '',
disabled: true,
}
},
Put an errorBar below the submit button in the form
<div class="errorBar">{{validation}}</div>
We can tick front-end validation
Let’s do the laravel validation slightly differently. Last time, we saw that it can be done in the store function. This time, we would like to refactor the validation into a separate request.
php artisan make:request StorePortfolioItemRequest
This will create a file at:
App\Http\Requests\StorePortfolioItemRequest.php
This request file will handle your validation.
If the authorize() function returns false, change it to true. This function allows you to use the validation that this file provides.
We can later change this function to only authorize if we are logged in.
public function authorize()
{
return true;
}
Apply the **required**
rule to both itempic and name. The name should be **unique**
in the portfolio_items table.
public function rules()
{
return [
'itempic' => 'required',
'name' => 'required|unique:portfolio_items',
];
}
and now change the Request $request
in store()’s brackets to **StorePortfolioItemRequest** $request
public function store(R̶e̶q̶u̶e̶s̶t̶ StorePortfolioItemRequest $request) {
$file = $request->itempic;
$hashName = $file->hashName();
$this->resizeAndStore($file, $hashName);
$this->storeDetails($hashName);
}
Back-end validation gets a tick
We can tick front-end validation
The Read, Update and Delete functionality is going to be implemented in a table.
Let’s create a table that allows us to perform CRUD operations.
Read — We need to render the read into table
Update — We need to use a form similar to the create form to update the information
Delete — We need a delete button and a confirm delete popup.
We need a table with project names, the option to remove the project from the portfolio, and the option to change the project’s details etc.
Let’s have that in bullet points:
Our table is going to look like this:
Let’s have a table element
<table>
</table>
Within the table element, we need a **thead**
and a **tbody**
element. **thead**
is the element that will contain the headings i.e. Project Name, Edit and Delete in the image above. **tbody**
will contain the other rows.
<table>
<thead></thead>
<tbody></tbody>
</table>
table-headings within thead
element using the **th**
element:
<table>
<thead>
<tr>
<th>Project Name</th>
<th>Edit</th>
<th>Delete</th>
</tr>
</thead>
<tbody></tbody>
</table>
Within tbody
<table>
<thead>
<tr>
<th>Project Name</th>
<th>Edit</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr>
<td>Item Name</td>
<td>
<button>EDIT</button>
</td>
<td>
<button>DELETE</button>
</td>
</tr>
</tbody>
</table>
Now you should have something that looks like this:
CRUD Table at beginning
This is how you would create a table that has nothing to do with a database. For a table that is linked to databases, we need to use a for loop. In Vue js, the for loop is called v-for loop. To we get to the v-for method, we need to go through the ‘Database-Route-Script-Template’ road.
We should submit some items and then the database part is done.✅
Let’s go to the route and see the portfolio items
The route part is done ✅
The script tag’s getPortfolio() method
getPortfolio() {
axios.get('/api/portfolio')
.then(response => {
this.items = response.data;
})
.catch(error => {
console.log(error);
});
},
data() in script tag
data() {
return {
items: '',
}
},
The script tag part is done ✅
If we use vue-devtools, we can see the items that we are getting from the route
We can use v-for to go through these items and display parts of the **items**
data. The part that we want to display in this case is the name that is found within every item.
How a v-for loop works:
**variable**
for these objects.**variable**
is then used as the first word in the v-for loops’s quotation marks i.e. **""**
v-for="variable in object"
The object we want to loop through is called **items**
v-for="variable in o̶b̶j̶e̶c̶t̶ items"
A suitable word for an ‘object in items’ is ‘item’ so let’s change **variable**
to **item**
v-for="item in items"
Apply this v-for to the **tr**
within **tbody**
to render all the projects and get the name within every item using {{item.name}}
<tr v-for="item in items">
<td>{{item.name}}</td>
<td>
<button>EDIT</button>
</td>
<td>
<button>DELETE</button>
</td>
</tr>
Data from both the projects is rendered in the table
Let’s build the add button above the **thead**
<caption><button>+ Add New Item</button></caption>
<thead>
...
</thead>
Upon clicking the edit button, we want a modal to popup and the modal should contain the update form.
<button @click="editItemPopup(item)">EDIT</button>
In methods:
editItemPopup(item) {
this.item = item;
if (this.updateItemModal === false) {
this.updateItemModal = true;
}
else if (this.updateItemModal === true) {
this.updateItemModal = false;
}
},
Code Explanation: editItemPopup is the method that will be used. item in the brackets is the v-for
**variable**
.
We can see that v-for can also be used to run methods.
Before the PortfolioEditor.vue gets too big, we should make the table a child component and pass the items as a prop (similar to Chapter Three).
<template>
<section>
<h1>Portfolio Editor</h1>
<form>...</form>
<portfolio-editor-table :items="items"/>
</section>
</template>
<script>
import portfolioEditorTable from './portfolioEditorTable';
export default {
name: "PortfolioEditor",
data() {
return {
items: '',
}
},
components: {portfolioEditorTable},
mounted() {
this.getPortfolio();
},
methods: {
getPortfolio() {
axios.get('/api/portfolio')
.then(response => {
this.items = response.data;
})
.catch(error => {
console.log(error);
});
}
}
}
</script>
Passing the prop
<script>
export default {
name: "portfolioEditorTable",
props: {
items: ''
},
data() {
return {
p: this.$parent
}
}
}
</script>
You may have noticed that within data there is **p: this.$parent**
. This is just so that you don’t have to type **this.$parent**
when calling a method in the parent, you can just shorten it to **p**
(or anything of your choosing).
<button @click="p.editItemPopup(item)">EDIT</button>
We should keeep the editItemPopup method in the parent component because we want to have the CRUD api methods in one file and the “non-api” methods in other files.
So if you’ve got the editItemPopup(item) in the table component, you should move it to the parent component.
Import the update-item-modal and place it above the table component within PortfolioEditor.vue
<section>
<h1>Portfolio Editor</h1>
<update-item-modal v-if="updateItemModal" :item="item"/>
<portfolio-editor-table :items="items"/>
</section>
We will create this modal in the next chapter.
At the end of the last chapter, we had a list of what to do in future chapters. From that list we have tackled front-end and back-end validations (for the add/create functionality)
We are yet to to the following:
And we are yet to create a Portfolio.vue component which will consist of
☞ Laravel Tutorial - Abusing Laravel
☞ Restful controllers in Laravel
☞ Learn GraphQL with Laravel and Vue.js - Full Tutorial
☞ Complete Employees Management Tutorial - Laravel 8 With Vuejs - Full Laravel 8 Course