VueJS - Load child components dynamically - laravel

I'm trying to figure out how to conditionally load sub/child component from Laravel's blade template
I have a main container, which is being called from the blade:
<structure-container view='menu-index'></structure-container>
Here is the StructureContainer.vue
<template>
<div>
//I can have if-else statement here by "view", but looking for a better solution
<menu-index></menu-index>
</div>
export default{
components: {
'menu-index': require('./menu/IndexComponent.vue'),
'test': require('./menu/TestComponent.vue'),
},
props: ['view']
}
As you can see I have two child components: menu-index & test. I want to pass the parameter from blade and display the corresponding component. Can I avoid if-else statements? there should be a better way to do this

You can simply bind the component name dynamically to the is property (using v-bind:is or :is), i.e.:
<div>
<component :is="view"></component >
</div>
The really awesome thing about this is that the <component> container is reactive, and you can dynamically load/unload/change the components on-the-fly. How awesome is that? :)
For more information, I encourage you to read up the documentation on dynamic components on the official docs.

Related

Getting an Alpine JS value inside a blade component?

I have an x-for loop:
<template x-for="item in items" :key="item.id">
Inside the template I call my blade component:
<x-card />
How can I pass vars from the Alpine JS loop to the blade component?
I've seen this and have tried to implement it:
<template x-for="item in items" :key="item.id">
<x-card ::title="item.title" />
....
But I cannot output the title var inside the blade component.
AlpineJS is JavaScript, therefore client side. Blade is server-side. The tip you passed is what is described in the docs and is just to prevent Blade from evaluating the statement, since Blade can also process single-colon variables.
If your goal is to display the title as text within the card, then have a look at x-text. You should be able to call x-text="item.title". :title should be setting the title attribute on the item.

VueJS v-for in laravel blade

How can I create a list in laravel (v7) blade using the VueJS v-for method?
inside home.blade.php:
<template v-for="(item,index) in this.list">
<qm-item number="#{{index}}"></qm-item>
</template>
in the source code this results in:
<qm-item number="index"></qm-item>
but i would like to have number=0 or =1 on the first qm-item, number=2 on the second and so on.
UPDATE: the issue was how I was checking it, since the DOM is re-rendered I cannot check in the browser source code for this, because this won't be up to date.
You should bind the number as follows:
<qm-item :number="index"></qm-item>
You need to bind number:
<template v-for="(item,index) in this.list">
<qm-item :number="index"></qm-item>
</template>
index will be defined on the Vue.js side, not on the Laravel side.
When you pass data from blade to your Vue component you have to bind the props with a leading :
So, in your case, it should be <qm-item :number="{{index}}"></qm-item>
Also, use variable just like you normally do in the blade.

How to enhance an existing Laravel project with VueJS?

I just finished my project using only the Laravel framework. Now I want to add vue.js into my project to render the views without loading them. I went through some tutorials and found that I need to convert my blade files into Vue components to achieve this. But as I know it's a big process as some of the functions won't work in VueJS. And I don't know how to do it. Please, someone, guide me here on how to do that. Thanks in advance.
Rebuild your basic structure into a Vue template:
// MyTemplate.vue
<template>
<div> <!-- keep this single "parent" div in your template -->
<!-- your Blade layout here -->
</div>
</template>
<script>
export default {
props: [ 'data' ]
}
</script>
Add your Vue template as a global component in app.js:
// app.js
import Vue from 'vue';
window.Vue = Vue;
Vue.component('my-template', require('./MyTemplate.vue').default);
const app = new Vue({
el: '#app'
});
Use this new Vue template in your Blade template as below:
<my-template :data="{{ $onePhpVarThatHoldsAllYourData }}"></my-template>
Once inside the Vue template, you will not be able to reach back to your Laravel methods (e.g. #auth) or for additional data without an Ajax request, so make sure you package all the data you need in your Vue template up front. All of the data you send in will be prefixed inside the Vue template with the name of the prop you assign it to, in this case data.
Note, once you get more familiar with Vue you will likely start segregating the individual data values being passed to your template. For that, you will need to specify additional props in the props array in step 1, e.g.
props: ['a', 'b', 'c'],
and then individually pass their values with
<my-template :a="{{ $a }}" :b="{{ $b }}" :c="{{ $c }}"></my-template>
Convert your Blade directives to Vue directives:
Loops (e.g. #foreach) to v-fors:
#foreach ($items as $item)
<li>{{ $item }}</li>
#endforeach
becomes
<li v-for="item in data.items">{{ item }}</li>
Control structures (e.g. #if ($something !== $somethingElse) ... #endif) to v-ifs:
<div v-if="data.something !== data.somethingElse">
...
</div>
In general, as it was already mentioned in comments, there's no short way for converting your application from blades to VueJS components. Anyway, if you consider migrating to VueJS, I'd recommend you to make a full migration instead of partially using Vue components.
The main idea of migration to VueJS is to transfer all logic that you did in blade templates (like foreach's, if's etc) to Vue components and fetch all data using AJAX requests (e.g. with help of axios or nativelly).
In this case, your controllers should return all data needed for page rendering and Vue components will take care of rest rendering logic.
Also, it's a good option to use vue-router to handle rounding and make your application behave as SPA. In this case, you should create one wildcard route in your application that will return only one blade template. Inside of this template you should insert root tag that will initiate VueJS. The rest will be on the VueJS side.
If you are planning to migrate the whole application then start with authentication.
Part #1: https://codebriefly.com/laravel-jwt-authentication-vue-ja-spa-part-1/
Part #2 https://codebriefly.com/laravel-jwt-authentication-vue-js-spa-part-2/
This tutorial helped me in the past to getting started. After that split your code into components.
If you want to learn basics first then you can go with this tutorial I found this useful.
https://www.youtube.com/playlist?list=PL4cUxeGkcC9gQcYgjhBoeQH7wiAyZNrYa
Hope this helps!

Using iron-ajax to grab string and insert onto page

<template is="dom-bind">
<iron-ajax
auto
url="http://localhost:9000/api/version"
last-response="{{versionNumber}}"
verbose
></iron-ajax>
<template is="dom-repeat" items="{{versionNumber}}">
<small class="u-ml+">{{item.first}}</small>
</template>
<template>
<small>[[versionNumber]]</small>
</template>
</template>
I'm a little bit lost with Polymer - I have an iron-ajax element which is set up to talk an API endpoint, which is returning the current version of my application.
I want to be able to bind this version number directly on the page. Is there something I'm doing incorrectly in the above code?
I tried using a dom-repeat template and attempting to grab the first item, but I don't seem to be getting anything. Same with attempting to one-way bind inside of a <small> tag.
My understanding is that if I'm within a dom-bind template, I don't have to define a custom element.
Yes, data-binding works inside of a dom-bind template without the need for a custom element.
One problem in your code is the template tag around <small>
<template>
<small>[[versionNumber]]</small>
</template>
The content of a template by itself won't be shown/rendered in the DOM. See http://www.html5rocks.com/en/tutorials/webcomponents/template/ for some detailed information about templates).
Using <small>[[versionNumber]]</small> inside of your dom-bind template with the extra template tag should work.
Another issue is, that iron-ajax by default handles responses as JSON, so will probably run into a parse error when it receives a string and last-response will get no value.
You would have to specify the handleAs property of iron-ajax accordingly.
<iron-ajax handle-as="text" ...>
And dom-repeat will only work for arrays.

Infinite Scroll for Polymer 1.0 (without appending)

Tried mixing some jQuery but everything is based on appending, something I don't wanna use because i'm trying to base my design completely on binding and stamping templates and elements. I tried using scrollTop in various ways but I always ended up being depended on what's appended on the local DOM. If i'm not mistaken, dom-repeat has nothing to do with appending, but with creating the same stamp and bind it multiple times (correct me if i'm wrong, I started looking into Polymer 1 recently).
I only found this one good example of using infinite scroll over a repeating template https://github.com/chadliu23/event-infinite-scroll but unfortunately it's not what i'm looking for as I'm mixing iron-ajax parsing data into my template. Simulating chadliu23's example lead me into a silly middle step of creating an extra array and pushing data from ajax into it, but it is totally something I don't want to do cause it's messing with my repeatable template restamping.
Also realized that there are ways to create infinite scrolling-ish effects with css but can't figure out any other way to implement this but in sets of images.
Meanwhile, sadly, it seems like iron-list is nowhere near ready yet and I can't find any way to use the concept idea of core-list into polymer 1.0.
So..... Any suggestions on the table?
I think you need an item-page component:
<dom-module id="item-page">
<template>
<iron-ajax id="ajax"
params="{{_composeParamsForPage(page)}}"
last-response="{{pageData}}">
</iron-ajax>
<template is="dom-repeat" items="{{pageData}}" as="item">
<!-- Compose your list of items for this page here -->
</template>
</template>
<script>
(function(){
"use strict";
Polymer({
is: 'item-page',
properties: {
page: {
type: Number,
observer: '_updatePage(page)'
}
},
_updatePage: function(page) {
this.$.ajax.generateRequest();
}
});
})();
</script>
</dom-module>
Then, to create an infinitely scrolling list:
<template is="dom-repeat" items="{{pages}}" as="page">
<item-page page="{{page}}"></item-page>
</template>
It's important that in your item-page component the iron-ajax does not have auto set, because the page property will initially be set to undefined. Instead, you should observe page and act accordingly only once it's updated to something other than undefined.

Resources