Quantcast
Channel: Devdactic – Ionic Tutorials
Viewing all articles
Browse latest Browse all 183

How to Create & Publish an Angular Library with Ionic Components

$
0
0

When your application becomes bigger, separating different elements into a library and reusing them across your projects is almost always necessary.

In the past we’ve seen the setup for an Ionic multi app project with shared library, and today we will take things a step further and focus on the creation of such a library.

We’ll create a library with service and component that can be used, and additionally add a configuration block so we could easily inject values into our library when we include it.

To follow the code you need to have the Angular CLI installed.

In our example we will build a library to make HTTP calls to a WordPress URL, however I only noticed after creating the code that there is already a package called ngx-wordpress, so don’t confuse the two!

Creating a new Angular Library

Since a later version the Angular CLI allows to create a workspace without any real content or project – exactly what we need as a starting point for our new library! So we pass a flag to skip the creation of an application, then we can navigate into the new workspace.

Inside we generate a new library and use a custom prefix wp or whatever you want, and also change the entry file but that’s optional as well. Additionally we already generate the component that we will work on later inside the library and then run the build command which is needed at least once to build the /dist folder!

When you get asked about the router and CSS just pick the defaults, they are not really used anyway.

ng new devdacticLibrary --createApplication=false
cd ./devdacticLibrary
ng generate library ngx-wordpress --prefix=wp --entryFile=ngx-wp
ng g component postCard
ng build

# Create the local NPM link
cd dist/ngx-wordpress
npm link

The last two lines are for testing our library local: You don’t want to build, publish and update a library all the time while testing, and you don’t have to!

You can create a local symlink using NPM that can then be used in the project where you want to integrate the package!

For now we will implement the library and everything related to it, we come to the integration on our Ionic project later.

Library Configuration & Service

You might have seen this with other packages that you include with a forRoot() call in your module, and that’s the behaviour we want to implement. To do so, we need to add a function to the main module of our library that exports some information and becomes a LibConfig object which is a simple interface that we define in there as well.

You could also pass more or other information to your library of course, we will simply pass a URL to it for now.

We as create an InjectionToken with our interface as this is not defined at runtime, we just need it do be injected into our module. We will also inject this LibConfigService in the next step inside a service to retrieve the actual value that was passed to our library!

Now go ahead and change the ngx-wordpress/src/lib/ngx-wordpress.module.ts to:

import { NgxWordpressService } from './ngx-wordpress.service';
import { NgModule, ModuleWithProviders, InjectionToken } from '@angular/core';
import { PostCardComponent } from './post-card/post-card.component';
import { HttpClientModule } from '@angular/common/http';
import { IonicModule } from '@ionic/angular';
import { CommonModule } from '@angular/common';

export interface LibConfig {
  apiUrl: string;
}

export const LibConfigService = new InjectionToken<LibConfig>('LibConfig');

@NgModule({
  declarations: [PostCardComponent],
  imports: [CommonModule, HttpClientModule, IonicModule],
  exports: [PostCardComponent]
})
export class NgxWordpressModule {
  static forRoot(config: LibConfig): ModuleWithProviders {
    return {
      ngModule: NgxWordpressModule,
      providers: [
        NgxWordpressService,
        {
          provide: LibConfigService,
          useValue: config
        }
      ]
    };
  }
}

Make sure that whenever you generate other components or services you add them to the declaration and exports in here, otherwise they won’t be available to the app using your library.

Note: We also import the IonicModule in here because our component will use Ionic elements!

Now let’s implement the service of our library which will make some HTTP requests. In the constructor you can see how we inject the token we defined in the previous step, and you can add a log so you see that you really passed the value from your app to the lib.

Besides that we have simply twi functions, I looked at the great wp-api-angular package for this and simply used the configuration and URL of the library.

Open the ngx-wordpress/src/lib/ngx-wordpress.service.ts and change it to:

import { Injectable, Inject } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { LibConfigService, LibConfig } from './ngx-wordpress.module';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class NgxWordpressService {
  baseUrl = this.config.apiUrl;

  constructor(@Inject(LibConfigService) private config: LibConfig, private http: HttpClient) {
    console.log('My config: ', config);
  }

  getPostList(options = {}): Observable<any[]> {
    let params = new HttpParams();
    let keys = Object.keys(options);
    
    for (let key of keys) {
      params = params.append(key, options[key]);
    }

    const requestOptions = {
      params: params
    };
    
    return this.http.get<any[]>(`${this.baseUrl}/posts`, requestOptions).pipe(
      map(posts => {
        for (let post of posts) {
          post.media_url = post['_embedded']['wp:featuredmedia'][0]['media_details'].sizes['medium'].source_url;
        }
        return posts;
      })
    );
  }

  getPost(postId: number) {
    return this.http.get(`${this.baseUrl}/posts/${postId}`);
  }
}

We don’t really need to talk about the functionality in detail, you can also add some own functions for testing to see how things work.

Creating the Library Component

Let’s finish the library by implementing a simple component. I basically wanted to provide a card interface for posts like you can see in the previous WordPress Ionic 4 post, so we need to define an input for the component to pass the data to it.

Also, this component has a button and can output a value once you click it – so you can pass data to it, and you even get something back! You can easily define this EventEmitter and you’ll see how to use it once we integrate the library.

The component doesn’t do anything else, so now change your ngx-wordpress/src/lib/post-card/post-card.component.ts to this:

import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'wp-post-card',
  templateUrl: './post-card.component.html',
  styleUrls: ['./post-card.component.css']
})
export class PostCardComponent implements OnInit {
  @Input('post') post: any;

  @Output()
  details: EventEmitter<number> = new EventEmitter<number>();

  constructor() {}

  ngOnInit() {}

  openDetails() {
    this.details.emit(this.post.id);
  }
}

Finally the view of the component, based on the JSON data that we get back from WordPress (exactly: The WordPress URL you pass to the library).

As said before, we need the Ionic Module because we are using Ionic elements, and because there is an Angular pipe included we also had to import the CommonModule which we did in the first step.

Besides that the component is simply creating some elements based on the data that is (hopefully) passed to it, so go ahead with the ngx-wordpress/src/lib/post-card/post-card.component.html:

<ion-card>
  <ion-card-header>
    <ion-card-title [innerHTML]="post.title.rendered"></ion-card-title>
    <ion-card-subtitle>{{ post.date_gmt | date }}</ion-card-subtitle>
  </ion-card-header>
  <ion-card-content>
    <img [src]="post.media_url">
    <div [innerHTML]="post.excerpt.rendered"></div>
    <ion-button expand="full" fill="clear" (click)="openDetails()" text-right>Read More...</ion-button>
  </ion-card-content>
</ion-card>

Now our library is finished, and you want to see the result!

Testing the Library Local

We don’t want to publish the library yet, as this process takes too long while testing different things.

Let’s start a blank new Ionic app, and then simply run the npm link command inside the apps folder again:

ionic start libIntegration blank
cd ./libIntegration
npm link ngx-wordpress

You won’t see it inside your package.json file, but you should see a statement on the console which shows the path of your symlink.

There’s also a tiny issue with the Angular setup and symlinks, and in order to fix this you can right now open your angular.json inside the Ionic project and add the preserveSymlinks entry at the shown path:

"projects": {
    "app": {
      "architect": {
        "build": {
          "options": {
            "preserveSymlinks": true,

Now with this in place, it’s time to import the library!

Go ahead and open your app/app.module.ts and add the import like:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';

import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';

import { NgxWordpressModule } from 'ngx-wordpress';

@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule,
    NgxWordpressModule.forRoot({
    apiUrl: 'https://YOURWORDPRESSDOMAIN.com/wp-json/wp/v2'
  })],
  providers: [
    StatusBar,
    SplashScreen,
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

As you can see, we can pass in our own URL at this point or, if you feel fancy, also use the environment file of your project in order to use dev/prod environments!

Whenever you want to use components from your own library with Ionic you also have to import the library to the module file of your page, in our case it’s the default page at app/home/home.module.ts:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IonicModule } from '@ionic/angular';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';

import { HomePage } from './home.page';
import { NgxWordpressModule } from 'ngx-wordpress';

@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    IonicModule,
    RouterModule.forChild([
      {
        path: '',
        component: HomePage
      }
    ]),
    NgxWordpressModule
  ],
  declarations: [HomePage]
})
export class HomePageModule {}

At this point we also don’t need the configuration block anymore, just the plain import.

Now we can use both the service and component that we created in the library. First, the service to actually get some data and then the component to render our posts as cards.

Our app/home/home.page.ts has only this one call and also already another openDetails function – we will see how to trigger it in the next step:

import { Component } from '@angular/core';
import { NgxWordpressService } from 'ngx-wordpress';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {
  posts = [];

  constructor(private wpService: NgxWordpressService) {
    this.wpService.getPostList({'_embed': true}).subscribe(res => {
      this.posts = res;
    });
  }

  openDetails(event) {
    console.log('should open: ', event);
  }
}

We simply call the library service, no further URL needs to be passed to it. This is a perfect way to manage your own API calls and share it across your company, the WordPress stuff is just an example of course!

Now we got an array of posts from WordPress, and we can use the data and pass it to the component that’s contained in our library. Also, we are now attaching our own function to the event emitter that we defined, just like you would do with a click on an ion-button.

The view now becomes super small, so change your app/home/home.page.html to:

<ion-header>
  <ion-toolbar color="primary">
    <ion-title>
      Devdactic nxg-wp
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>

  <wp-post-card *ngFor="let post of posts" [post]="post" (details)="openDetails($event)"></wp-post-card>

</ion-content>

That’s it, you can now serve the app, and also run the build for the library including watch flag to directly work on your Angular library and see the results with live reload!

Publishing your Library to NPM

Once you are confident that the version of the library is ready to be published, you can naivgate into the build folder of your library and simply run the publish command:

cd dist/ngx-wordpress
npm publish

Make sure that you are logged in to NPM on your command line and that you also specify a decent readme for your package.

Conclusion

We’ve seen that it’s no rocket science to create your own library with a dynamic configuration that can be passed to it. You can add components, directives, pipes, services, everything you want to share with your other applications.

Hopefully this gives you a good starting point for your next project, and if you want even more guidance and help you can become a member of the Ionic Academy and stay on top of the Ionic game at any time!

You can also find a video version of this article below.

The post How to Create & Publish an Angular Library with Ionic Components appeared first on Devdactic.


Viewing all articles
Browse latest Browse all 183

Trending Articles