3

I am trying to implement owl-carousel into my Angular 2 app.

I followed this example How to use owl-carousel in Angular2? and it actually works well with the only problem that my items are async change (ng-content async change).

By implementing the solution on plnkr when the content of my owl-courosel changes (promoter or detractor), the plugin doesn't reload. So I see just a list of the items but they do not scroll.

So I have nps-comments.component.html where the carousel component is called:

<section class="purchasers comments" *ngIf="comments.promoters.length || comments.detractors.length">
  <carousel class="promoters" *ngIf="comments.promoters.length" [options]="{ items: 1 }">
    <p *ngFor="let promoter of comments.promoters">{{promoter}}</p>
  </carousel>
  <carousel class="detractors" *ngIf="comments.detractors.length" [options]="{ items: 1 }">
    <p *ngFor="let detractor of comments.detractors">{{detractor}}</p>
  </carousel>
</section>

Then the actual carousel.component.ts

import {
  Component,
  Input,
  ElementRef
} from '@angular/core';

import 'jquery';
import 'owl-carousel';

@Component({
  moduleId: module.id,
  selector: 'carousel',
  templateUrl: 'carousel.component.html',
  styleUrls: ['carousel.component.css']
})

export class CarouselComponent {
  @Input() options: Object;

  private $carouselElement: any;

  private defaultOptions: Object = {};

  constructor(private el: ElementRef) { }

  ngAfterViewInit() {
    for (let key in this.options) {
      if (this.options.hasOwnProperty(key)) {
        this.defaultOptions[key] = this.options[key];
      }
    }

    let outerHtmlElement: any = $(this.el.nativeElement);
    this.$carouselElement = outerHtmlElement.find('.owl-carousel').owlCarousel(this.defaultOptions);
  }

  ngOnDestroy() {
    this.$carouselElement.trigger('destroy.owl.carousel');
    this.$carouselElement = null;
  }
}

And this is the carousel.component.html:

<div class="owl-carousel owl-theme">
  <ng-content></ng-content>
</div>

Any help would be really appreciated. Thank you.

Community
  • 1
  • 1

1 Answers1

2

I am sharing my workaround of using owl owl.carousel@2.1.4 with angular 2.0.0 + webpack.

First you will need to install the above^ packages via npm or similar.

Then --> npm install imports-loader

(For using owl within component otherwise you will get function is undefined. Since third-party modules are relying on global variables like $ or this being the window object.).

I'm using webpack so this section is for webpack users:

imports-loader as follow:

{test: /bootstrap\/dist\/js\/umd\//, loader: 'imports?jQuery=jquery'}

Also u can use jQuery as (webpack):

var ProvidePlugin = require('webpack/lib/ProvidePlugin');

use as plugin:

plugins: [
       new webpack.ProvidePlugin({
            jQuery: 'jquery',
            $: 'jquery',
            jquery: 'jquery',
            'window.jQuery': 'jquery'
        })
    ]

For images loader:

{
   test: /\.(png|jpe?g|gif|ico)$/,
   loader: 'file?name=public/img/[name].[hash].[ext]'
}

*public/img -- images folder

CSS loader:

{
   test: /\.css$/,
   include: helpers.root('src', 'app'),
   loader: 'raw'
}

The vendors.js file should import the following:

import 'jquery';
import 'owl.carousel';
import 'owl.carousel/dist/assets/owl.carousel.min.css';

Please be aware that owl.carousel 2 is still use andSelf() deprecated function of jQuery so we need to replace them with the new version of addBack().

Goto node_modules folder in the owl package dist/owl.carousel.js: replace all the occurrences of andSelf() with --> addBack().

Now is the angular 2 part:

owl-carousel.ts:

import {Component} from '@angular/core';

@Component({
    selector: 'carousel',
    templateUrl: 'carousel.component.html',
    styleUrls: ['carousel.css']
})
export class Carousel {
    images: Array<string> = new Array(10);
    baseUrl: string = './../../../../public/img/650x350/';
}

carousel.component.ts:

import { Component, Input, ElementRef, AfterViewInit, OnDestroy } from '@angular/core';

@Component({
    selector: 'owl-carousel',
    template: `<ng-content></ng-content>`
})
export class OwlCarousel implements OnDestroy, AfterViewInit{
    @Input() options: Object;

    $owlElement: any;

    defaultOptions: Object = {};

    constructor(private el: ElementRef) {}

    ngAfterViewInit() {
        for (var key in this.options) {
            this.defaultOptions[key] = this.options[key];
        }
        var temp :any;
        temp = $(this.el.nativeElement);

        this.$owlElement = temp.owlCarousel(this.defaultOptions);
    }

    ngOnDestroy() {
        this.$owlElement.data('owlCarousel').destroy();
        this.$owlElement = null;
    }
}

carousel.component.html:

<owl-carousel class="owl-carousel"[options]="{navigation: true, pagination: true, rewindNav : true, items:2, autoplayHoverPause: true, URLhashListener:true}">
    <div class="owl-stage" *ngFor="let img of images; let i=index">
        <div class="owl-item">
            <a href="#"><img src="{{baseUrl}}{{i+1}}.png"/></a>
        </div>
    </div>
</owl-carousel>

Make sure to bootstrap everything in the app.module:

import { NgModule } from '@angular/core';
import { BrowserModule }  from '@angular/platform-browser';
import { AppComponent } from './app.component';
import  {OwlCarousel} from './components/carousel/carousel.component';
import  {Carousel} from './components/carousel/owl-carousel';


@NgModule({
    imports: [
        BrowserModule,
        NgbModule,
    ],
    declarations: [
        AppComponent,
        OwlCarousel,
        Carousel
    ],
    providers: [appRoutingProviders],
    bootstrap: [ AppComponent ]
})
export class AppModule { }

Now you can use the directive/component in the entire app within the template/templateUrl section, no need to import anything.

Please follow the above since all the steps are necessary to complete the integration between angular 2.0.0 final release and owl.carousel 2.1.4 release.

peterh
  • 11,875
  • 18
  • 85
  • 108
shadiggy
  • 39
  • 3
  • Thanks for this, I've implemented the equivalent of this, but it doesn't seem to work when the content changes (i.e. the list of images in your case). Is it working with changes to the content with you? – Ash McConnell Sep 28 '16 at 13:59
  • First if u have any errors or u can attach the exception/code will be helpful 4 me to reply. – shadiggy Sep 28 '16 at 15:22
  • Second: In carousel.component.html --> I'm using it dynamically in carousel.component.ts (u can inject content from a service or manipulate the data within the class of carousel and use binding within the html). In my example the carousel component rely on the images path which is in my public folder ../public/img/1.png and it is dynamically loading 10 images into the carousel component. – shadiggy Sep 28 '16 at 15:30
  • For injecting data from a service using Observable or Promises you can do that but keep in mind to use the | async when using ngFor directive: {{ dataFromService | async }} Otherwise u might use the subscribe/unsubscribe. In case u need help with how 2 use services I'll post it using a new Thread. – shadiggy Sep 28 '16 at 15:49
  • Thanks for your replies, the problem is when changing the number of elements in the ngFor. I am filtering the list supplied by a parent component (using myArray,filter(...). Have you tried changing the *number* of child divs dynamically? I don't get any errors, it just stops sliding – Ash McConnell Sep 28 '16 at 16:11
  • All the divs within the carousel element should be display as long as they are within the directive. You can bind whatever property or use @Input as well, it should work fine. I didn't understand if u try to add/remove divs from the DOM, u might just use [hidden] property if that was ur purpose... – shadiggy Sep 28 '16 at 20:40
  • Unfortunately I can't share the code, but thanks for your tips, I'll report back if I get it working. – Ash McConnell Sep 29 '16 at 07:38