Angular Gridster-2 Redraggable and Resize - angular8

I am new to angular gridster 2
Could you please let me know if we are able to display the already developed charts using angular gridster 2 , I am also looking to see the same approach . As my UI has 5 different div blocks and inside there is a card being displayed in each div block ..
How can I use angular gridster to disaply these 5 cards with draggable and resizable features after loading the page..
<gridster [options]="options">
<div class="col-md-12 ml-auto mr-auto" >
<div class="row" > <!--[item]="item" *ngFor="let item of dashboard" gridster-item-->
<gridster-item class="col-lg-3 col-md-6 col-sm-6" [item]="item" *ngFor="let item of dashboard" >
<card1></card1>
<card2></card2>
</gridster-item>
</gridster>
export class dashboard implements oninit{
options={
draggable: {
enabled: true},
resizable: {
enabled: true
}
};
dashboard = [
{cols: 1, rows: 1, y: 0, x: 0}
];
}
Please explain what does the rows,columns, x and y represents .. How does it effect the UI if these values are changed..

Cols and Rows are your gridster layout design to allow gridster-items. If cols = 1 and rows = 1, you can put one gridster item only except maxItemRows and maxItemCols set greater than 1. The y and x are col and row index for gridster-item.
Example, cols = 5, rows = 5, and x = 3, y = 2, gridster-item will display start from column 3 and row 2.
Here are some articles about gridster.
https://medium.com/javascript-in-plain-english/drag-and-drop-dashboard-builder-with-angular-and-gridster-a07592e54ce2
https://developer.aliyun.com/mirror/npm/package/helio-angular-gridster

Related

Dynamically Tally Child Elements by Classname in Vue

I have a page that allows a user to drag/drop images into pre-defined DIVs, then I tally up the total value of the images based on their class name. What I am trying to do is get vue to read the values from each outer div.answer and get the class names of the child images.
My source code is:
<div
is="box-answers"
v-for="box in boxes.slice().reverse()"
v-bind:key="box.id"
v-bind:level="box.level"
v-bind:hint="box.hint"
></div>
<script>
Vue.component('box-answers', {
props: ['level','hint'],
template: '<div class="droppable answer :id="level" :title="hint"></div>'
});
new Vue({
el: '#mainapp',
data: {
boxes: [
{ id: 1, level: 'baselevel-1', hint: 'x 1' },
{ id: 2, level: 'baselevel-2', hint: 'x 20' },
{ id: 3, level: 'baselevel-3', hint: 'x 400' },
{ id: 4, level: 'baselevel-4', hint: 'x 8,000' },
{ id: 5, level: 'baselevel-5', hint: 'x 160,000' }
]
}
</script>
This converts to the follow HTML (the nested DIVs and SPANs are user-possible entries by dragging):
<div id="baselevel-5" class="droppable answer" title="x 160,000">
<div><img src="images/line.gif" alt="Five" class="imgfive"></div>
<span><img src="images/dot.gif" alt="One" class="imgone"></span>
</div>
...
<div id="baselevel-1" class="droppable answer" title="x 1">
<span><img src="images/line.gif" alt="One" class="imgone"></span>
</div>
Currently, I have jQuery/JavaScript calculating the point values using the following:
$(function(j) {
var arAnswers = Array(1);
count = 0; //
j("div.answer").each(function( idx ) {
currentId = j(this).attr('id');
ones = 0;
fives = 0;
if ( j("#" + currentId).children().length > 0 ) {
ones = j("#" + currentId).children().find("img.imgone").length * 1;
fives = j("#" + currentId).children().find("img.imgfive").length * 5;
arAnswers[count] = ones + fives; //Tally box value
count++;
}
});
});
I would like Vue to perform similar iteration and addition to return total value of ones and fives found based on the image classname.
Currently, you are approaching this problem as a pure-play DOM operation. If that is what you need then you can simply use $refs:
<!-- NOTICE ref -->
<div ref="boxAnswers"
is="box-answers"
v-for="box in boxes.slice().reverse()"
v-bind:key="box.id"
v-bind:level="box.level"
v-bind:hint="box.hint">
</div>
Inside your high-level component, you will have a function like:
function calculate() {
// NOTICE $refs
const arAnswers = this.$refs.boxAnswers.map((x) => {
// $el is the DOM element
const once = x.$el.querySelectorAll('img.imgone').length * 1;
const fives = x.$el.querySelectorAll('img.imgfive').length * 5;
return once + fives
});
return arAnswers;
}
But this is not the correct Vue way of doing things. You have to think in terms of events and data model (MVVM - don't touch DOM. DOM is just a representation of your data model). Since, you have a drag-n-drop based application, you have to listen for drag, dragstart, dragend and other drag events. For example:
<!-- NOTICE drop event -->
<div #drop="onDropEnd(box, $event)"
is="box-answers"
v-for="box in boxes.slice().reverse()"
v-bind:key="box.id"
v-bind:level="box.level"
v-bind:hint="box.hint">
</div>
Your onDropEnd event handler will look like:
function onDrop(box, $event) {
// box - on which box drop is happening
// $event.data - which image is being dropped
// Verify $event.data is actually the image you are intending
if ($event.data === 'some-type-image') {
// Do the counting manipulations here
// ... remaining code
}
}
This is not a complete code as I don't know other components. But it should help you with the required direction.

Setting tabIndex nicely in VueJS when iterating over multiple objects

I have a couple of nested objects I need to iterate over to create inputs. I've dumbed it down to an example below. I would like to set tabIndexes in these inputs.
<div v-for="(iv, ik, ii) in {a: 'x', b: 'x'}" :key="ii">
<div v-for="(jv, jk, ji) in {a: 'y', b: 'y'}" :key="ji">
<div v-for="(kv, kk, ki) in {a: 'z', b: 'z', c: 'z'}" :key="ki">
<input type="text" :tabindex="(ii * 100) + (ji * 10) + ki" />
<label>{{(ii * 100) + (ji * 10) + ki}}</label>
</div>
</div>
</div>
What is the best way to be able to set my tabindex to 0, 1, 2, 3, etc? I found that setting a third arg on the v-for provides a numeric index, but what I've got seems a little convoluted. Is there a better way I can go about this?
The above results in output like the following:
[___________] 0
[___________] 1
[___________] 2
[___________] 10
[___________] 11
[___________] 12
[___________] 100
[___________] 101
[___________] 102
[___________] 110
[___________] 111
[___________] 112
Which works, but seems less than ideal. I know the tab key will work as intended if they're sequential and that gaps seem fine. But is there a cleaner way I could get 1 through 12 instead of the staggered numbers I've got? Basically like a running index (x++, etc) for each time I hit the ?
I tried setting an int in the 'data' and then a method to increment that, but quickly threw myself into an infinite re-rendering loop.
Thanks.
there is a trick to doing this, but it's a hack, and not exactly a best practice
Template:
{{numitems = 0 | hide}}
<div v-for="(iv, ik, ii) in {a: 'x', b: 'x'}" :key="ii">
<div v-for="(jv, jk, ji) in {a: 'y', b: 'y'}" :key="ji">
<div v-for="(kv, kk, ki) in {a: 'z', b: 'z', c: 'z'}" :key="ki">
<input type="text" :tabindex="numitems += 1" />
<label>{{(ii * 100) + (ji * 10) + ki}}</label>
</div>
</div>
</div>
script hide Filter definition
filters: {
hide: function(value){
return ''
}
}
you don't need the hide filter, but it ensures that you don't put anything into the template during definition. You can also define numitems in data. And use methods to reset and increment.
another option is setting up a computed value that uses the number you generate as an index to the incremental values without any gaps.
Whenever your objects' keys change, you can do the calculation, generating the keys either using your method, or using an object.
here's an example that uses ${ik}_${jk}_${kk} as the key
tabIndexVals(): {
let c = 0;
let o = {};
Object.keys(i).forEach(ik => {
Object.keys(i[ik]).forEach(jk => {
Object.keys(i[ik][jk]).forEach(kk => {
let key = `${ik}_${jk}_${kk}`;
o[key] = c;
c++;
})
})
})
return o;
}

Unselect effect Radio Material2 Angular 5

I'm using mat-radio-button(material2) and I have something like a quiz. When I select an answer and go to the next quiz, the visual effect of unselect is showing in the second quiz. This happens if the selected option in the first quiz, appears in the second too, in the same position or after.
I made a simple example. You can reproduce it selecting "aa", then click ">>" button.
HTML
<mat-radio-group class="example-radio-group" [(ngModel)]="radioSelected">
<mat-radio-button *ngFor="let option of tempSublist" [value]="option" class="example-radio-button" color="primary">
{{option}}
</mat-radio-button>
</mat-radio-group>
<hr>
<button (click)="prev()"><<</button>
<button (click)="next()">>></button>
Typescript
list = [
['aa','bb','cc'],
['cc','aa','bb'],
['bb','cc','ee'],
['dd','bb','cc'],
];
index = 0;
tempSublist = this.list[this.index];
radioSelected:string;
next(){
if(this.index < this.list.length){
this.radioSelected = null;
this.index++;
this.tempSublist = this.list[this.index];
}
}
prev(){
if(this.index >= 1){
this.radioSelected = null;
this.index--;
this.tempSublist = this.list[this.index];
}
}
https://stackblitz.com/edit/angular-7s9qvs?file=app%2Fradio-ng-model-example.html
How can I solve this?
Adding trackBy option on ngFor helps to reduce the transition between the two states. Also disabling repples with [disableRipple]="true" seems to reduce the visual effect:
HTML
<mat-radio-button [disableRipple]="true" *ngFor="let option of tempSublist;
trackBy: trackByFn" ...>
...
Typescript
next() {
if (this.index < this.list.length - 1) {
//add this ^
...
trackByFn(index, item) {
return index;
}
I thought adding a control on navigation buttons would be nice:
<button [disabled]="index <= 0" (click)="prev()"><<</button>
^- this
<button [disabled]="index === tempSublist.length" (click)="next()">>></button>
^- this
Demo
If this is not enough, in last recourse, hide and show the whole block.
Wrap it in a div and set ngIf:
<div *ngIf="isVisible">
<mat-radio-group class="example-radio-group" [(ngModel)]="radioSelected">
...
and use timeout in the class on button click:
next() {
isVisible = true;
this.isVisible = false;
if (this.index < this.list.length - 1) {
...
}
setTimeout(() => this.isVisible = true, 30)
}
Demo

Vue v-for changing from version 1 to version 2

I'm learning Vue.js and found this fiddle that does exactly what I want to do.
Here is the fiddle: https://jsfiddle.net/os7hp1cy/48/
I integrated this and am getting this error:
invalid expression: v-for="user in users | filterBy searchKey | paginate"
So I've done some digging and I see it has changed from version 1 to 2. However, I don't know how to fix this.
<li v-for="user in users | filterBy searchKey | paginate">{{ user.name }}</li>
I would like to replace this with something that Vue 2 will support and will work the same way.
As of Vue version 2, filters can only be used inside text interpolations ({{ }} tags). See the documentation for migrating from Vue version 1.
You can use a computed property to filter the users and use that computed property in the v-for directive instead:
computed: {
filteredUsers: function() {
let key = this.searchKey.toUpperCase();
return this.users.filter((user) => {
return user.name.toUpperCase().indexOf(key) !== -1
})
},
paginatedUsers: function() {
var list = this.filteredUsers;
this.resultCount = list.length
if (this.currentPage >= this.totalPages) {
this.currentPage = this.totalPages
}
var index = this.currentPage * this.itemsPerPage
return list.slice(index - 1, index - 1 + this.itemsPerPage)
}
}
<li v-for="user in paginatedUsers">{{ user.name }}</li>
Also, when using v-for to generate a range of numbers like you do for your page numbers, Vue version to starts the index at 1 instead of 0. So, you'll need to update the logic depending on a starting index of 0 as well.
Here's a working fiddle.

Bootstrap media object cannot align properly

I am using Bootstrap 3 media objects and I have this code here dynamically I want to have 4 columns in one row,but the problem is that the last row will have spaces inbetween to the second row.
here is my code.
//content.php
for(var i=0;i<12;i++){
mymdedia+=
'<div class="media col-sm-3">'+
'<div class="media-left">'+
'<a href="#">'+
'<img src=".....">'+
'</a>'+
'</div>'+
'<div class="media-body">'+
'<h4 class="media-heading">'John Doe'</h4>'+
'<p>Live: '+data[i].address+'</p>'+
'</div>'+
'</div>';
}
$('#mycontainer').append(mymdedia);
//index.php
<div class="someclass">
<div class="row" id="mycontainer">
</div>
</div>
and this is the result looks like
I believe you are placing all the media objects in one row ... and they don't fit.
In every row that you've opened ( <div class='row'>...</div> ) you can place only 4 <div class='media col-sm-3'></div> ... because each col size = 3 ... ( 4x3 = 12 )
So in the for loop, after each 4 media objects - you should close the current row div and create a new <div class='row'> ...
See plunker: http://plnkr.co/edit/BJa3qdbKcYMDwhW809gp?p=preview
Right after the for loop:
for(var i=0;i<12;i++){
if ( i && i % 4 == 0 )
mymdedia += '</div><div class="row">'; // close row and create a new one
... your original code ...
}
mymdedia += '</div>' // close the last row outside the for loop ...