Dynamic templating and decoraiton in VUE - javascript

I have VUE Field and Form components which renders a form depending on dynamic data.
<template>
<form #submit="$emit('submit', $event)" >
<template v-for="(item, index) in form.elements">
<Field
v-model="value[index]"
:label="item.options.label"
:type="item.type"
/>
</template>
</form>
</template>
I can render a form by passing structure data
<Form :form="formStructure" v-model="state" #submit="save()" />
This renders the from elements with the order in the formStructure object
What I want to do is to add a decoration template to Form component and customize the view.
<Form :form="formStructure" v-model="state" #submit="save()" >
<template >
<div class="row">
<div class="col-4"><i class="icon-user"></i> <!-- Field component will be render here by Form componenet --> </div>
<div class="col-8"><i class="icon-mail"></i> <!-- Field component will be render here by Form componenet --> </div>
</div>
<h2>A Custom Header</h2>
<div class="row">
<!-- another row with other elements-->
</div>
</template>
</Form>
formStructure data is someting like
{
"form":{
"method":"post",
"id":"formF75a1543a"
},
"elements":{
"username":{
"type":"inputtext",
"options":{
"label":"Pick a username"
}
},
"email":{
"type":"inputtext",
"options":{
"label":"Your e-mail"
}
}
}
}
How can I achive this?

I found the answer after a long research. Slots are not enough to solve the problem. Teleport will do the trick.
<!-- The Form Component -->
<template>
<form #submit="$emit('submit', $event)" >
<slot name="default" ></slot>
<template v-for="(item, index) in form.elements">
<teleport :to="'#'+index" :disabled="!fieldTargetExists(index)">
<Field
v-model="value[index]"
:label="item.options.label"
:type="item.type"
/>
</teleport>
</template>
</form>
</template>
<!-- Using component with a template -->
<Form :form="formStructure" v-model="state" #submit="save()" >
<template >
<div class="row">
<div class="col-4"><i class="icon-user"></i> <span id="username"></span> </div>
<div class="col-8"><i class="icon-mail"></i> <span id="email"></span> </div>
</div>
<h2>A Custom Header</h2>
<div class="row">
<div id="submit"></div>
</div>
</template>
</Form>

Related

Vue Js component wont display in Blade view Laravel

My Blade View 'chat.blade.php'
#extends('layouts.app')
#section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading"</div>
<div class="panel-body">Chats
<chat-messages :messages="messages"></chat-messages>
</div>
<div class="panel-footer">
<chat-form
v-on:messagesent="addMessage"
:user="{{ Auth::user() }}"
></chat-form>
</div>
</div>
</div>
</div>
</div>
#endsection
This code is extend from app.blade.php, which probably its not really the main focus of the problem
and here is My 2 Component From My Vue JS
'ChatMessages.vue'
<template>
<ul class="chat">
<li class="left clearfix" v-for="message in messages">
<div class="chat-body clearfix">
<div class="header">
<strong class="primary-font">
{{ message.user.name }}
</strong>
</div>
<p>
{{ message.message }}
</p>
</div>
</li>
</ul>
</template>
<script>
export default {
props: ['messages']
};
</script>
and here is the 'ChatForm.vue'
<template>
<div class="input-group">
<input id="btn-input" type="text" name="message" class="form-control input-sm" placeholder="Type your message here..." v-model="newMessage" #keyup.enter="sendMessage">
<span class="input-group-btn">
<button class="btn btn-primary btn-sm" id="btn-chat" #click="sendMessage">
Send
</button>
</span>
</div>
</template>
<script>
export default {
props: ['user'],
data() {
return {
newMessage: ''
}
},
methods: {
sendMessage() {
this.$emit('messagesent', {
user: this.user,
message: this.newMessage
});
this.newMessage = ''
}
}
}
</script>
and here is the following screenshot of the problem
Click here for the Picture
as you can see in the screenshot, the chat component from vue.js listed below, wont show up, its just appear blank, only displaying scrollbar,navbar and 'Chats' text. which is came from app.blade.php]
Please help me :). Thanks
You do not commit any messages to your component. :messages="messages" is Vue syntax.
Assuming your controller assigns $messages to your view:
<chat-messages messages="{{ json_encode($messages) }}"></chat-messages>
should do it. You need to use blade variables to assign

Is there any feasible solution to split the components template into multiple parts in Vue js?

I have a component and it has different template-content based on the props value. I feel like the component's structure isn't good enough and it'll be really uncomfortable for any person to understand the code. How can I reduce the components into multiple divisions?
<template>
<div>
<div v-if='config.isTag'>
<!-- some 10 lines of code -->
<span
v-if="ok"
class="class">
</span>
<span v-else >
<em>
<!-- some 3 lines of code --></em>
</span>
</div>
<div
v-if="ok"
class='classes'>
<div v-if="ok">
<div v-if="ok" >
<!-- some 20 lines of code -->
</div>
<div
v-else
class="classes"
>
<!-- some 40 lines of code -->
<div
class="class">
<!-- some 4 lines of code -->
</div>
</div>
</div>
<div
class="option-list"
>
<div
v-if="ok">
<!-- some 30 lines of code -->
</div>
<div
v-show='condition_ok'
v-for="(data, i) in list"
:key="i">
<!-- some 40 lines of code -->
</div>
</div>
</div>
</div
</template>
So this is my template and it takes almost 200 lines of code..... Is there any possible way that I could make it shorter or I could have any template from multiple places and just import it if needed?
That is Splitting the components into multiple fragments.
It's not only possible, it's essential!
In your case, it could be done like so:
<template>
<div>
<div v-if='config.isTag'>
<CustomComponentOne></CustomComponentOne>
</div>
<div v-if="ok"
class='classes'>
<CustomComponentTwo></CustomComponentTwo>
<div class="option-list">
<div v-if="ok">
<CustomComponentThree></CustomComponentThree>
</div>
<div v-show='condition_ok'
v-for="(data, i) in list"
:key="i">
<CustomComponentFour></CustomComponentFour>
</div>
</div>
</div>
</div>
</template>
<script>
import CustomComponentOne from '#/components/CustomComponentOne'
import CustomComponentTwo from '#/components/CustomComponentTwo'
import CustomComponentThree from '#/components/CustomComponentThree'
import CustomComponentFour from '#/components/CustomComponentFour'
export default {
components: {
CustomComponentOne,
CustomComponentTwo,
CustomComponentThree,
CustomComponentFour
}
}
</script>
<style scoped>
</style>

Add VueJs modal component to Laravel template

Question about simple situation, I want add vue modal component to default Laravel project.
When I registered in this way :
Vue.component('modal', {
template: '#modal-template'
})
new Vue({
el: '#app',
data: {
showModal: false
}
})
And named component like this :
ModalComponent.vue
Browser give me next errors.
All details in the Github repository
By the way, maybe better add modal window from bootstrap with jQuery or it will be messy?
you should have :
Vue.component('modal', require('./components/ModalComponent.vue'));
and inside your ModalComponent.vue file:
<template>
<transition name="modal">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header">
default header
</slot>
</div>
<div class="modal-body">
<slot name="body">
default body
</slot>
</div>
<div class="modal-footer">
<slot name="footer">
default footer
<button class="modal-default-button" #click="$emit('close')">
OK
</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
</template>
<script>
export default {
}
</script>
view.blade.php
<div id="app">
<button id="show-modal" #click="showModal = true">Show Modal</button>
<!-- use the modal component, pass in the prop -->
<modal v-if="showModal" #close="showModal = false">
<!--
you can use custom content here to overwrite
default content
-->
<h3 slot="header">custom header</h3>
</modal>
</div>

jquery geocomplete not working with vue js

When I wrap a form with jquery geocomplete in a Vue modal, the geocomplete stops working. I'm not sure how to fix it so that geocomplete works when the modal is active.
Here is a working example without Vue or a modal:
https://jsfiddle.net/3gzovugy/68/
Here is the same example, wrapped in the vue site's example modal, that won't geocode. Any suggestions?
https://jsfiddle.net/3gzovugy/96/
<div id="app">
<button id="show-modal" #click="showModal = true">Show Modal</button>
<modal v-if="showModal" #close="showModal = false">
<div slot="body">
<form>
<div class="item">
<input id="autocomplete" placeholder="Look up your address" type="text">
</div>
<div class="item">
<input class="cat_textbox" id="houseNo" data-geo="street_number" type="text" placeholder="House Number" maxlength="50" />
</div>
<div class="item">
<input class="cat_textbox" id="street" data-geo="route" type="text" placeholder="street" maxlength="50" />
</div>
<div class="item">
<input class="cat_textbox red" id="BillingAddress" data-geo="street_address" type="text" placeholder="Billing Address" maxlength="50" name="BillingAddress" />
</div>
</form>
</div>
</modal>
</div>
<template id="modal-template">
<transition name="modal">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header">
default header
</slot>
</div>
<div class="modal-body">
<slot name="body">
default body
</slot>
</div>
<div class="modal-footer">
<slot name="footer">
default footer
<button class="modal-default-button" #click="$emit('close')">
OK
</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
</template>
I see your geocomplete related code is in $(document).ready(function(). Taking inspiration from this answer, you can move code inside document.ready to one of the lifecycle hooks for this purpose. You can try using mounted as it comes pretty close to $(document).ready():
Vue.component('modal', {
template: '#modal-template',
mounted () {
$("#autocomplete").geocomplete({
details: "form div",
detailsAttribute: "data-geo",
types: ["(cities)"],
componentRestrictions: {
country: "in"
}
});
}
})
With these changes, you code starts to show suggestion: check here. You may need some other UI changes to see the geocomplete dropdown on top.

Semantic UI Modal only shows up once in Meteor

I have got a meteor app which uses the Semantic UI. I am using the semantic:ui-css package for this.
I have got a template called project where I want to show a modal.
There for I created a second template which is the modal like this:
<template name="xform">
<div id="modal" class="ui modal">
<i class="close icon"></i>
<div class="header">New Project</div>
<div class="content">
<div class="ui form" id="newProject">
<div class="one fields">
<div class="field">
<legend>Project Details</legend>
<label>Name</label>
<input id="name" name="name" type="text" placeholder="Project name">
</div>
<input type="submit" class="button small createProject" style="color:white;position:relative;float:right;padding: 0.875rem 1.75rem 0.9375rem 1.75rem;font-size: 0.8125rem;" value="Create">
</div>
</div>
</div>
<div class="actions">
<div class="ui button">
Cancel
</div>
<div class="ui button">
Okay
</div>
</div>
</div>
</template>
Then I haved added a event to a button like this to show the modal:
Template.projects.events({
'click .newProject': function(event, template){
template.$('.ui.modal').modal({
onDeny : function(){
return false;
}}).modal('show');
}
});
If I click the button for the first time the modal opens like wanted.
I can see that the modal is now not anymore inside the html of my template but at the end of my body.
I can close the modal using the close button but when I now try to open it again it does not open any more.
The modal div is still on the bottom of my body.
I have tried several ways to workaround this problem but none have worked.
Here my complete code: http://i.imgur.com/r9JNrlr
To follow up from my comment, this is the code that is working for me. It definitely seems like you have an issue with jQuery finding multiple elements. I created a new meteor project:
$ meteor create semantic
$ cd semantic
$ meteor add semantic:ui-css
$ meteor add iron:router
semantic.html
<template name="MainLayout">
<h1>Title</h1>
{{> yield}}
</template>
<template name="projects">
{{> xform}}
<button class="newProject">New Project</button>
</template>
<template name="xform">
<div id="modal" class="ui modal">
<i class="close icon"></i>
<div class="header">New Project</div>
<div class="content">
<div class="ui form" id="newProject">
<div class="one fields">
<div class="field">
<legend>Project Details</legend>
<label>Name</label>
<input id="name" name="name" type="text" placeholder="Project name">
</div>
<input type="submit" class="button small createProject" style="color:white;position:relative;float:right;padding: 0.875rem 1.75rem 0.9375rem 1.75rem;font-size: 0.8125rem;" value="Create">
</div>
</div>
</div>
<div class="actions">
<div class="ui button">
Cancel
</div>
<div class="ui button">
Okay
</div>
</div>
</div>
</template>
semantic.js
if (Meteor.isClient) {
Template.projects.events({
'click .newProject': function(event, template){
$('.ui.modal').modal({
onDeny : function(){
return false;
}}).modal('show');
}
});
Meteor.startup(function() {
Router.configure({
layoutTemplate: 'MainLayout'
})
Router.route('/',{
name: 'projects'
})
});
}
After running meteor and loading http://localhost:3000 I can open and close the modal multiple times.
Try to set the modal non-detachable in onRendered callback function of template.
Template.projects.onRendered(() => {
$('.ui.modal').modal({ detachable: false });
});
Hope it helps.

Categories