Using a JQuery Plug-in in a mean application through a directive and TypeScript Interfaces

elevysi · 28 February 2017 |

There are loads of interesting Jquery libs out there and not all of them have their corresponding Angular modules offered. Since JQuery has been out for a longer time, you are going to find cool JQuery plugins with no equivalent as Angular modules. It is advised to avoid explicitly using JQuery in Angular since the later was introduced to overcome the unorganized code resulting from the use of JQuery; nevertheless, Angular itself relies on JQuery and hence can by default handle what is offered through JQuery.

The problem remains that one cannot just afford to create Angular modules for the JQuery plugins they might be interested in, each and every time they need them. I faced this problem when I attempted to use CubePortfolio in my MEAN Application; the JQuery plugin works great but did not have an Angular module. My solution was to use the plugin as an Angular directive. I would give the data to the directive and let the directive display my data by applying the plugin magic on the data.

What I did first was to make sure that my JQuery and CubePortfolio scripts were imported and available in my single-page. I was then introduced to the concept of interfaces in Typescript; one may declare within the JQuery Typescript interface the CubePortfolio methods that will be used in our directive; these methods shall be the methods offered by the plugin which are declared in the interface. Please note the use of the any identifier to refer to the type of argument the methods accept.

//_directives/cube.portfolio.d.ts

interface JQuery {
    cubeportfolio(options?:any):JQuery;
    cubeportfolio(type?:any, html?: any):JQuery;
}

In my setting, the directive needs to receive snaps that will be formatted by my cubePortfolio plugin in a very fashionable way.

//_directives/cube.portfolio.component.ts

import { Component, OnInit, ElementRef, ViewChild, AfterViewInit, Input } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';

import { Snap } from "../_models/snap";

declare var $:JQueryStatic;
/// <reference path="cube.portfolio.d.ts" />



@Component({
    moduleId: module.id,
    selector: 'cubeportfolio',
    templateUrl: 'cube.portfolio.component.html'
})
 
export class CubePortfolioComponent implements OnInit, AfterViewInit{
    
    @ViewChild('gridSnapsContainer') 
    el:ElementRef;

    cubePortfolioInited : boolean = false;


    private _snaps = new BehaviorSubject<Snap[]>([]);
    snap : Snap;
    selectedSnapId : string;

    @Input()
    set snaps(value) {
        // set the latest value for _data BehaviorSubject
        this._snaps.next(value);
    };

    get snaps() {
        // get the latest value from _data BehaviorSubject
        return this._snaps.getValue();
    }

    compSnaps : Snap [];
    
    ngOnInit() {
        this._snaps
            .subscribe (snaps => {
                this.compSnaps = snaps;
                this.applyCubePortfolio();
            });
    }
 
    applyCubePortfolio(){


        var options = {
        layoutMode: 'grid',
        rewindNav: true,
        scrollByPage: false,
        mediaQueries: [{
            width: 1100,
            cols: 3
        }, {
            width: 800,
            cols: 3
        }, {
            width: 500,
            cols: 2
        }, {
            width: 320,
            cols: 1
        }],
        defaultFilter: '*',
        animationType: 'rotateSides',
        gapHorizontal: 10,
        gapVertical: 10,
        gridAdjustment: 'responsive',
        caption: 'overlayBottomPush',
        displayType: 'sequentially',
        displayTypeSpeed: 100,

        // lightbox
        lightboxDelegate: '.cbp-lightbox',
        lightboxGallery: true,
        lightboxTitleSrc: 'data-title',
        lightboxCounter: '<div class="cbp-popup-lightbox-counter">{{current}} of {{total}}</div>',

        // singlePage popup
        singlePageDelegate: '.cbp-singlePage',
        singlePageDeeplinking: true,
        singlePageStickyNavigation: true,
        singlePageCounter: '<div class="cbp-popup-singlePage-counter">{{current}} of {{total}}</div>',
        singlePageCallback: function(url : any, element : any) {
            // to update singlePage content use the following method: this.updateSinglePage(yourContent)
        },

        // singlePageInline
        singlePageInlineDelegate: '.cbp-singlePageInline',
        singlePageInlinePosition: 'below',
        singlePageInlineInFocus: true,
        singlePageInlineCallback: function(url : any, element : any) {
            // to update singlePageInline content use the following method: this.updateSinglePageInline(yourContent)
            var t = this;            
            // Update with the service
            $.ajax({
                    url: "api/snap/"+url,
                    type: 'GET',
                    dataType: 'html',
                    timeout: 5000
                })
                .done(function(result) {

                    t.updateSinglePageInline(result);

                })
                .fail(function() {
                    t.updateSinglePageInline("Error! Please refresh the page!");
                });
        }
    };
    
        
        if(this.compSnaps != null){
            if(this.cubePortfolioInited){
                /**
                 * Start by a destroy
                 */
                $(this.el.nativeElement).cubeportfolio('destroy');
            }

            setTimeout(() => { 
                $(this.el.nativeElement).cubeportfolio(options);
                this.cubePortfolioInited = true;

            }, 2000);
//     $(this.el.nativeElement).cubeportfolio('appendItems', htmlContent);
        }
    }

    ngAfterViewInit() {
        
    }
    
}

Then in my component I will retrieve the snaps I want to display through the getSnaps method and will, later on, pass them to the cubPortfolio directive within the component view template.

//snaps/snaps.component.ts

import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { Location } from "@angular/common";
import { Observable } from "rxjs";
import {Subject} from 'rxjs/Subject';

import { Snap } from "../_models/snap";
import { SnapService } from "../_services/snap.service";

@Component({
    moduleId: module.id,
    selector : "lalifeapp",
    templateUrl : 'snaps.component.html'
})

export class SnapsComponent implements OnInit{

    snaps : Subject <Snap[]> = new Subject();
    snaps$ = this.snaps.asObservable();
    compSnaps : Snap [];

    constructor(
        private snapService : SnapService,
        private router : Router,
        private location : Location
    ){};

    getSnaps(): void{
        this.snapService.getSnaps()
            .then(snaps => {
                this.compSnaps = snaps;
                this.snaps.next(snaps);
            });
    }

    ngOnInit(): void {
        this.getSnaps();
    }

    deleteSnap(id : string): void{
        this.snapService.deleteSnap(id)
            .then(() => {
                // this.goBack();
                this.router.navigateByUrl("/");
            });
    }

    goBack() : void {
        this.location.back();
    }
}

The corresponding view shall pass the snaps to the directive:

//snaps/snaps.component.html

<cubeportfolio [snaps]='compSnaps'></cubeportfolio>






Share: