Vue.js: Conditional rendering based on computed property - laravel

I am trying to conditionally render element(s) based on a computed property but not having much luck even though my computed property is returning true or false correctly.
I am passing the user object in as a property (from Laravel) and all is working fine. I am able to see the user -- and their related role(s).
I've tested via Laravel to make sure I am sending the correct user and the relationship and everything looks good there as well.
Controller
$user = User::with('roles')->find(Auth::id());
blade
<my-component :user="{{ $user }}"></my-component>
VueComponent.vue
<template>
<div>
<div v-if="admin">
<p>You are an admin!</p>
</div>
<div v-else>
<p>You are not an admin.</p>
</div>
</div>
</template>
<script>
export default {
...
computed: {
admin: function () {
this.user.roles.forEach((role) => {
console.log('role: ', role); // role: admin (string)
if (role.name === 'admin') {
console.log('user is an admin!'); // I am getting here.
return true;
} else {
console.log('user is NOT an admin.');
}
});
return false;
},
},
methods: {
//
},
props: {
user: {
type: Object,
required: true,
},
},
}
</script>
I'm sure I am not implementing the computed property correctly; any help is greatly appreciated!

You problem is using foreach in wrong way! please use this instead:
computed: {
admin: function () {
for (var i = 0; i < this.user.roles.length; i++){
if ( this.user.roles[i].name === 'admin') {
return true;
}
}
return false;
}
}
you can read this article about forEach in js https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
Your mistake was return true from forEach callback function and leave this true value useless and then return false value on admin function.

Related

Redirect in inertia js and jetstream in javascript code

I want to be redirected to single question page after creation of question
summitQuestion(){
this.question_button = true
axios.post('/api/question/ask', {
'title' : this.question.title,
'body' : this.question.body,
'tags' : this.tags,
'user_id' : this.$page.props.user.id
}).then(response=>{
this.question_button = false
console.log(response.data)
}).catch(error=>{
this.question_button = false
console.log(error.response.data.errors)
if(error.response.data.errors){
this.title_errors = error.response.data.errors.title
this.body_errors = error.response.data.errors.body
}
})
},
I have this function I want after the success of the request to redirect I a spa way without page reloading to question single page I am using inertia js and jetstream my laravel router is below
Route::middleware(['auth:sanctum', 'verified'])->get('/question/{question}', 'App\Http\Controllers\QuestionController#show')->name('question-single');
Simply use the visit method on the inertia like shown below.
this.$inertia.visit(route('question-single'), { method: 'get' });
If you got everything correct from your code above remaining the redirection without your page reloading, then I guess the modification of your code will be the sample as folows;
summitQuestion(){
this.question_button = true
axios.post('/api/question/ask', {
'title' : this.question.title,
'body' : this.question.body,
'tags' : this.tags,
'user_id' : this.$page.props.user.id
}).then(response=>{
this.question_button = false
// console.log(response.data)
this.$inertia.visit(route('question-single'), { method: 'get', data: response.data });
}).catch(error=>{
this.question_button = false
console.log(error.response.data.errors)
if(error.response.data.errors){
this.title_errors = error.response.data.errors.title
this.body_errors = error.response.data.errors.body
}
})
},
You can make reference to this by visiting The Official Inertiajs Website
If you are using Inertia, you are supposed to create a form with v-model linked to fields. Add to that a button of type submit that call your method (see below the method example).
<form #submit.prevent="submitQuestion">
<input type="text" v-model="form.title">
<button type="submit"></button>
</form>
<script>
export default {
data() {
return {
form: this.$inertia.form({
title: this.question.title,
body: this.question.body,
tags: this.tags,
user_id: this.$page.props.user.id,
}, {
bag: 'default',
}),
}
},
methods: {
summitQuestion: function() {
this.form.post('/api/question/ask');
},
}
};
</script>
The redirection can be done directly on your controller method.
class QuestionController extends Controller
{
public function create(Request $request) {
// Create your question
return redirect()->route('your.route');
}
}

Why this validation gives me always required?

probably I'm doing something wrong, I have been coding by instinct haha. Laravel validation seems super easy to implement but for some reason between my vuejs component to my php function I'm always getting "required".
I'm new with both Laravel and Vuejs, it seems to me that my php function is fine (for what I can see on the web) but probably I'm missing something on the comunication between laravel and vue. Can you tell me whats wrong?
public function createTag(Request $request)
{
try {
$data = request()->validate([
'title' => 'required'
]);
$tag = new Tag;
$tag->title = $request->title;
if($tag->save())
{
$tag->usercontracts()->attach($request->usercontractId);
}
return response()->success(__('success.showing', ['resource' => 'des Vertrags', 'resourceE' => 'tag']), $tag->id, 200);
} catch (Exception $e) {
return response()->error(__('error.showing', ['resource' => 'des Vertrags', 'resourceE' => 'tag']), 400, $e);
}
}
<template>
<div id="relative">
<button #click.prevent="show = 1" v-if="show == 0">+tag</button>
<input type="text" v-model="title" name="title" v-if="show == 1">
<button #click="createTag" v-if="show == 1">add</button>
</div>
</template>
<script>
import TagService from "#/services/TagService";
export default {
name: "add-tag-component",
props: ['usercontractId'],
data(){
return {
title:null,
show:0
};
},
methods:
{
async createTag()
{
const {body: {data}} = await TagService.createTag(this.title, this.usercontractId);
this.$emit('addedTag', this.title, data);
this.title = '';
this.show = 0;
}
}
};
</script>
And this is TagService
export default {
createTag(title, usercontractId, tagId) {
return Vue.http.post(`contract/createTag/${title}/${usercontractId}`, tagId);
}
}
I'm also getting this error. May be here is the answer?
Vue warn]: Error in v-on handler (Promise/async): "[object Object]"
found in
---> at resources/assets/js/components/contract/listing/AddTagComponent.vue
at resources/assets/js/components/contract/listing/ContractListingItemComponent.vue
at resources/assets/js/components/contract/listing/ContractListingComponent.vue
In your TagService
You need to pass the ${title} as payload not as uri.
export default {
createTag(title, usercontractId, tagId) {
return Vue.http.post(`contract/createTag/${title}/${usercontractId}`, tagId);
}
}
to
export default {
createTag(title, usercontractId, tagId) {
return Vue.http.post(`contract/createTag`, {
tagId: tagId,
title: title,
usercontractId: usercontractId
});
}
}
Laravel validates the payload you pass.

check if data passed from laravel to vue exists

Sometimes I need to pass data from my controller to the view and sometimes I dont!
But when theres no data passed I get this error:
Undefined variable: tutorial (View:
/var/www/html/resources/views/home.blade.php)
No data example:
public function index()
{
return view('home');
}
Containg data example
public function tutorial()
{
return view('home', ['tutorial' => 'Welcome to myproject, lets get started!!!']);
}
Blade:
beforeMount()
{
let tutorial = {{ var_export($tutorial) }} // ERROR!!
if (tutorial) {
return
}
this.reloadData()
}
I think I can do this way in my index function:
public function index()
{
return view('home', ['tutorial' => []);
}
But its just gross!! Theres anyway to check if data passed from laravel exists in my vue??
Because you cannot just put the Laravel variable in js.
Options to solving the problem included:
Performing an api request for the data using the application
component once it had been mounted.
Attaching the data into the
Javascript context using the blade template.
You can do some thing like this:
<Home tutorial="{{ $tutorial }}"> <!-- make tutorial become to props -->
</Home>
And in your view component:
Vue.component('Home', {
props: [
{
name: 'tutorial', // add this props
default: '',
}
],
beforeMount()
{
if (this.tutorial) { // use like this.tutorial
return
}
this.reloadData()
}
});

Laravel nova, custom resource tool not appears in edit mode

I have a custom resource-tool working fine in the view panel of a resource, but it dont appears when i go o the edit mode. Is there something i should add to the component or to the Nova configuration to enable the component in the edit mode?
Code in User.php
public function fields(Request $request)
{
return [
ID::make()->sortable(),
Text::make('First name', 'firstName')
->sortable()
->rules('required', 'max:255'),
Text::make('Last name', 'lastName')
->sortable()
->rules('required', 'max:255'),
Text::make('Email')
->sortable()
->rules('required', 'email', 'max:254')
->creationRules('unique:users,email')
->updateRules('unique:users,email,{{resourceId}}'),
Password::make('Password')
->onlyOnForms()
->creationRules('required', 'string', 'min:6')
->updateRules('nullable', 'string', 'min:6'),
YesNovaUserPermissions::make(),
];
}
User view:
User edit:
Nova does not seem to allow you to obtain this functionality with a custom resource but you can with a custom field. You basically create a "dummy" field which does not really exist on the model and use a mutator on the model to overwrite the default model saving functionality.
Following the documentation above, you can build a Vue component which will appear within the resource edit form itself, similarly to how I have done with the tags picker pictured below.
Code for that:
<template>
<default-field :field="field" :errors="errors" :show-help-text="showHelpText">
<label for="tag" class="inline-block text-80 pt-2 leading-tight">Tag</label>
<template slot="field">
<div id="multitag-flex-holder">
<div id="multitag-search-holder" class="w-1/2">
<div class="search-holder">
<label>Search Categories</label>
<input type="text" v-model="searchString" #focus="isSearching = true" #blur="isSearching = false" style="border:2px solid #000"/>
<div class="found-tags" v-if="isSearching">
<div v-for="(tag, i) in foundTags" #mousedown="addToSelected(tag)" :key="i">{{tag.name}}</div>
</div>
</div>
</div>
<div class="select-tags-holder w-1/2">
<div class="selected-tags">
<div v-for="(tag, i) in selectedTags" :key="'A'+i" #click="removeTag(tag)">{{tag.name}} X</div>
</div>
</div>
</div>
</template>
</default-field>
</template>
<script>
import { FormField, HandlesValidationErrors } from 'laravel-nova'
export default {
mixins: [FormField, HandlesValidationErrors],
props: ['resourceName', 'resourceId', 'field'],
data: function () {
return {
selectedTags:[],
isSearching:false,
searchString:''
}
},
mounted(){
console.log(this.field)
this.field.value.forEach((tag)=>{
this.addToSelected(tag)
})
formData.append('whereType', 'Tag');
},
computed: {
// a computed getter
foundTags() {
// `this` points to the vm instance
return this.field.tags.filter((tag) => {
if(tag.name.search(new RegExp(this.searchString, "i")) >= 0){
if(this.selectedTagNames.indexOf(tag.name) == -1){
return tag;
}
};
})
},
selectedTagNames(){
var selNames = this.selectedTags.map((tag) => {
return tag.name;
})
return selNames;
}
},
methods: {
/*
* Set the initial, internal value for the field.
*/
setInitialValue() {
this.value = this.field.value || ''
},
removeTag(tag){
var index = this.selectedTags.indexOf(tag);
if (index > -1) {
this.selectedTags.splice(index, 1);
}
},
addToSelected(tag){
this.selectedTags.push(tag)
},
/**
* Fill the given FormData object with the field's internal value.
*/
fill(formData) {
var tagIdArray = []
this.selectedTags.forEach((tag)=>{
tagIdArray.push(tag.id)
})
formData.append(this.field.attribute, tagIdArray)
},
},
}
</script>
Then, you can overwrite how the save functionality works in your model to accommodate for the "dummy" field. Note below instead of syncing the tags directly on the mutator, which will work most of the time depending on your data structure, I had to pass the tags to the "Saved" event on the model to accommodate for when creating a new record and the associated record id is not yet available, thus cannot be synced for a many to many relationship.
public function setTagsAttribute($value)
{
$tags = explode(",", $value);
$this->tempTags = $tags;
unset($this->tags);
}
protected static function booted()
{
static::saved(function ($article) {
$article->tags()->sync($article->tempTags);
});
}

How to retrieve User name in Vue component with Laravel

I have a vue component that will send a message and I want to send the logged in user name. I'm using laravel 5.5
methods:{
sendMessage(){
this.$emit('messagesent', {
message:this.messageText,
user: {
name : {{Auth()::user()->name}} //Here is where it should be
}
});
this.messageText = '';
}
}
I already tried using
{{Auth()::user()->name}}
Auth()::user()->name
Both give errors. How can I retrieve the name?
Depend on your approach, this can be achieve in different ways
1. Pass in the username from the view
<send-message user={{ $user->username }} ></send-message>
then inside your vuejs component
export default {
props: ['user'],
data(){
return {
user: ''
}
},
methods: {
sendMessage(){
var user = JSON.parse(this.user)
//do you thing here
}
},
}
2. Bind the user information to window object on your page header
<script>
window.user = {!! json_encode([
'user' => $user->username,
]) !!};
</script>

Resources