Data bound v-if dynamic condition - javascript

I was wondering if it's possible to have a data binded v-if directive, I have an array of objects representing nav links or buttons (for login , logout ...) each of this objects has a v-if property where I define the v-if condition as a string.
From my laravel backend:
$globals['socialLinks'] =
[
[ 'title' => 'facebook', 'v-if' => '$app.auth', 'icon' => 'fab fa-facebook-f', 'url' => config('app.facebook'), 'image' => '/img/social/facebook-alt-white.svg' ],
];
In my template (I transform this into json and pass it to my vue component template):
<div class="LAYOUTfooter9_row_container">
<a class="LAYOUTfooter9_social_image_container" :href="social.url" v-for="(social,index) in $app.globals.socialLinks" v-if="[social.v-if]" :key="index+'S'">
<img class="LAYOUTfooter9_social_image" :src="social.image">
</a>
</div>

Execute JavaScript code stored as a string can be dangerous. Think twice if you really need it...
<a class="LAYOUTfooter9_social_image_container" :href="social.url" v-for="(social,index) in $app.globals.socialLinks" v-if="evaluateCondition(social.v-if)" :key="index+'S'">
methods: {
evaluateCondition(condition) {
return eval(condition)
}
}
Your conditions have to be an expression and will be executing in the context of Vue instance so instead of $app.auth you need to use this.$app.auth

Related

Maping inside a map - Vanilla Javascript

Coding noob here :)
I'm working on a project where I have prepared an array that I'm mapping over and printing. Inside the array I have another array, called "lang", that I can't reach from the map. I want to print each item in that array into an <li>, as the code is now all the items are printed as one <li> , which I don't want. I have tried maping inside the map in various ways but not gotten it to work.
Is the right way to do a map inside the map? In that case, how is it done?
Is there another way to do this?
Please see part of the array and the map function I have created down below. projectContainer, which I'm adding the innerHTML to, is a div inside the index.html file.
},
{
img: "./pictures/guess-who.png",
projectTitle: 'GUESS WHO?',
projectDescription: 'Recreation of the game "Guess who?" built with HTML, CSS and JavaScript.',
lang: [
'HTML', 'CSS', 'JavaScript'
],
url: '',
className: '',
id: 6
}
]
}
const printProjects = () => {
projectArray.projects.map((project) => {
projectContainer.innerHTML += `
<div class="project-container ${project.id}">
<img src=${project.img} alt="picture of guess who game"></img>
<h4>${project.projectTitle}</h4>
<p>${project.projectDescription}</p>
<ul>
<li>${project.lang} </li>
</ul
</div>
`
})
}
printProjects()
You can do something like this and use map(...).join('')
<ul>
${project.lang.map(el => `
<li>${el}</li>
`).join('')}
</ul>

Vue JS Tags Won't Render to HTML From JSON

I have JSON that contains HTML tags such as href, how can I click it without refreshing the page? I have already searched for it but no one is working...
Table
JSON
I'm using JSON datatable.
to me it seems you need to use auth guards
see here
https://router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards
1st thing you must try is whether your are able to log something when your route is clicked by using code here
router.beforeEach((to, from, next) => {
console.log(to);
console.log(from);
})
if that's successful for this particular route try following
beforeRouteLeave (to, from, next) {
if (to === "edit") {
next(false)
}
}
Unless you're on full build, you cannot unfortunately add components dynamically like that. Have a read on Template Compilation and how to make use of it.
But in most cases (unless you're server-side rendering), you'd want to pass the router-link attributes as an object, something like:
const jsonData = [
{
id: 2,
title: 'First Vue JS',
body: 'Vue JS',
aksi: {
text: 'Edit',
attrs: {
to: {
name: 'edit',
params: {
// Assuming you have the `post` item here (usually while doing `v-for`)
id: post.id
}
},
class: 'btn btn-primary'
}
}
}
]
And rendering them, like:
<table>
<tr v-for="item in jsonData" :key="item.id">
<td>{{item.title}}</td>
<td>{{item.body}}</td>
<td>
<router-link v-bind="item.attrs">{{item.aksi.text}}</router-link>
</td>
</tr>
</table>
If you're not looping over them in the template, you might want to figure out how to do column rendering and customize this particular cell therein.

How to filter a data object?

Context: I receive from Elasticsearch the result of a search (example below) which I put into a Vue.js data object. I then list the data via <div v-for="result in results">{{result.name}}</div>.
var vm = new Vue({
el: "#root",
data: {
results: [{
'name': 'john',
'big': true
},
{
'name': 'jim',
'tall': true
},
{
'name': 'david'
}
]
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.js"></script>
<div id="root">
<div v-for="result in results">{{result.name}}</div>
</div>
I now would like to filter the results. To do so, I will have switches which will be bound via v-model.
Question: what is the correct way to handle filtering in Vue.js?
I would like to render (via a v-if, I guess) only elements from results which match a filter (say, big is checked - so only johnshould be visible), or a concatenation of filters (logical AND).
The part I have a hard time turning into Vue.js philosophy is "display the element if all active switches are present (value true) in that element).
Since I am sure that having a chain of v-ifs is not the right approach, I prefer to ask before jumping into that (and I would probably rather rerun a search with parameters than go this way - but I would prefer to avoid the search way).
Create a computed property which returns only the filtered results:
computed: {
filteredResults() {
return this.results.filter((result) => {
// your filter logic, something like this:
// return result.big || result.tall
});
}
}
And use it in the v-for instead:
<div v-for="result in filteredResults">{{result.name}}</div>

Vue.js 2 - retrieve form elements from server and render CRUD

I'm Vue.js newbie and my task is:
make an ajax call (GET) to server, using RESTful API (Laravel on background)
retrieve a (JSON) list of Form CRUD items in array (like checkbox, input text, textarea...) with their properties (value, checked, custom classes...)
render CRUD form with these form items maybe using Vue's loop
I'm wondering if it could be rendered using components somehow. But I don't know the correct way.
Frankly, I exactly don't know how to solve this problem with Vue.js - rendering items from array and each item has it's own markup and properties (checkbox has it's own, textbox, select, textarea...).
I'm building a web application based on CRUD operations and I'm trying to write universal components. The easiest way is to do a special component with hard-written sub-components for each subpage, but I don't like this way if not needed.
Thank you!
EDIT: I don't have much code yet, but this is where I am...
<script>
// ./components/CrutList.vue
export default {
mounted() {},
data() {
return {
items: []
}
},
props: ['resource'],
methods: {
getItems() {
var resource = this.$resource('api/'+this.resource+'{/id}');
resource.get({}).then(function(items){
if(items.body.status == 'success'){
this.items = items.body.items;
}
}).bind(this);
},
deleteItem(item) {
// perform CRUD operation DELETE
alert('delete action');
}
}
}
</script>
My idea is using CrudList component to CRUD listing...
<crud-list resource="orders">
In laravel I do something like this:
return response()->json([
'status' => 'success',
'items' => [
[
'itemComponent' => 'checkbox',
'props' => [
'checked' => true,
'label' => "Checkbox č.1",
'name' => 'checkbox1'
]
],
[
'itemComponent' => 'checkbox',
'props' => [
'checked' => true,
'label' => "Checkbox č.2",
'name' => 'checkbox2'
]
],
[
'itemComponent' => 'checkbox',
'props' => [
'checked' => true,
'name' => 'checkbox3'
]
],
],
]);
...it's very simplified, but it's just example of what I'm doing.
Now the problem is:
take the 'itemComponent' part from the returned array item (this is in a loop),
if it's a checkbox, take (for example) Checkbox.vue component, fill it with properties ('props' part of the array item)
I read about slots, but it's not what I'm looking for. Is there something I can use for dynamic components?
Check out this jsFiddle working example for dynamic forms:
https://jsfiddle.net/mani04/kr8w4n73/1/
You can do it easily by using a lot of v-ifs for each and every form element type you might get from server. It is a bit cumbersome but I can't find any other way.
In the above example, I have the form structure as follows:
var formItems = [{
input_type: "text",
input_label: "Login",
values: {
value: "your_name#example.com"
}
},
{...},
{...}];
Once you have that data, then it is a matter of iterating through formItems, checking input_type and activating the relevant form control.
Here is how my dynamic form template looks like, for the above input:
<div v-for="formItem in formValues">
<div v-if="formItem.input_type == 'text'">
<input type="text" v-model="formItem.values.value">
</div>
<div v-if="formItem.input_type == 'password'">
<input type="password" v-model="formItem.values.value">
</div>
<div v-if="formItem.input_type == 'checkbox'">
<input type="checkbox" v-model="formItem.values.checked">
{{formItem.values.label}}
</div>
</div>
My jsFiddle example uses form-horizontal from bootstrap, and I am also able to display the labels well. If I put that in the example above, it will get cluttered and will not let you see how it works.
Hope it helps! You can change the formItems data structure to meet your needs, and modify the template accordingly.

Passing data to components in vue.js

I'm struggling to understand how to pass data between components in vue.js. I have read through the docs several times and looked at many vue related questions and tutorials, but I'm still not getting it.
To wrap my head around this, I am hoping for help completing a pretty simple example
display a list of users in one component (done)
send the user data to a new component when a link is clicked (done) - see update at bottom.
edit user data and send it back to original component (haven't gotten this far)
Here is a fiddle, which fails on step two: https://jsfiddle.net/retrogradeMT/d1a8hps0/
I understand that I need to use props to pass data to the new component, but I'm not sure how to functionally do it. How do I bind the data to the new component?
HTML:
<div id="page-content">
<router-view></router-view>
</div>
<template id="userBlock" >
<ul>
<li v-for="user in users">{{user.name}} - <a v-link="{ path: '/new' }"> Show new component</a>
</li>
</ul>
</template>
<template id="newtemp" :name ="{{user.name}}">
<form>
<label>Name: </label><input v-model="name">
<input type="submit" value="Submit">
</form>
</template>
js for main component:
Vue.component('app-page', {
template: '#userBlock',
data: function() {
return{
users: []
}
},
ready: function () {
this.fetchUsers();
},
methods: {
fetchUsers: function(){
var users = [
{
id: 1,
name: 'tom'
},
{
id: 2,
name: 'brian'
},
{
id: 3,
name: 'sam'
},
];
this.$set('users', users);
}
}
})
JS for second component:
Vue.component('newtemp', {
template: '#newtemp',
props: 'name',
data: function() {
return {
name: name,
}
},
})
UPDATE
Ok, I've got the second step figured out. Here is a new fiddle showing the progress: https://jsfiddle.net/retrogradeMT/9pffnmjp/
Because I'm using Vue-router, I don't use props to send the data to a new component. Instead, I need set params on the v-link and then use a transition hook to accept it.
V-link changes see named routes in vue-router docs:
<a v-link="{ name: 'new', params: { name: user.name }}"> Show new component</a>
Then on the component, add data to the route options see transition hooks:
Vue.component('newtemp', {
template: '#newtemp',
route: {
data: function(transition) {
transition.next({
// saving the id which is passed in url
name: transition.to.params.name
});
}
},
data: function() {
return {
name:name,
}
},
})
-------------Following is applicable only to Vue 1 --------------
Passing data can be done in multiple ways. The method depends on the type of use.
If you want to pass data from your html while you add a new component. That is done using props.
<my-component prop-name="value"></my-component>
This prop value will be available to your component only if you add the prop name prop-name to your props attribute.
When data is passed from a component to another component because of some dynamic or static event. That is done by using event dispatchers and broadcasters. So for example if you have a component structure like this:
<my-parent>
<my-child-A></my-child-A>
<my-child-B></my-child-B>
</my-parent>
And you want to send data from <my-child-A> to <my-child-B> then in <my-child-A> you will have to dispatch an event:
this.$dispatch('event_name', data);
This event will travel all the way up the parent chain. And from whichever parent you have a branch toward <my-child-B> you broadcast the event along with the data. So in the parent:
events:{
'event_name' : function(data){
this.$broadcast('event_name', data);
},
Now this broadcast will travel down the child chain. And at whichever child you want to grab the event, in our case <my-child-B> we will add another event:
events: {
'event_name' : function(data){
// Your code.
},
},
The third way to pass data is through parameters in v-links. This method is used when components chains are completely destroyed or in cases when the URI changes. And i can see you already understand them.
Decide what type of data communication you want, and choose appropriately.
The best way to send data from a parent component to a child is using props.
Passing data from parent to child via props
Declare props (array or object) in the child
Pass it to the child via <child :name="variableOnParent">
See demo below:
Vue.component('child-comp', {
props: ['message'], // declare the props
template: '<p>At child-comp, using props in the template: {{ message }}</p>',
mounted: function () {
console.log('The props are also available in JS:', this.message);
}
})
new Vue({
el: '#app',
data: {
variableAtParent: 'DATA FROM PARENT!'
}
})
<script src="https://unpkg.com/vue#2.5.13/dist/vue.min.js"></script>
<div id="app">
<p>At Parent: {{ variableAtParent }}<br>And is reactive (edit it) <input v-model="variableAtParent"></p>
<child-comp :message="variableAtParent"></child-comp>
</div>
I think the issue is here:
<template id="newtemp" :name ="{{user.name}}">
When you prefix the prop with : you are indicating to Vue that it is a variable, not a string. So you don't need the {{}} around user.name. Try:
<template id="newtemp" :name ="user.name">
EDIT-----
The above is true, but the bigger issue here is that when you change the URL and go to a new route, the original component disappears. In order to have the second component edit the parent data, the second component would need to be a child component of the first one, or just a part of the same component.
The above-mentioned responses work well but if you want to pass data between 2 sibling components, then the event bus can also be used.
Check out this blog which would help you understand better.
supppose for 2 components : CompA & CompB having same parent and main.js for setting up main vue app. For passing data from CompA to CompB without involving parent component you can do the following.
in main.js file, declare a separate global Vue instance, that will be event bus.
export const bus = new Vue();
In CompA, where the event is generated : you have to emit the event to bus.
methods: {
somethingHappened (){
bus.$emit('changedSomething', 'new data');
}
}
Now the task is to listen the emitted event, so, in CompB, you can listen like.
created (){
bus.$on('changedSomething', (newData) => {
console.log(newData);
})
}
Advantages:
Less & Clean code.
Parent should not involve in passing down data from 1 child comp to another ( as the number of children grows, it will become hard to maintain )
Follows pub-sub approach.
I've found a way to pass parent data to component scope in Vue, i think it's a little a bit of a hack but maybe this will help you.
1) Reference data in Vue Instance as an external object (data : dataObj)
2) Then in the data return function in the child component just return parentScope = dataObj and voila. Now you cann do things like {{ parentScope.prop }} and will work like a charm.
Good Luck!
I access main properties using $root.
Vue.component("example", {
template: `<div>$root.message</div>`
});
...
<example></example>
A global JS variable (object) can be used to pass data between components. Example: Passing data from Ammlogin.vue to Options.vue. In Ammlogin.vue rspData is set to the response from the server. In Options.vue the response from the server is made available via rspData.
index.html:
<script>
var rspData; // global - transfer data between components
</script>
Ammlogin.vue:
....
export default {
data: function() {return vueData},
methods: {
login: function(event){
event.preventDefault(); // otherwise the page is submitted...
vueData.errortxt = "";
axios.post('http://vueamm...../actions.php', { action: this.$data.action, user: this.$data.user, password: this.$data.password})
.then(function (response) {
vueData.user = '';
vueData.password = '';
// activate v-link via JS click...
// JSON.parse is not needed because it is already an object
if (response.data.result === "ok") {
rspData = response.data; // set global rspData
document.getElementById("loginid").click();
} else {
vueData.errortxt = "Felaktig avändare eller lösenord!"
}
})
.catch(function (error) {
// Wu oh! Something went wrong
vueData.errortxt = error.message;
});
},
....
Options.vue:
<template>
<main-layout>
<p>Alternativ</p>
<p>Resultat: {{rspData.result}}</p>
<p>Meddelande: {{rspData.data}}</p>
<v-link href='/'>Logga ut</v-link>
</main-layout>
</template>
<script>
import MainLayout from '../layouts/Main.vue'
import VLink from '../components/VLink.vue'
var optData = { rspData: rspData}; // rspData is global
export default {
data: function() {return optData},
components: {
MainLayout,
VLink
}
}
</script>

Categories

Resources