How to disable ngx slick slider when few items? - css

I'm working with ngx-slick-slider (https://www.npmjs.com/package/ngx-slick-carousel) and it doesn't seem to have a config to disable the slider when there are few items. When there are less items than the needed to display the carousel they displayed centered in the screen. How can I achieve this?
Template:
<div class="scrolling-wrapper" *ngIf="hasUsage">
<ngx-slick-carousel class="carousel"
#slickModal="slick-carousel"
[config]="slideConfig"
(init)="slickInit($event)"
(breakpoint)="breakpoint($event)"
(afterChange)="afterChange($event)"
(beforeChange)="beforeChange($event)">
<div ngxSlickItem *ngFor="let card of data" class="slide">
<div class="card-container">
<card-component
[thecard]="card"
></card-component>
</div>
</div>
</ngx-slick-carousel>
</div>
Component.ts:
slideConfig = {
"slidesToShow": 7,
"slidesToScroll": 4,
"infinite": false
};
slickInit(e:any) {
console.log('slick initialized');
}
breakpoint(e:any) {
console.log('breakpoint');
}
afterChange(e:any) {
console.log('afterChange');
}
beforeChange(e:any) {
console.log('beforeChange');
}

I see two ways to go about this:
Disable the buttons in the slick config { 'arrows': false }
and then create custom elements in your template that hook up to slickNext() and slickPrev(). You can now disable those buttons using something like *ngIf="slides.length <= mySlideLimit
You could also create custom templates for your forward/previous buttons in the config using the nextArrow and prevArrow config options.
I persionallly prefer option 1.
template:
<div class="scrolling-wrapper" *ngIf="hasUsage">
<ngx-slick-carousel class="carousel" ...>
</ngx-slick-carousel>
<button *ngIf="data.length >= 3" (click)="slickModal.slickNext">Next</button>
<button *ngIf="data.length >= 3" (click)="slickModal.slickPrev">Prev</button>
</div>
Component.ts
slideConfig = {
"slidesToShow": 7,
"slidesToScroll": 4,
"infinite": false,
"arrows": false,
};
...
You can find more info on all that suff here: http://kenwheeler.github.io/slick/

Related

VUE3.JS - Modal in a loop

I have a GET request in my home.vue component.
This query allows me to get an array of objects.
To display all the objects, I do a v-for loop and everything works fine.
<div class="commentaires" v-for="(com, index) of coms" :key="index">
My concern is that I want to display an image by clicking on it (coms[index].imageUrl), in a modal (popup).
The modal is displayed fine but not with the correct image, i.e. the modal displays the last image obtained in the loop, which is not correct.
Here is the full code of my home.vue component
<template>
<div class="container">
<div class="commentaires" v-for="(com, index) of coms" :key="index">
<modale :imageUrl="com.imageUrl_$this.index" :revele="revele" :toggleModale="toggleModale"></modale>
<img class="photo" :src=""" alt="image du commentaire" #click="toggleModale">
</div>
</div>
</template>
<script>
//import axios from "axios";
import axios from "axios";
import Modale from "./Modale";
export default {
name: 'HoMe',
data() {
return {
coms: [],
revele: false
}
},
components: {
modale: Modale
},
methods: {
toggleModale: function () {
this.revele = !this.revele;
},
</script>
Here is my modale.vue component
<template>
<div class="bloc-modale" v-if="revele">
<div class="overlay" #click="toggleModale"></div>
<div class="modale card">
<div v-on:click="toggleModale" class="btn-modale btn btn-danger">X</div>
<img :src=""" alt="image du commentaire" id="modal">
</div>
</div>
</template>
<script>
export default {
name: "Modale",
props: ["revele", "toggleModale", "imageUrl"],
};
</script>
I've been working on it for 1 week but I can't, so thank you very much for your help...
in your v-for loop you're binding the same revele and toggleModale to every modal. When there is only one revele then any time it's true, all modals will be displayed. It's therefore likely you're actually opening all modals and simply seeing the last one in the stack. You should modify coms so that each item has it's own revele, e.g.:
coms = [
{
imageUrl: 'asdf',
revele: false
},
{
imageUrl: 'zxcv',
revele: false
},
{
imageUrl: 'ghjk',
revele: false
}
];
then inside your v-for:
<modale
:image-url="com.imageUrl"
:revele="com.revele"
#toggle-modale="com.revele = false"
></modale>
<img class="photo" :src=""" alt="image du commentaire" #click="com.revele = true">
passing the same function as a prop to each modal to control the value of revele is also a bad idea. Anytime a prop value needs to be modified in a child component, the child should emit an event telling the parent to modify the value. Notice in my code snippet above I replaced the prop with an event handler that turns the revele value specific to that modal to false. Inside each modal you should fire that event:
modale.vue
<div class="btn-modale btn btn-danger" #click="$emit('toggle-modale')">
X
</div>
This way you don't need any function at all to control the display of the modals.

Highlight the sidebar item according the progress

I have a sidebar to display the job progress. Now I have three phases.
At the beginning the project has not started yet, so I want all sidebar items are greyed out. I want the current phase is based on the previous phases completion.
On the right side there are three buttons. Here is the logic.
Working on phase 1. When I work on the phase 1, the Phase 1 sidebar item is active and the color is green. Phase 2 and Phase 3 items are still inactive.
Working on phase 2. When I click Phase 1 completed button then we go to phase 2, the Phase 2 is colored as green and Phase 1 is still active but just no color.
Working on phase 3. When I click Phase 2 completed button then we go to phase 3, the Phase 3 is colored as green and Phase 1 and Phase 2 are active but just no color.
When we go to the next phase, the previous phase items have their borders visible.
My ts code:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-sidenav',
templateUrl: './sidenav.component.html',
styleUrls: ['./sidenav.component.css']
})
export class SidenavComponent implements OnInit {
sidenavWidth = 10;
ngStyle: string;
active1: boolean;
active2: boolean;
active3: boolean;
constructor() {
}
ngOnInit() {
this.active1 = false;
this.active2 = false;
this.active3 = false;
}
submit1() {
this.active1 = true;
this.active2 = false;
this.active3 = false;
console.log('1');
}
submit2() {
this.active1 = true;
this.active2 = true;
this.active3 = false;
console.log('2');
}
submit3() {
this.active1 = true;
this.active2 = true;
this.active3 = true;
console.log('3');
}
}
My html:
<mat-sidenav-container fullscreen>
<mat-sidenav #sidenav mode="side" class="example-sidenav" [ngStyle]="{ 'width.em': sidenavWidth }" opened="true">
<div class="logomain">Project progress</div>
<mat-nav-list>
<mat-list-item routerLink="1" [routerLinkActive]="[active1]" >
<div fxFlex="10"></div>
<div *ngIf="sidenavWidth > 6" class="sidenav-item">
<h5 class="lead">Phase 1</h5>
</div>
</mat-list-item>
<mat-list-item routerLink="2" [routerLinkActive]="[active2]">
<div fxFlex="10"></div>
<div *ngIf="sidenavWidth > 6" class="sidenav-item">
<h5 class="lead">Phase 2</h5>
</div>
</mat-list-item>
<mat-list-item routerLink="3" [routerLinkActive]="[active3]">
<div fxFlex="10"></div>
<div *ngIf="sidenavWidth > 6" class="sidenav-item">
<h5 class="lead">Phase 3</h5>
</div>
</mat-list-item>
</mat-nav-list>
</mat-sidenav>
<!-- <div class="example-sidenav-content">
<router-outlet></router-outlet>
</div> -->
<br><br><br>
<section>
<br>
<div class="example-button-row">
<button mat-raised-button color="primary" (click) ="submit1()">Phase 1 completed</button>
</div>
</section>
<mat-divider></mat-divider>
<br>
<section>
<br>
<div class="example-button-row">
<button mat-raised-button color="primary" (click) = "submit2()">Phase 2 completed</button>
</div>
</section>
<br>
.<section>
<br>
<div class="example-button-row">
<button mat-raised-button color="primary" (click) = "submit3()">Phase 3 completed</button>
</div>
</section>
<br>
</mat-sidenav-container>
Here is the stackblize example.
My questions. When I click the buttons the side bar item are not greyed out by the condition. Also I am not sure how to apply the green color to the working phase.
Finally I have many items instead of 3 items in the example. I don't want manually to set boolean values in the click events.
Update:
The correct editable stackblitz link https://stackblitz.com/edit/angular-material-with-angular-sidenav-etrylt
It looks like you just need a simple structure to represent your Phases with a property to indicate if it is completed or not.
interface Phase {
id: number;
name: string;
isComplete: boolean;
}
Then, when you click a button, toggle the isComplete property.
You can define an array of Phase objects and use *ngFor to repeat them in your template. This will prevent your template from growing when you add more phases.
sidebar.component.ts:
public phases = [
{ id: 1, name: "Phase 1", isComplete: false },
{ id: 2, name: "Phase 2", isComplete: false },
{ id: 3, name: "Phase 3", isComplete: false }
];
sidebar.component.html:
<mat-nav-list>
<mat-list-item *ngFor="let phase of phases">
<h5>{{ phase.name }}</h5>
</mat-list-item>
</mat-nav-list>
<section *ngFor="let phase of phases">
<button
(click)="togglePhaseComplete(phase)"
[disabled]="phase.isComplete"
>
{{ phase.name }} completed!
</button>
</div>
</section>
Then, you can use this isComplete property in your template to disable buttons for phases that have already been completed:
[disabled]="phase.isComplete"
You can also make a little helper function to determine if a phase is Active or not in order to display your green background color:
public isActive(phase: Phase) {
// the first incomplete phase found is active
const activePhase = this.phases.find(p => !p.isComplete);
return phase.id === activePhase?.id;
}
Then just add [class.green]="isActive(phase)" to your mat-list-item (assuming you defined a class named "green" in your css).
Here's a working StackBlitz
Now, disabling the nav items for phases that haven't yet been active is a little more work because the mat-list-item doesn't have a disabled property like the button does, so something like this will NOT work:
<mat-list-item *ngFor="let phase of phases"
[disabled]="!isActive(phase) && !phase.isComplete"
>
But, as mentioned in this answer, you can create a css class to disable mouse events and display the item as disabled, so the code would look like:
<mat-list-item *ngFor="let phase of phases"
[class.disabled]="!isActive(phase) && !phase.isComplete"
>
<button
mat-raised-button
color="primary"
[ngClass]="
phase.active1 === true
? 'active'
: completedPhase.indexOf('active1') > -1
? 'pre-active'
: ''
"
>
Phase 1 completed
</button>
<button
mat-raised-button
color="primary"
[ngClass]="
phase.active2 === true ? 'active' : completedPhase.indexOf('active2') > -1
? 'pre-active'
: ''
"
>
Phase 2 completed
</button>

How to access array elements inside vue.js conditional css class

I'm getting starting with Vue.js and I have a simple page set up to experiment with conditional css.
<div id="app">
<div class="demo" #click="handleClick(0)" :class="{ 'red': attachRed[0] }">
</div>
<div class="demo" #click="handleClick(1)" :class="{ 'red': attachRed[1] }">
</div>
<div class="demo" #click="handleClick(2)" :class="{ 'red': attachRed[2] }">
</div>
</div>
and then my js
new Vue({
el: "#app",
data: {
attachRed: [false, false, false]
},
methods: {
handleClick: function(index) {
this.attachRed[index] = !this.attachRed[index];
console.log(this.attachRed)
}
}
});
Each div is a grey block. When attaching the "red" class, the block turns red. attachRed array is updating every time a demo div is clicked. But the class is never added. If I start the attachRed property off as being true, then the red class is attached initially, but this isn't toggled when clicked. This works if these values aren't stored in an array though.
Is it possible to make the view bindings watch for these changes or to manually trigger one? Or is there some sort of gotcha when it comes to array properties?
It is a gotcha. This page goes into it a bit: https://vuejs.org/2016/02/06/common-gotchas/
In short, you want to do
var val = this.attachedRed[index]
this.attachedRed.$set(index, !val);

Telescope Owl Carousel

I am trying to use owl-carousel with Telescope but i am not able to make it to work. I have no dynamic data at this point. Below are the steps I followed.
Create a Telescope package.
Added Theme Zeiss.
Add the owl-carousel package
Then on the user Profile, where the telescope app shows the list of Posts the user has liked in a tabular format, i want to display it as a carousel with the images. (Please note this is the end goal, for now i am just trying to get the basic owl-carousel functionality work)
Below are the steps i took.
On the User_profile template i added a new template carousel
<template name="carousel">
<div class="user-profile-votes grid grid-module">
<div id="owl-carousel">
<div class="item"><h1>1</h1></div>
<div class="item"><h1>2</h1></div>
<div class="item"><h1>3</h1></div>
<div class="item"><h1>4</h1></div>
<div class="item"><h1>5</h1></div>
<div class="item"><h1>6</h1></div>
<div class="item"><h1>7</h1></div>
<div class="item"><h1>8</h1></div>
<div class="item"><h1>9</h1></div>
<div class="item"><h1>10</h1></div>
<div class="item"><h1>11</h1></div>
<div class="item"><h1>12</h1></div>
<div class="item"><h1>13</h1></div>
<div class="item"><h1>14</h1></div>
<div class="item"><h1>15</h1></div>
<div class="item"><h1>16</h1></div>
</div>
</div>
<div class="customNavigation">
<a class="btn prev">Previous</a>
<a class="btn next">Next</a>
</div>
</template>
After that is done, i added the js file supporting the template as below.
Template.carousel.rendered = function() {
Meteor.setTimeout(function() {
var owl = $('#owl-carousel');
owl.owlCarousel({
navigation : true,
loop: true,
autoplay: 1000,
items : 10, //10 items above 1000px browser width
itemsDesktop : [1000,5], //5 items between 1000px and 901px
itemsDesktopSmall : [900,3], // betweem 900px and 601px
itemsTablet: [600,2], //2 items between 600 and 0
itemsMobile : false, // itemsMobile disabled - inherit from itemsTablet option
mouseDrag: true,
touchDrag: true,
afterInit : function(elem){
var that = this
that.owlControls.prependTo(elem)
}
});
}, 0)
}
With this the carousel works, shows me 10 different elements, as you can see in the below video. Even the Autoplay works, the however the buttons or dots don't. I tried zillions of things and referred many articles changed timeout, added autorun, etc. However, this is a static list.
Not sure why it's not working, any help will be great.
https://youtu.be/Ljoxw561Eic
This owl-carousel package is using Owl Carousel 2
The link in the atmospherejs.com is wrong , that is old version. The Options & Events are different from this version.

Template helper crashing app

My app is crashing my browser after implementing this columnWidth helper method. All I'm trying to do is rotate between col-md-7 and col-md-5 (Bootstrap classes) so that no two consecutive posts are the same width.
columnWidth: function() {
if (Session.get('columnWidth') === 'col-md-7') {
Session.set('columnWidth', 'col-md-5');
} else {
Session.set('columnWidth', 'col-md-7');
}
return Session.get('columnWidth');
}
The post template:
{{#each this}}
<div class="{{columnWidth}}">
<img src="{{image}}" height="350" width="{{imageWidth}}" alt="">
<div class="content">
<h2>{{title}}</h2>
<p>{{content}}</p>
<span class="dateAuthored">{{date}}</span>
</div>
</div>
{{/each}}
this refers to:
data: function() {
return Articles.find();
}
Any ideas why this is happening? I'm not receiving any errors. The browser tab just becomes unresponsive. Thanks.
You are constantly setting the same reactive variable so for example with the first div when the helper is called it will set it to col-md-7, then when it is called for the 2nd row you are changing the same variable to col-md-5 which is problematic for 2 reasons:
a) the template will redraw the first column and so they will both be col-md-5
b) the same helpers will get called again. I believe your browser crashes because you have created an infinite loop. Try console logging something inside your columnWidth helper and see how many times it gets called.
To achieve what you want you need to get the index of the {{#each }} loop and then have the column class dependent on whether it's odd or even. Unfortunately getting the index in meteor handlebars is a little tricky.
Try:
{{#each articles}}
<div class="{{columnWidth index}}">
<img src="{{image}}" height="350" width="{{imageWidth}}" alt="">
<div class="content">
<h2>{{title}}</h2>
<p>{{content}}</p>
<span class="dateAuthored">{{date}}</span>
</div>
</div>
{{/each}}
Then the following helpers:
articles: function() {
//'this' in this context should work.
//if not just replace with Articles.find().map(...
var articles = this.map(function(article, index) {
var i = _.extend(article, {index: index});
return i;
});
return articles;
},
columnWidth: function(index) {
if (index % 2 === 0)
return "col-md-5";
else
return "col-md-7"
}

Resources