I've searched Google for this but none of the "solutions" I've found work, and are from 2016. I had it working fine with 1.x, but now I am trying to follow the migration guide to update to 2.0.
I have a setup like this in the HTML with two Bootstrap cards containing lists:
<div class="row">
<div class="col">
<div class="card">
<div class="card-header">Fruits</div>
<div class="card-body">
<ul [dragula]="firstbag"
[(dragulaModel)]="produce.fruits"
class="drag-container">
<li *ngFor="let item of produce.fruits"
class="drag-item">{{item.label}}</li>
</ul>
</div>
</div>
</div>
<div class="col">
<div class="card">
<div class="card-header">Vegetables</div>
<div class="card-body">
<ul [dragula]="firstbag"
[(dragulaModel)]="produce.vegetables"
class="drag-container">
<li *ngFor="let item of produce.vegetables"
class="drag-item">{{item.label}}</li>
</ul>
</div>
</div>
</div>
</div>
However, when I try and compile this I am getting:
ERROR in : Can't bind to 'dragula' since it isn't a known property of 'ul'. (" class="card-header">Fruits</div>
<div class="card-body">
<ul [ERROR ->][dragula]="firstbag"
[(dragulaModel)]="produce.fruits"
")
: Can't bind to 'dragulaModel' since it isn't a known property of 'ul'. (" <div class="card-body">
<ul [dragula]="firstbag"
[ERROR ->][(dragulaModel)]="produce.fruits"
class="drag-container">
")
I have imported as describe in my app module:
import { DragulaModule } from 'ng2-dragula';
#NgModule({
imports: [
...,
DragulaModule.forRoot()
],
})
export class AppModule { }
The data for the lists is in my component:
produce = {
fruits: [
{
id: 0,
label: 'Squash'
},
{
id: 1,
label: 'Pineapples'
},
{
id: 2,
label: 'Raspberries'
}
],
vegetables: [
{
id: 3,
label: 'Carrots'
}
]
};
I have changed the syntax to [(dragulaModel)] in the html, as you can see above.
At this point, I'm lost.
Related
My question might not match to the problem that I am facing, So, I will explain it in detail here.
This is code for SeparateTechnology.vue. I have removed a few methods to make code shorter.
<template>
<div>
<div v-for="mss in ms" :key="mss.name">
<section class="container shadow-box techno-box" v-if="mss.name==selected">
<p>Review the software criteria below and ensure that you complete each section</p>
<h4>{{mss.name}}</h4>
<div class="row shadow-box">
<div class="col-sm-12 col-lg-6">
<p>Select all that apply</p>
<div class="dropdown">
<input
v-model.trim="inputValue"
class="dropdown-input"
type="text"
placeholder="Search for your Solution"
/>
<div class="dropdown-list" v-show="selected">
<div
v-show="itemVisible(item)"
v-for="item in mss.subMenu"
:key="item"
class="dropdown-item"
>
{{ item }}
<button
class="button button-mini button-dark button-rounded itemLabel"
#click="selectSolution(item,mss)"
>
<i class="icon-line-plus"></i>
</button>
</div>
</div>
</div>
</div>
<div class="col-sm-12 col-lg-6 rightList">
<div class="dropdown-list" v-show="selected" v-if="mss.selected_solutions!=''">
<div v-for="item in mss.selected_solutions" :key="item" class="dropdown-item">
{{ item }}
<button
class="button button-mini button-dark button-rounded deleteLabel"
#click="deleteSelectedItem(item,mss)"
>
<i class="icon-line-minus"></i>
</button>
</div>
</div>
<div v-else>
<div class="style-msg errormsg">
<div class="sb-msg">
<i class="icon-remove"></i>You have not selected any solutions.
</div>
</div>
</div>
<button
class="button button-mini"
#click="clearUserOptions(mss)"
v-if="mss.selected_solutions.length > 1"
>
<i class="icon-line-cross"></i>Clear all selection
</button>
</div>
<div style="padding:20px;"></div>
</div>
<div class="row">
<div class="col-sm-12 col-md-3 inputTitle">
<h5>Don't see it in the list above:</h5>
</div>
<input
class="col-sm-12 col-md-6"
type="text"
v-model="value"
#keydown.enter="getUserSolution(value,mss)"
placeholder="Enter your solution here.. "
/>
</div>
<div style="padding:20px;"></div>
<div class="row shadow-box">
<h5
class="col-sm-12"
>Identify how the software solution is leveraged within your organization</h5>
<div
v-for="item in mss.selected_solutions"
:key="item"
class="clearfix col-sm-12 col-md-6"
style="padding:20px"
>
<span v-if="mss.isDifferent=='campaign'">
<div class="card">
<h5 class="card-header">{{item}}</h5>
<CheckBox
:groups="campaignMangment"
name="campaignMangment"
:value="item"
classProp="col-sm-12"
#clicked-show-detail="clickedShowDetailModal"
/>
{{item}} and {{productSelected}}
</div>
</span>
</div>
</div>
<button class="btn btn-primary" #click="postUserDetails(mss)">Submit</button>
</section>
</div>
</div>
</template>
<script>
import CheckBox from "../../../components/checkbox/Checkbox";
export default {
name: "technology",
data() {
return {
usageValue: "",
value: "",
campainCheckedNames: [],
checked: "",
productSelected: [],
checkedValues: "",
exists: null,
inputValue: "",
campaignMangment: [
"Business to Customer (B2C)",
"Business to Business (B2B)",
"Customer to Customer (C2C)",
"Customer to Business (C2B)",
"E-Government",
"M-Commerce",
],
};
},
props: ["ms", "selected"],
//method to show all the solutions that contains the user
methods: {
clickedShowDetailModal: function (campainCheckedNames) {
console.log(campainCheckedNames);
this.productSelected.push(campainCheckedNames);
},
},
components: {
CheckBox,
},
};
</script>
This is CheckBox.vue
<template>
<div class="row col-mb-0">
<div
:class="classProp + ' checkbox-margin'"
v-for="singleSelector in groups"
:key="singleSelector"
>
<div>
<input
:id="singleSelector+groupId"
:value="singleSelector"
class="checkbox-style"
type="checkbox"
v-model="checkedValues"
#change="showDetailModal"
/>
<label :for="singleSelector +groupId" class="checkbox-style-3-label">{{singleSelector}}</label>
</div>
</div>
</div>
</template>
<script>
let groupId = 0;
export default {
props: {
groups: Array,
name: String,
classProp: String,
value: String,
},
data() {
return {
groupId: groupId++,
checkedValues: [],
inputs: {},
};
},
methods: {
showDetailModal: function (e) {
this.$set(this.inputs, this.value, e.target.value);
this.$emit("clicked-show-detail", this.inputs);
},
},
};
</script>
<style scoped>
.checkbox-margin {
margin-bottom: 10px;
}
</style>
Right Now, the line {{item}} and {{productSelected}} prints output like below as shown in screenshot
.
Problem: On every click/unclick of checkbox it adds an item to an array and not in the format I want. and if I select the checkbox on left only, it adds that item on right as well as shown in the screenshot above. It is due to the same array declaration, but I couldn't think more than that.
Expected Output: For every selected item, I want to print the list of selected checkboxes in an array format like below.
"selected_solutions": [{
"name": "Adobe Campaign",
"usage": [ "Business to Customer",...]
}, {
"name": "Marin Software",
"usage": ["M-Commerce",...]
}]
Even small hints or tips would be more than appreciated. I don't mind posting code on gist if required. Thank you.
With checkbox-components you want to emit "event.target.checked" likeso:
this.$emit('input', event.target.checked)
That way it'll behave like a checkbox and you can use a v-model on the parent:
<CheckBox v-model="item.checked" ...>
I do not use v-model inside the component and instead just bind :checked="value", but that might be up to preference. But using both :value and v-model could (should?) cause problems because v-model is actually a :value + emitter under the hood (or so I heard).
Anyway, here is my minimal reusable checkbox-wrapper:
<template>
<input type="checkbox" :checked="value" #change="handleChange" />
</template>
<script>
import Vue from 'vue'
export default Vue.extend({
name: 'MyCheckbox',
props: {
value: { type: Boolean, default: false }
},
methods: {
handleChange(event) {
this.$emit('input', event.target.checked)
}
}
})
</script>
After that is done you can just filter on checked items:
computed: {
checkedSolutions(){
return this.ms[0].selected_solutions
.filter( solution => solution.checked );
}
}
I have written the following code:
<div>
<div id="app" class="container">
<div class="grid">
<article v-for="tool in tools">
<div class="title">
<h3>{{capitalizeFirstLetter(tool.name)}}</h3>
</div>
<div class="description">
{{tool.description}}
</div>
<br>
<div>
<toggle-button :value=tool.status color="#82C7EB" :width=100 :height=30 :sync="true" :labels="{checked: 'Following', unchecked: 'Unfollowing'}" #change="onChange(tool)"/>
</div>
</article>
</div>
</div>
</div>
Recently I started using Bootstrap-Vue. I'm trying to figure out how to add pagination on the bottom.
I'm not sure how aria-controls works. My goal is to have 9 blocks of tools on each page. How should I add the pagination so I could move to the next 9 blocks of tools?
Since it wasn't clear if you needed client-side pagination or serverside i made an example of both in the snippet.
For simplicity I've made it 3 per page, but you could change it to 9.
The first one gets the initial page on load, and then calls the API every time the page changes by using a watcher, that calls a method with the new page which then retrieves that place and replaces our old data with the new.
The second one loads all the data on page load, and instead slices the data array based on the per_page property, so that only the items for that page is shown.
For this I've used a computed property which automatically updates based on the properties used inside it.
Both paginations have aria-controls defined with the id of the container of our page elements. This is used to tell screen readers what elements the pagination changes.
The classes row, col-*, border, mx-auto, h2 and text-center is classes used for styling and layout and isn't part of the actual solution, so you can freely change or remove them.
new Vue({
el: '#app',
computed: {
pagination2CurrentItems() {
const startIndex = (this.pagination2.current_page - 1) * this.pagination2.per_page;
const endIndex = startIndex + this.pagination2.per_page;
return this.pagination2.items.slice(startIndex, endIndex)
}
},
created() {
this.getPagination1Data()
this.getPagination2Data()
},
filters: {
capitalizeFirstLetter(value) {
return value.charAt(0).toUpperCase() + value.slice(1)
}
},
data() {
return {
pagination1: {
items: [],
per_page: 3,
total_rows: 0,
current_page: 1
},
pagination2: {
items: [],
per_page: 3,
total_rows: 0,
current_page: 1
}
}
},
methods: {
getPagination1Data(page = 1) {
fetch(`https://reqres.in/api/unknown?page=${page}&per_page=3`)
.then((response) => {
return response.json();
})
.then((data) => {
this.pagination1.total_rows = data.total;
this.pagination1.items = data.data;
});
},
getPagination2Data() {
/*
This endpoint only has 12 items total,
so this will get all in one call
*/
fetch(`https://reqres.in/api/unknown?per_page=12`)
.then((response) => {
return response.json();
})
.then((data) => {
this.pagination2.total_rows = data.total;
this.pagination2.items = data.data;
});
}
},
watch: {
'pagination1.current_page'(newPage) {
this.getPagination1Data(newPage)
}
}
})
<script src="https://unpkg.com/vue#2.6.11/dist/vue.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.5.0/dist/bootstrap-vue.js"></script>
<link href="https://unpkg.com/bootstrap#4.4.1/dist/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://unpkg.com/bootstrap-vue#2.5.0/dist/bootstrap-vue.css" rel="stylesheet"/>
<div id="app" class="container">
<div id="tools_list_1" class="row">
<div class="col-12 h2 text-center">
Server pagination
</div>
<article class="col-3 mx-auto border" v-for="tool in pagination1.items">
<div class="title">
<h3>{{ tool.name | capitalizeFirstLetter }}</h3>
</div>
<div class="description">
{{ tool.pantone_value }}
</div>
</article>
</div>
<div class="row mt-2">
<div class="col-12">
<b-pagination
v-model="pagination1.current_page"
:per-page="pagination1.per_page"
:total-rows="pagination1.total_rows"
aria-controls="tools_list_1"
>
</b-pagination>
</div>
</div>
<div id="tools_list_2" class="row">
<div class="col-12 h2 text-center">
Client pagination
</div>
<article class="col-3 mx-auto border" v-for="tool in pagination2CurrentItems">
<div class="title">
<h3>{{ tool.name | capitalizeFirstLetter }}</h3>
</div>
<div class="description">
{{ tool.pantone_value }}
</div>
</article>
</div>
<div class="row mt-2">
<div class="col-12">
<b-pagination
v-model="pagination2.current_page"
:per-page="pagination2.per_page"
:total-rows="pagination2.total_rows"
aria-controls="tools_list_2"
>
</b-pagination>
</div>
</div>
</div>
If you are interested in server side pagination with url changes of query params (using vue-router in history mode) and you need browser history (back/forward) to work properly, you can check my answer to a similar question. I think it can be adapted to not use b-table.
Place pagination number in url Vuejs
same as title says. A computed property from a object it´s not reactive until the input box lost the focus.
But if the object is in data, the value changes as soon as changed in the input.
I created a codepen with the example: https://codepen.io/Albvadi/pen/QWwMOQV
<div id="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Match Component</div>
<div class="card-body">
<div class="container">
<div class="row align-items-center">
<div class="col-4 text-right">{{homeTeam.desc}}</div>
<div class="col-2">
<input class="form-control text-center" type="number" min="0" v-model.number="homeTeam.score" />
</div>
<div class="col-2">
<input class="form-control text-center" type="number" min="0" v-model.number="awayTeam.score" />
</div>
<div class="col-4">{{ awayTeam.desc }}</div>
<div class="row">
<div class="col">
DATA:
<b>{{ homeTeam.score }}</b>
</div>
<div class="row">
<div class="col">
COMPUTED:
<b>{{ awayTeam.score }}</b>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
new Vue({
el: "#container",
data: {
homeTeam: {
name: "SPA",
desc: "Spain",
score: 0
}
},
computed: {
awayTeam: function() {
return {
name: "GER",
desc: "Germany",
score: 0
};
},
},
mounted() {
console.log("Component mounted.");
}
});
If you change the first input, the data value is changed inmediate.
If you change the second input, the computed value if changed only when input lost focus.
How can I solve this?
== EDIT ==
Thanks for your answers!
I know that putting awayteam in the data section solves the problem, it was just an example to simplify my problem.
My problem was that along with the two results, I wanted to have a computed property to be able to issue to another component.
I edited the codepen with the final result: https://codepen.io/Albvadi/pen/jOELYwN and it´s working correctly.
But #Sami Kuhmonen comments that computed properties are only read properties but in my checkFinalResult I´m setting the computedMatch..
It´s the right approach??
Computed properties should be considered read only. Every time there's a change in the properties they reference their value is computed from other values and all changes are lost (or at least should be thought that way, there may be cases where some values remain). Even though sometimes changing their properties seems to work it's not guaranteed and they may change whenever.
In the codepen you already have the computed property referencing other properties from the object, so you'll have to add all the things you want to change similarly and then change that value. This will cause the computed value to be re-evaluated and the changes will be visible and persisted.
Note that building a computed object from a lot of separate things might not be the most performant way and it depends on the situation how it should be handled. If the values are only used within the component then it's easy to just use them directly but if you need an object of specific form for something and need it reactive then computed property might be the way to go.
In your case, why this computed property does not update DOM element instantly,because it has no data reference with Vue instance.Here, it return some static value only,which was not the computed property's purpose. computed property came to purpose,when you need to calculate or compute some decision on the basis of your data property of your vue instance.
new Vue({
el: "#container",
data: {
homeTeam: {
name: "SPA",
desc: "Spain",
score: 0
},
awayTeam: {
name: "GER",
desc: "Spain",
score: 0
},
},
mounted() {
console.log("Component mounted.");
}
});
<div id="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Match Component</div>
<div class="card-body">
<div class="container">
<div class="row align-items-center">
<div class="col-4 text-right">{{homeTeam.desc}}</div>
<div class="col-2">
<input class="form-control text-center" type="number" min="0" v-model.number="homeTeam.score" />
</div>
<div class="col-2">
<input class="form-control text-center" type="number" min="0" v-model.number="awayTeam.score" />
</div>
<div class="col-4">{{ awayTeam.desc }}</div>
<div class="row">
<div class="col">
DATA:
<b>{{ homeTeam.score }}</b>
</div>
<div class="row">
<div class="col">
COMPUTED:
<b>{{ awayTeam.score }}</b>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
Why do you use a computed property for this task? I would simply do it this way.
new Vue({
el: "#container",
data: {
homeTeam: {
name: "SPA",
desc: "Spain",
score: 0
},
awayTeam: {
name: "GER",
desc: "Germany",
score: 0
},
},
mounted() {
console.log("Component mounted.");
}
});
I just writed the vue simple code, But unable to follow the HTML effect. After traversal rendering a bit wrong. If gift object is no, for example the goods object has two data, goods_b1 + goods_b2. But i want to follow the HTML effect. Go to the HTML still. And go to the vue loops.
I want to the this effect:
Look at the javascript:
var app = new Vue({
el: "#app",
data: {
list: [{
id: 1,
name: 'A',
goods: [{
name: "goods_a1"
}],
gift: [{
name: "gift_a1",
}]
}, {
id: 2,
name: 'B',
gift: [],
goods: [{
name: "goods_b1"
}, {
name: "goods_b2"
}],
}, {
id: 3,
name: 'C',
goods: [{
name: "goods_c1"
}, {
name: "goods_c2"
}, {
name: "goods_c3"
}],
gift: [{
name: "gift_c1",
}]
}]
}
})
HTML:
<div id="app">
<div class="mui-row" v-for="item in list">
<div class="span-title-main">
<span class="span-title">{{item.name}}</span>
</div>
<br>
<ul>
<li>
<div class="mui-col" v-for="items in item.goods">
<span class="span-name">{{items.name}}</span>
</div>
<div class="addspan">+</div>
<div class="mui-col" v-for="itemss in item.gift">
<span class="span-name">{{itemss.name}}</span>
</div>
<div class="addspan">+</div>
</li>
</ul>
</div>
</div>
Are you asking that the (+) being inside the loop of your goods and gift ?
<div id="app">
<div class="mui-row" v-for="item in list">
<div class="span-title-main">
<span class="span-title">{{item.name}}</span>
</div>
<br>
<ul>
<li>
<div class="mui-col" v-for="items in item.goods">
<span class="span-name">{{items.name}}</span>
<div class="addspan">+</div>
</div>
<div class="mui-col" v-for="itemss in item.gift">
<span class="span-name">{{itemss.name}}</span>
</div>
</li>
</ul>
</div>
</div>
Edit: Remove the (+) for gifts loop as requested by OP.
Note: if the OP is asking to have a style for element in between goods and gift. I would suggest to use the css :last selector with a display:none to have this kind of effect.
It looks like the only difference is that you want a + button to appear after each item.goods instead of just one after the loop.
So put it inside the loop:
<template v-for="items in item.goods"><!-- using "template" to avoid modifying your html structure; you could of course use any tag -->
<div class="mui-col">
<span class="span-name">{{items.name}}</span>
</div>
<div class="addspan">+</div>
</template>
<div class="mui-col" v-for="items in item.gift">
<span class="span-name">{{items.name}}</span>
</div>
<!-- your image doesn't show a + button after gifts, so I've omitted it here -->
In my angular js controller, I have a json array that contains continents and inside the continents contain an array of countries
//CONTROLLER
app.controller('CountryController', function(){
this.continents =
[
{
name:'Africa',
countries:
[
{ name: 'Algeria', code:'DZ', operators:'3'},
{ name: 'Angola', code:'AO', operators:'1'},
{ name: 'Benin', code:'BJ', operators:'2'},
{ name: 'Burkina Faso', code:'BF', operators:'1'}
],
},
{
name:'AsiaPacific',
countries:
[
{ name: 'Afghanistan', code:'AF', operators:'4'},
{ name: 'Bahrain', code:'BH', operators:'2'}
],
}
];
});
In my view i want to display the countries in tabbed elements arranged by continents i.e I have tabs to hold the countries in each continent. e.g i have a tab for Africa and inside this tab i want to display the countries in the Africa object.
I have tried using the 'filter' to display only countries in a particular continent in each tab but it is not working neither does anything show up. On inspecting the element the code seems to be commented. This is the first task I am trying to accomplish using Angular-js so I am still learning the ropes.
This is what my view looks like
<div ng-app="countriessupported" class="container container_with_height">
<div ng-controller="CountryController as countryCtrl" id="myTabContent" class="tab-content hidden-xs">
<div class="tab-pane fade active in" id="africa">
<div class="col-md-12 countries_col-12" ng-repeat="continent in countryCtrl.continents.name | filter: 'Africa' ">
<a href="">
<div class="col-md-3 country_container" ng-repeat="country in continent.countries">
<div class="country_name">
{{ country.name }}
</div>
</div>
</a>
</div>
</div>
</div>
</div>
So the idea is to have the countries repeated in the div with class col-md-3
How can i achieve this please?
Try this html Code
<div ng-app="countriessupported" class="container container_with_height">
<div ng-controller="CountryController as countryCtrl" id="myTabContent" class="tab-content hidden-xs">
<div class="tab-pane fade active in" id="africa">
<div class="col-md-12 countries_col-12" ng-repeat="continent in countryCtrl.continents | filter: 'Africa' ">
<a href="">
<div class="col-md-3 country_container" ng-repeat="country in continent.countries">
<div class="country_name">{{ country.name }}</div>
</div>
</a>
</div>
</div>
</div>
</div>
let me know if this is helpful
Try to change you first repeater to this ng-repeat="continent in countryCtrl.continents instead of ng-repeat="continent in countryCtrl.continents.name the filter sholud be working anyway