I have created drag and elements with the help of cdk-angular (https://material.angular.io/cdk/drag-drop/overview) but the problem is I can only drag and drop the elements either horizontally or vertically.
Suppose I have an array like this.
completed = [{
name: 'HTML',
},
{
name: 'CSS',
},
{
name: 'Angular',
},
{
name: 'React',
},
{
name: 'Vue',
}
];
and in Html I am using it in the following way
<div class="list"
cdkDropList
#todoList="cdkDropList"
cdkDropListOrientation="horizontal"
[cdkDropListData]="completed"
[cdkDropListConnectedTo]="[doneList]"
(cdkDropListDropped)="onDrop($event)">
<ng-container *ngFor="let option of options;let i = index">
<div class="mat-card" cdkDrag>
<p>{{option.name}}</p>
</div>
</ng-container>
</div>
Here is the result
After adding cdkDropListOrientation="horizontal" I can drag horizontally, and when I remove it I can drag it vertically.
But when the options are big then I need to wrap it and then I need horizontal and vertical drag together. Can anyone put some lights on it. I'm stuck at this point.
Related
I know that "v-if" must avoid with "v-for" but not sure about "v-show" because it is just to toggle the display attribute.
This is the code in case anyone wants to know. Basically, I try to switch 3 different types of filter list. The code runs fine but I just wanna know if it should be avoid like "v-if".
<template>
<button
v-for="(filter, index) in filterList" :key="index"
#click="chosenFilter = filter.name"
>
{{ filter.name }}
</button>
<div
v-for="(filter, index) in filterList" :key="index"
v-show="chosenFilter === filter.name"
>
<div v-for="(listItem, index) in filter.list" :key="index">
{{ listItem }}
</div>
</div>
</template>
<script>
data () {
return {
filterList: [
{ name: 'Type 1', list: [] },
{ name: 'Type 2', list: [] },
{ name: 'Type 3', list: [] }
],
chosenFilter: 'Type 1'
}
}
</script>
From the official style guide: https://v2.vuejs.org/v2/style-guide/#Avoid-v-if-with-v-for-essential
There are 2 points in which it's not a good practice to use that, the 2nd one is interesting: To avoid rendering a list if it should be hidden. This one is basically fine since you're not doing heavy JS rendering, just basic CSS toggling.
So yeah, I'd say it's correct to have a v-show (and ESlint is not complaining btw).
But IMO, you can solve this kind of behavior in pretty much all cases with a computed: your filter button could be selected with an ID and your list rendering could be filtered with a filter here.
Replace #click="chosenFilter = filter.name" with #click="chooseFilter and get the ID (thanks to $event) of the item you've clicked on, then filter your list with the selected filter.
Scenario:
Displaying the expansion panel name and content Dynamically using Angular material expansion panel using JSON.
Issue:
I able to place the Panel name Dynamically but for the content, I need to check the JSON in that I have a type key so based on type I need to push that particular functionality that particular div.
JSON:
[{
module: "Person Details",
type: 'table',
moduleData: [
{
"firstName":"MaxRunner",
"ID":"12345",
}
]
},
{
module: "Current Location",
type: 'horizontal-stepper',
CurrentData: [
{
"step":1,
"description":"Philips",
"status":true
},
{
"step":2,
"description":"Philips",
"status":true
},
{
"step":3,
"description":"Philips",
"status":false
}
]
}
];
here based on type key and I am pushing the moduledata key to the div present what I did was like
I manually gave the key names but suppose in future in future if I have objects then I manually cannot set each name in div so is there any dynamically way to do this?
what I did
<div *ngIf="module.type==='table'">
<div *ngFor="let moduleKey of getKeys(module.moduleData[0])">
<div > {{ moduleKey }} </div>
<div> {{module.moduleData[0][moduleKey]}} </div>
</div>
</div>
<div style="border-bottom:0;" *ngIf="module.type==='horizontal-stepper'">
<div [ngClass]="'col-xs-3' + getStatusClass(step, index, module.CurrentData)" *ngFor="let step of module.CurrentData; let index = index">
<div><div class="progress-bar"></div></div>
<div >{{step.step}}</div>
<div class="bs-wizard-info text-center" *ngIf="isActive(step.status, index, module.CurrentData)">{{step.description}}</div>
</div>
</div>
IN the code currently, I am implementing a manual way like giving the key name "module.CurrentData"
here I don't want to give name manually like "getStatusClass(step, index, module.CurrentData)"
Here is my stackblitz.
Here is the updated (generic) version: StackBlitz
I have Updated the data to be more generic by adding data property instead of different property name for different types.
Here is the data look like now:
this.data = [{
module: "Person Details",
type: 'table',
data: [
{
"firstName":"MaxRunner",
"ID":"12345",
}
]
},
{
module: "Current Location",
type: 'horizontal-stepper',
data: [
{
"step":1,
"description":"Philips",
"status":true
},
{
"step":2,
"description":"Philips",
"status":true
},
{
"step":3,
"description":"Philips",
"status":false
}
]
}
];
I have updated the template code and now you only need to pass data instead of different names for different types.
<mat-accordion>
<mat-expansion-panel *ngFor="let module of data">
<mat-expansion-panel-header>
{{module.module}}
</mat-expansion-panel-header>
<div *ngIf="module.type==='table'">
<div *ngFor="let moduleKey of getKeys(module.data[0])">
<div > {{ moduleKey }} </div>
<div> {{module.data[0][moduleKey]}} </div>
</div>
</div>
<div style="border-bottom:0;" *ngIf="module.type==='horizontal-stepper'">
<div [ngClass]="'col-xs-3' + getStatusClass(step, index, module.data)" *ngFor="let step of module.data; let index = index">
<div><div class="progress-bar"></div></div>
<div >{{step.step}}</div>
<div class="bs-wizard-info text-center" *ngIf="isActive(step.status, index, module.data)">{{step.description}}</div>
</div>
</div>
</mat-expansion-panel>
I am not sure this will help you. But in angular, there is a way to iterate over the key-value pairs using keyValuePipe. With this, you will get the key without hardcoding. For example,
<ng-container *ngFor="let module of modules">
<div *ngFor="let moduleKey of module|keyvalue">
<!--you can have dynamic key such as moduledata|currentData etc-->
</div>
</ng-container>
I have a list of objects that are being shown in a v-for loop. They all have a certain key value pair, and based upon that value I'd like for the user to be able to toggle a button outside the loop structure to either show or hide those elements. Basically I want all of the items to be shown at first, and then once the button is toggled, only the items with the true value to be shown, then back to all items, etc.
Something like
const items = [
{
exampleKey: false
},
{
exampleKey: true
},
{
exampleKey: false
}
]
<button #click="updateList">click to update list</div>
<div v-for="items in itemList">item example</div>
methods: {
updateList: function(){
// make the magic happen
}
}
Of course this is just some pseudo code but it illustrates what I'm trying to get at. I am looking for some type of method or computed property that will let the user toggle the items visibility.
So you shouldn't combine v-for and v-if on the same element. What you can do is either include a filter in your v-for:
<div v-for="item in items.filter(i => i.exampleKey)">{{item.foo}}</div>
Or (my preference) you can iterate items as normal to create container divs, and add child content only where the desired condition is satisfied:
<div v-for="item in items">
<div v-if="item.exampleKey">{{item.foo}}</div>
</div>
If you want to add a control to hide/show items with an exampleKey of false, you can change your loop to:
<div v-for="item in items">
<div v-if="item.exampleKey || showItemsWithFalseExampleKey">{{item.foo}}</div>
</div>
And you can create a data property "showItemsWithFalseExampleKey" that is toggled by a button:
<button #click="showItemsWithFalseExampleKey = !showItemsWithFalseExampleKey">Toggle hidden items</button>
Of course, render cost for v-if is a lot higher than using v-show, so choose which is better based on your situation: https://v2.vuejs.org/v2/guide/conditional.html#v-if-vs-v-show
I would make a data property to toggle as true/false, when the button is clicked and have a computed property return the items based on that property.
Something like this:
<button #click="showElements = !showElements">click me</button>
<div v-for="item in filteredItems">{{ item }}</div>
data() {
return {
items: [
{
exampleKey: false
},
{
exampleKey: true
},
{
exampleKey: false
}
],
showElements: true
};
},
computed: {
filteredItems() {
return showElements ? this.items : this.items.filter(item => item.exampleKey);
}
}
Example based on Aurelia doc.
Page code:
export class MyPage {
products = [
{ id: 0, name: 'Motherboard' },
{ id: 1, name: 'CPU' },
{ id: 2, name: 'Memory' },
];
selectedProduct = null;
}
Page HTML:
<label>
Select product:<br/>
<select value.bind="selectedProduct">
<option model.bind="null">Choose...</option>
<option repeat.for="product of products"
model.bind="product">
${product.id} - ${product.name}
</option>
</select>
</label>
<div if.bind="selectedProduct">
Selected product 1: ${selectedProduct.id} - ${selectedProduct.name}
<div if.bind="selectedProduct.id > 0">
Selected product 2: ${selectedProduct.id} - ${selectedProduct.name}
</div>
</div>
When selecting CPU, then selecting null value, then selecting Memory, Selected product 1 is updated correctly with a value from a select element, but Selected product 2 is stuck with a CPU value.
How to bind selected value correctly inside the inner div?
In my application, I want to be able to display a content of selected item. Depending on an item type, I have a several <div if.bind="item.type === N">...</div> elements in order to display different HTML for each type of an item.
Note: binding doesn't work with newest packages, but works fine when I assign specific versions to the following packages in my package.json:
"aurelia-templating": "1.4.2"
"aurelia-templating-binding": "1.3.0"
"aurelia-templating-resources": "1.4.0"
"aurelia-templating-router": "1.1.0"
I have this object structure:
lines: [{
order: '1',
text: ' blue'
},{
order: '2',
text: 'green'
},{
order: '3',
text: 'yellow'
}]
And this is rendered on the page like this:
Blue
Green
Yellow
I want reorder the elements (and the object) without drag-drop, but with button up and down. Like this:
Blue - [down]
Green [up, down]
Yellow [up]
Each bullet is a component. How can I achieve that?
Based on assumptions from the little information you provided, I gave it a go.
Read: Vuejs list caveats
From the documentation:
Due to limitations in JavaScript, Vue cannot detect the following changes to an array: When you directly set an item with the index, e.g. vm.items[indexOfItem] = newValue
So when you modify an array in Vue you should use one of the following:
// Vue.set
Vue.set(example1.items, indexOfItem, newValue)
// Array.prototype.splice
example1.items.splice(indexOfItem, 1, newValue)
Your view can look something like:
<div id="app">
<div v-for="(line, index) in lines">
<p style="display: inline-block">{{line.text}}</p>
<button #click="up(index)" v-if="index !== 0">UP</button>
<button #click="down(index)" v-if="index !== lines.length-1">DOWN</button>
</div>
</div>