Hide Card Elements in Vue - vuejs2

I've got a little question on how to do something like that:
I have 2 components (one for a Sidebar and one for my Cards (Bootstrap Cards))
The cards component gets rendered within a foreach loop directly from the database. There are 3 attributes: title, description and category.
Right now I want the sidebar to filter through that category and display all or only one category dynamically via Vue.
But I really don't know how. I'm new to Vue. I think I should use a onclick method but how to look for the right directory.
welcome.blade.php
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
#include('includes.header')
<body>
<div id="app" class="wrapper">
#include('includes.nav')
#include('includes.headerPicture')
<!--#include('includes.headerSidebar')-->
<cards></<cards>
#include('includes.footer')
</div>
<script src="/js/app.js"></script>
</body>
</html>
And the CardComponent
<template>
<div class="container">
<div class="row">
<div class="container sidebar-container">
<ul class="nav justify-content-center nav-tabs">
<li class="nav-item">
<a class="nav-link" href="/" style="color:white">Alle</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/livingspaces" style="color:white">Wohnraum</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/therapies" style="color:white">Therapien</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/news" style="color:white">News</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/achievements" style="color:white">Erfolge</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/events" style="color:white">Events</a>
</li>
</ul>
</div>
<div class="card" style="width: 18rem;" v-for="card in cards">
<img src="/img/card2.jpg" class="card-img-top">
<div class="card-body">
<h5 class="card-title">{{card.title}}</h5>
<p class="card-text">{{card.text}}</p>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
card: {
title: '',
description: '',
category: '',
text: ''
},
cards: [],
validationErrors: '',
uri: 'http://localhost:8000/cards/',
isHidden: true
}
},
methods: {
loadCards() {
axios.get(this.uri).then(response=>{
this.cards = response.data.sortedData;
this.loading=true;
})
}
},
mounted() {
this.loadCards();
}
}
</script>
<style>
.sidebar-container {
padding-bottom: 2em;
}
</style>
JSON Data:
{
sortedData: [
{
id: 1,
title: "Test2222",
description: "Test",
text: "Test",
category: "achievement",
created_at: "2019-01-17 15:56:14",
updated_at: "2019-01-25 12:25:26"
},
{
id: 2,
title: "TestView",
description: "asd",
text: "asd",
category: "achievements",
created_at: "2019-01-18 12:06:40",
updated_at: "2019-01-18 12:06:40"
},
{
id: 1,
title: "Test",
description: "Test",
text: "Testr",
category: "news",
created_at: "2019-01-16 16:05:51",
updated_at: "2019-01-16 16:05:51"
},
{
id: 1,
title: "Test",
description: "Test",
text: "Test",
category: "livingspaces",
created_at: "2019-01-16 16:31:53",
updated_at: "2019-01-16 16:31:53"
},
{
id: 2,
title: "Test",
description: "Test",
text: "Test",
category: "livingspaces",
created_at: "2019-01-17 15:55:48",
updated_at: "2019-01-17 15:55:48"
},
{
id: 3,
title: "Test",
description: "asdsadsadsadsadsdsdsadasdasdasdsadasdadsadsadasdsasadasdsadsadasdsads",
text: "Tesst",
category: "achievement",
created_at: "2019-01-28 15:15:20",
updated_at: "2019-01-28 15:15:20"
},
{
id: 1,
title: "Test",
description: "Test",
text: "Test",
category: "events",
created_at: "2019-01-16 16:43:05",
updated_at: "2019-01-16 16:43:05"
},
{
id: 1,
title: "Test",
description: "Test",
text: "Test",
category: "therapien",
created_at: "2019-01-16 16:42:35",
updated_at: "2019-01-16 16:42:35"
},
{
id: 2,
title: "TestFinal",
description: "Test",
text: "Test",
category: "therapies",
created_at: "2019-01-18 12:20:17",
updated_at: "2019-01-18 12:20:17"
}
]
}
Could you give me a hint or a little example on how to do something like that?

You need to bring your app.js code in the blade file otherwise you can't access the data fields.
Once you get your app.js code, create a data field sCat: ''. Then in your card-component use an if statement to see if the current category is equal to the sCat or sCat is equal to null. For ex:
<card-component title={{$data->title}} description={{$data->description}} category={{$data->category}} v-if="(sCat == {{$data->category}} || sCat === '')"></card-component>
A better approach would be to create a master component and put everything you have under the id="app" div, in the master component. This way you will have better control of your vue codes.
EDIT
Approach 1:
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
#include('includes.header')
<body>
<div id="app" class="wrapper">
<nav-component></nav-component>
<header-component></header-component>
<header-sidebar-component></header-sidebar-component>
<div class = "container">
<div class="row">
#foreach ($sortedData as $data)
<card-component title={{$data->title}} description={{$data->description}} category={{$data->category}}></card-component v-if="(sCat == {{$data->category}} || sCat === '')">
#endforeach
</div>
</div>
</div>
<script src="/js/app.js"></script>
//Move your vue instance from app.js to here as below
<script>
new Vue({
el: "#app",
data: {
sCat: '',
...
}
....
});
</script
</body>
</html>
Approach 2 (recommended):
welcome.blade.php
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
#include('includes.header')
<body>
<div id="app" class="wrapper">
<master card-items="{{ $data }}">
</div>
<script src="/js/app.js"></script>
</body>
</html>
Master.vue
template
<nav-component></nav-component>
<header-component></header-component>
<header-sidebar-component></header-sidebar-component>
<div class = "container">
<div class="row">
<div class="col-x-y" v-for="item in items"> //add some column values if needed or use plain div
<card-component :title="item->title" :description="item->description" :category="item->category" v-show="(sCat === item->category || sCat === '')"></card-component>
</div>
</div>
script
import NavComponent from 'pathToNavComponent.js';
//import other components
props: {
items: Array
}
components: {
NavComponent,
... //other components imported
}
data: {
sCat: '',
...
}
...

Related

vue multiselect : remove group label if empty

Such as the print screen, i want to remove intended group label if all childs are selected
In the screenshot, "Liège (Province)" is a part of the group label (Province).
If i select it, it disappears from the group but the group label remains displayed.
How can I hide or remove the group label or the LI tag if all the child elements have been tagged?
<multiselect
v-model="values"
:options="options"
track-by="label"
label="value"
ref="multiselect"
group-values="datas"
group-label="type"
:custom-label="styleOption"
:placeholder="$trans('common.search.search_placeholder')"
open-direction="bottom"
:multiple="true"
:searchable="true"
:internal-search="false"
:clear-on-select="true"
:close-on-select="false"
:options-limit="10"
:limit="5"
:limit-text="limitText"
:max-height="400"
:show-no-results="false"
:show-no-options="false"
:option-height="0"
:hide-selected="true"
#search-change="getCities"
#close="closeEventHandler">
<template slot="tag" slot-scope="{ option, remove }">
<div class="custom_tag" #click="remove(option)">
<span class="tag">{{ option.value }}</span>
<div class="tag_close">
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" class="tag-close-icon"><path d="M18.3 5.71a.996.996 0 0 0-1.41 0L12 10.59 7.11 5.7A.996.996 0 1 0 5.7 7.11L10.59 12 5.7 16.89a.996.996 0 1 0 1.41 1.41L12 13.41l4.89 4.89a.996.996 0 1 0 1.41-1.41L13.41 12l4.89-4.89c.38-.38.38-1.02 0-1.4z"></path></svg>
</div>
</div>
</template>
<template slot="option" slot-scope="props">
<span v-if="props.option.$isLabel">{{ props.option.$groupLabel }}</span>
<span v-else>{{ props.option.label }}</span>
</template>
</multiselect>
EDIT: I think we should put a condition "!props.option.length" in the "v-if" like snippet below but it doesn't work.
<template slot="option" slot-scope="props">
<span v-if="props.option.$isLabel && !props.option.length">{{ props.option.$groupLabel }}</span>
<span v-else>{{ props.option.label }}</span>
</template>
You just need a computed property for the option. It will hold the groups which are not included. Here is a working demo that has a filteredOptions property
var app = new Vue({
el: '#app',
components: { Multiselect: window.VueMultiselect.default },
data () {
return {
options: [
{
language: 'Javascript',
libs: [
{ name: 'Vue.js', category: 'Front-end' },
{ name: 'Adonis', category: 'Backend' }
]
},
{
language: 'Ruby',
libs: [
{ name: 'Rails', category: 'Backend' },
{ name: 'Sinatra', category: 'Backend' }
]
},
{
language: 'Other',
libs: [
{ name: 'Laravel', category: 'Backend' },
{ name: 'Phoenix', category: 'Backend' }
]
}
],
value: []
}
},
computed:{
filteredOptions(){
return this.options.filter(el => el.libs.some(r=> !this.value.includes(r)))
}
}
})
body { font-family: 'Arial' }
<!DOCTYPE HTML>
<html>
<head>
<title>Timeline</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-multiselect#2.1.0"></script>
<link rel="stylesheet" href="https://unpkg.com/vue-multiselect#2.1.0/dist/vue-multiselect.min.css">
<script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
</head>
<body>
<div id="app">
<div>
<label class="typo__label">Groups</label>
<multiselect v-model="value" :options="filteredOptions" :multiple="true" group-values="libs" group-label="language" :group-select="false" :hide-selected="true" placeholder="Type to search" track-by="name" label="name"><span slot="noResult">Oops! No elements found. Consider changing the search query.</span></multiselect>
<pre class="language-json"><code>{{ value }}</code></pre>
</div>
</div>
</body>

How to isolate component in Vue.js

I have a problem at the beginning of my Vue education.
I made a small app. How can I isolate component?
After clicking on the arrow I'd like to open only one section of information.
Here's the code and live demo:
https://lemonwm.github.io/app_vue/
https://github.com/lemonWM/app_vue
If you plan to iterate through your recipes, you can add a property expanded: false for each recipe and toggle this one (instead of a global data property):
<div class="element-menu" v-for="recipe in recipes">
<div class="main-element">
<h2>{{ recipe.title }}</h2>
<div>
<button v-on:click='recipe.expanded = !recipe.expanded'><img src="img/arrow_06.png" alt=""></button>
</div>
</div>
<div class="desribe-element">
<div v-if='recipe.expanded'>
<div class="recipe-ingredience-left">
<h3>Składniki</h3>
<p v-for="ingredient in recipe.ingredients">{{ ingredient }}</p>
</div>
<div class="recipe-ingredience-right">
<h3>Przygotowanie</h3>
<p>{{ recipe.description }}</p>
</div>
</div>
</div>
</div>
data() {
return {
recipes: [
{
title: "Spaghetti",
ingredients: [
"Podwójna porcja makaronu",
"500g sera białego tłustego",
"2 łyżeczki soli (lub do smaku)",
"1/2 łyżeczki zmielonego pieprzu"
],
description: "Lorem...",
expanded: false //<-- here
},
{
title: "Carbonara",
ingredients: [
"...",
],
description: "..",
expanded: false,
},
]
}
}

How could I use materialize CSS CDN in nuxt.js?

I want to use the 'dropdown' css in my nav bar, so I create a component;
Here is the header component:
<template>
<no-ssr>
<div>
<ul id='dropdown' class='dropdown-content'>
<li>one</li>
<li>two</li>
<li class="divider" tabindex="-1"></li>
<li>three</li>
<li><i class="material-icons">view_module</i>four</li>
<li><i class="material-icons">cloud</i>five</li>
</ul>
<div class="navbar-fixed">
<nav>
<div class="nav-wrapper">
<div class="container">
<i class="material-icons right">home</i>Home
<ul class="right hide-on-med-and-down">
<li><a class="dropdown-button" data-activates="dropdown" data-beloworigin="true" data-hover="true">Dopdown<i
class="material-icons right">arrow_drop_down</i></a></li>
<li>About</li>
<li>Contact</li>
<li><i class="material-icons right">search</i>Search</li>
</ul>
</div>
</div>
</nav>
</div>
</div>
</no-ssr>
</template>
I didn't run npm install stuff... like that, I just import the materialize css through CDN;
Here is my nuxt.config.js:
head: {
title: pkg.name,
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: pkg.description }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' },
{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.min.css'}
],
script: [
{ src: 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js' },
{ src: 'https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js' }
]
},
When I run 'npm run dev' in localhost, The 'dropdown' didn't work;
When I move my mouse on the button, nothing happened, when I click the button, nothing happened either.
And I got the error by accident,
Could anybody help me solve this problem? I really appreciate it.
First you need a Button/Link which opens your first Dropdown. (Note the "data-target"-Attribute)
<!-- Dropdown Trigger -->
<a class='dropdown-trigger btn' href='#' data-target='dropdown1'>Drop Me!</a>
<!-- Dropdown Structure -->
<ul id='dropdown1' class='dropdown-content'>
<li>one</li>
<li>two</li>
<li class="divider" tabindex="-1"></li>
<li>three</li>
<li><i class="material-icons">view_module</i>four</li>
<li><i class="material-icons">cloud</i>five</li>
</ul>
Then you need to initialize this button:
$('.dropdown-trigger').dropdown();
See https://materializecss.com/dropdown.html
Finally, it works. I add the Initialization in mount() function,
Here is the code
mounted() {
$('.dropdown-button').dropdown({
inDuration: 300,
outDuration: 225,
constrainWidth: true, // Does not change width of dropdown to that of the activator
hover: true, // Activate on hover
gutter: 0, // Spacing from edge
belowOrigin: true, // Displays dropdown below the button
alignment: 'left', // Displays dropdown with edge aligned to the left of button
stopPropagation: false // Stops event propagation
}
);
}

Logo to the left, form to the right

I am using Bulma and Vue, and I am trying to create a header for the site that consists of a logo on the left and a login form on the right.
This gives me a logo on the left, and then from the end of the logo until the end of the screen on the right, I have the elements shown there.
How do I do what I want? Thanks.
Template
<header>
<div class="navbar">
<a class="navbar-brand" href="/">FreeSongs™</a>
<form class="navbar-menu" #submit.prevent="signin" accept-charset="utf-8" autocomplete="on">
<div class="field-body ">
<FormField type="email" required="required" :tabindex="1" placeholder="Email" name="login[email]" autocomplete="email" v-model="stageName" v-validate="'required'" autocapitalize="off" autofocus="autofocus"></FormField>
<FormField type="password" required="required" :tabindex="2" placeholder="Password" name="login[password]" autocomplete="current-password" v-model="email" v-validate="'required|email'"></FormField>
<button class="button is-success" tabindex="3" type="submit" id="signin">Sign in</button>
<a class="btn btn-link" tabindex="4" href="/forgot">Forgot password?</a>
</div>
</form>
</div>
</header>
FormField Component
<template>
<div class="field">
<label v-if="label" class="label" :for="id">{{label}}</label>
<input :type="type" class="input" :class="{'is-danger':this.$validator.errors.has(label)}" :tabindex="tabindex" :name="name" :id="id" :autocomplete="autocomplete" :value="value" #input="updateValue" #change="updateValue" #blur="$emit('blur')" :disabled="disabled" :required="required" :placeholder="placeholder" />
<span v-show="this.$validator.errors.has(label)" class="subtitle is-6 has-text-danger">{{ this.$parent.errors.first(label) }}</span>
</div>
</template>
<script>
export default {
name: "FormField",
//inject: ['$validator'],
inject: {
$validator: '$validator'
},
$_veeValidate: {
name() {
return this.label;
},
// fetch the current value from the innerValue defined in the component data.
value() {
return this.value;
}
},
props: {
value: String,
placeholder:String,
id: {
type: String,
default: () => {
const rand = Math.floor((Math.random() * 10000) + 1); //TODO: Create enough margin so there won't be a chance it has the same ID as other elemnts. Change the method?
const id = `undefined_${Date.now()*rand}`; //${this._uid}
return id;
}
},
label: {
type: String,
required: false
},
type: {
type: String,
default: "text"
},
name: {
type: String,
required: true
},
autocomplete: {
type: String,
required: false
},
disabled: {
type: Boolean,
default: false
},
required:{
type:Boolean,
default:false
},
tabindex:{
type:Number
},
autocapitalize:{
type:String,
},
autofocus:{
type:Boolean
}
},
computed: {
},
created: function() {
console.log("Created");
},
mounted: function() {
console.log("Mounted");
},
methods: {
updateValue(e) {
this.$emit("input", e.target.value);
}
}
};
</script>
The documentation outlines how to do this:
https://bulma.io/documentation/components/navbar/
First, the navbar is split into two.
|navbar-brand|navbar-menu|
navbar-brand will always show on the left, the navbar-menu fills the rest of the space on the right.
Inside the navbar-menu, you can specify which side items will show with two more elements.
|navbar-start|navbar-end|
<nav class="navbar">
<div class="navbar-brand">
This is on the left of the bar.
</div>
<div class="navbar-menu">
This spans the rest of the space on the right of the bar.
<div class="navbar-start">
This is on the left.
<div class="navbar-item">Your items on the left</div>
</div>
<div class="navbar-end">
This is on the right.
<div class="navbar-item">Your items on the right</div>
</div>
</div>
</nav>

To do List with Vue js 2 using component or v-model

Hello I have here one code with two "todo list" implementations in Vuejs but I have a problem.
1 Using a vue component i am getting a waring about how to use the parent variable.
2 Doing it on the main function I cannot keep the old value for the discard implementation.
please find the working code
Running! todo list in codepen
Vue.component('ntodo-item', {
template: '\
<transition name="fade">\
<div id="if" class="row" v-if="edit">\
<div class="col-md-7">\
<input class="form-control" v-model="title">\
</div>\
<div id="sssss" class="col-md-5">\
<button class="btn btn-danger roundButton" v-on:click="$emit(\'edit\')">Discard</button>\
<button class="btn btn-success roundButton" v-on:click="updateValue">Save</i></button>\
</div>\
</div>\
<div id="else" class="row" v-else>\
<div class="col-md-7">\
{{ title }}\
</div>\
<div id="ssaaas" class="col-md-5">\
<button class="btn btn-danger roundButton" v-on:click="$emit(\'remove\')">Remove</button>\
<button id="aaa" class="btn btn-default roundButton" v-on:click="$emit(\'edit\')">Edit</button>\
</div>\
</div>\
</transition>\
',
props: [
'title' ,
'edit'
],
methods: {
updateValue: function () {
this.$emit('input', this.title);
}
}
})
var app14 = new Vue({
el: '#app-14',
data: {
newTodoText: '',
newTodoText2: '',
todos: [
{
id: 1,
title: 'Do the dishes',
edit:0
},
{
id: 2,
title: 'Take out the trash',
edit:0
},
{
id: 3,
title: 'Mow the lawn',
edit:0
}
],
todos2: [
{
id: 1,
title: 'Do the dishes',
edit:0
},
{
id: 2,
title: 'Take out the trash',
edit:0
},
{
id: 3,
title: 'Mow the lawn',
edit:0
}
],
nextTodoId: 4,
nextTodoId2: 4
},
methods: {
addNewTodo: function () {
this.todos.push({
id: this.nextTodoId++,
title: this.newTodoText,
edit:0
})
this.newTodoText = ''
this.todos = _.orderBy(this.todos, 'id', 'desc');
},
editTodo: function (item){
// console.log(item.title)
item.edit^= 1
},
updateValue: function (item, newValue){
item.title=newValue
item.edit^= 1
},
addNewTodo2: function () {
this.todos2.push({
id: this.nextTodoId2++,
title: this.newTodoText2,
edit:0
})
this.newTodoText2 = ''
this.todos2 = _.orderBy(this.todos2, 'id', 'desc');
},
editTodo2: function (item){
console.log(item.title)
item.edit^= 1
},
deleteTodo2: function (item){
this.todos2.splice(item.id, 1);
},
updateValue2: function(text){
console.log(text);
}
}
})
.fade-enter-active, .fade-leave-active {
transition: opacity 0.3s, transform 0.3s;
transform-origin: left center;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
transform: scale(0.5);
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.js"></script>
<div class="col-md-12">
<div class="graybox">
<h5>app14</h5>
<div id="app-14">
`enter code here`<div class="row">
<div class="col-md-6">
<h5> todo list using "ntodo-item" component</h5>
<p>This one show me a warning because the child cannot edit the va passed by the parent but it is working and spected</p>
<input class="form-control"
v-model="newTodoText"
v-on:keyup.enter="addNewTodo"
placeholder="Add a todo"
>
<hr>
<ul>
<li
is="ntodo-item"
v-for="(todo, index) in todos"
v-bind:key="todo.id"
v-bind:title="todo.title"
v-bind:edit="todo.edit"
v-on:input="updateValue(todo, $event)"
v-on:remove="todos.splice(index, 1)"
v-on:edit="editTodo(todo)"
></li>
</ul>
</div>
<div class="col-md-6">
<h5> todo list update</h5>
<p> This one is working without any warn but I dont know how to discard changes. I dont want to create a temp var because I want to be able to edit all of them at the same time. </p>
<input v-model="newTodoText2"
v-on:keyup.enter="addNewTodo2"
placeholder="Add a todo"
class="form-control"
>
<hr>
<ul>
<transition-group name="fade" >
<li v-for="(todo2, index) in todos2":key="todo2.id">
<div id="if" class="row" v-if="todo2.edit">
<div class="col-md-7">
<input class="form-control" ref="todo2" v-model="todo2.title">
</div>
<div id="sssss" class="col-md-5">
<button class="btn btn-success roundButton" v-on:click="editTodo2(todo2)">ok</button>
</div>
</div>
<div id="else" class="row" v-else>
<div class="col-md-7">
{{todo2.title}}
</div>
<div id="ssaaas" class="col-md-5">
<button class="btn btn-danger roundButton" v-on:click="todos2.splice(index, 1)">Remove</button>
<button id="aaa" class="btn btn-default roundButton" v-on:click="editTodo2(todo2)">Edit</button>
</div>
</div>
</li>
</transition>
</ul>
</div>
</div>
</div>
</div>
</div>
.
Echoing my comment:
Create a local variable copy of your title prop and emit that variable's changes on edit. If they discard the edit just reset the local variable to the value of the title prop. Working example on CodeSandbox here.
Todo Item Component
<button class="btn btn-danger roundButton" #click="discardEdit">Discard</button>
...
data() {
return {
// our local copy
localTitle: null,
};
},
mounted() {
this.localTitle = this.title;
},
methods: {
updateValue: function() {
this.$emit("input", this.localTitle);
},
discardEdit: function() {
// just set local back to title prop value
this.localTitle = this.title;
this.$emit('edit');
},
}