Angular项目与zowe plug-in的相互转换

一、Angular项目转为zowe plug-in

1.1 准备

开始前,要准备好angular和zowe的运行环境,确保一个纯angular项目可以正常运行,一个纯zowe的plun-in也如此。
准备好angular项目,在guide中,我们选择angular官网的《英雄指南》
下载后解压toh-pt6到zlux目录下:**/zlux/toh-pt6。
在根目录(**/zlux/toh-pt6)打开命令窗口,执行

npm install
npm start

浏览器访问http://localhost:4200,成功界面如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TbCDolcN-1575965141282)(evernotecid://20DBFB03-243A-4A3E-8FB8-45BD297479F0/appyinxiangcom/20560126/ENResource/p22)]

1.2 创建pluginDefinition.json

在根目录(**/zlux/toh-pt6)新建文件pluginDefinition.json,配置plug-in相关信息。

{
    "identifier": "org.openmainframe.zowe.tourOfHeroes",
    "apiVersion": "1.0.0",
    "pluginVersion": "0.0.1",
    "pluginType": "application",
    "webContent": {
        "framework": "angular2",
        "launchDefinition": {
            "pluginShortNameKey": "tourOfHeroes",
            "pluginShortNameDefault": "Tour of Heroes",
            "imageSrc": "assets/icon.png"
        },
        "descriptionKey": "",
        "descriptionDefault": "",
        "isSingleWindowApp": true,
        "defaultWindowStyle": {
            "width": 1300,
            "height": 500
        }
    }
}

在目录**/zlux/toh-pt6/src下创建assets,并assets放置一张名为“icon.png”的图片,作为plug-in的图标。

1.3 创建org.openmainframe.zowe.tourOfHeroes.json

方法一:在目录**/zlux/zlux-app-server/plugins下创建org.openmainframe.zowe.tourOfHeroes.json

{
  "identifier": "org.openmainframe.zowe.tourOfHeroes",
  "pluginLocation": "../../toh-pt6"
}

其中:
“identifier”对应pluginDefinition.json中的字段“identifier”,为plug-in的ID标识;
"pluginLocation"为toh-pt6对**/zlux/zlux-app-server/plugins的相对路径。

在目录**/zlux/zlux-build下执行命令:

ant deploy

方法二:在目录**/zlux/zlux-app-server/deploy/instance/ZLUX/plugins下创建org.openmainframe.zowe.tourOfHeroes.json

{
  "identifier": "org.openmainframe.zowe.tourOfHeroes",
  "pluginLocation": "../../toh-pt6"
}

1.4添加webpack相关的lib

npm install --save-dev webpack webpack-cli webpack-config ts-loader angular2-template-loader exports-loader css-loader style-loader html-loader

1.5 创建webpack.config.js

在根目录(**/zlux/toh-pt6)新建文件webpack.config.js,配置项目webpack打包

const path = require('path');
const webpackConfig = require('webpack-config');
const CopyWebpackPlugin = require('copy-webpack-plugin');

if (process.env.MVD_DESKTOP_DIR == null) {
  throw new Error('You must specify MVD_DESKTOP_DIR in your environment');
}

const config = {
  'entry': [
    path.resolve(__dirname, './src/plugin.ts'),
  ],
  'output': {
    'path': path.resolve(__dirname, './web'),
    'filename': 'main.js',
  },
  'plugins': [
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, './src/assets'),
        to: path.resolve('./web/assets')
      }
    ])
  ]
};

module.exports = new webpackConfig.Config()
  .extend(path.resolve(process.env.MVD_DESKTOP_DIR, 'plugin-config/webpack.base.js'))
  .merge(config);

1.6创建plugin.ts

在目录**/zlux/toh-pt6/src新建文件plugin.ts,配置项目webpack打包内容

export { AppModule as pluginModule } from './app/app.module';
export { AppComponent as pluginComponent } from './app/app.component';

1.7添加webpack打包命令

修改**/zlux/toh-pt6/package.json

 "scripts": {
    "start": "webpack --progress --colors --watch --mode=production",
    "build": "webpack --progress --colors"
  },

1.8修改app.module.ts

替换BrowserModule为CommonModule, 引入APP_BASE_HREF,添加到providers:

import { CommonModule, APP_BASE_HREF } from '@angular/common';
import { NgModule }       from '@angular/core';
import { FormsModule }    from '@angular/forms';
import { HttpClientModule }    from '@angular/common/http';

import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';
import { InMemoryDataService }  from './in-memory-data.service';

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

import { AppComponent }         from './app.component';
import { DashboardComponent }   from './dashboard/dashboard.component';
import { HeroDetailComponent }  from './hero-detail/hero-detail.component';
import { HeroesComponent }      from './heroes/heroes.component';
import { HeroSearchComponent }  from './hero-search/hero-search.component';
import { MessagesComponent }    from './messages/messages.component';


@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    AppRoutingModule,
    HttpClientModule,
  ],
  declarations: [
    AppComponent,
    DashboardComponent,
    HeroesComponent,
    HeroDetailComponent,
    MessagesComponent,
    HeroSearchComponent
  ],
  bootstrap: [ AppComponent ],
  providers: [{provide: APP_BASE_HREF, useValue: '/'}]
})
export class AppModule { }

说明:使用APP_BASE_HREF只是为了兼容angular router,但会导致zowe刷新功能异常,因为router会修改url,zowe将不识别。建议开发将其替换为Observable的消息通信与Directive等技术实现。

1.9 修该app.component.css

由于styles.css在当前webpack方式下没有与任何组件绑定,所以需要做一点代码迁移,即将src/styles.css的代码拷贝到app.component.css,并修改所有body为.app:

/* Global Styles */
h1 {
  color: #369;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 250%;
}
h2, h3 {
  color: #444;
  font-family: Arial, Helvetica, sans-serif;
  font-weight: lighter;
}
.app {
  margin: 2em;
}
.app, input[text], button {
  color: #333;
  font-family: Cambria, Georgia;
}
a {
  cursor: pointer;
  cursor: hand;
}
button {
  font-family: Arial;
  background-color: #eee;
  border: none;
  padding: 5px 10px;
  border-radius: 4px;
  cursor: pointer;
  cursor: hand;
}
button:hover {
  background-color: #cfd8dc;
}
button:disabled {
  background-color: #eee;
  color: #aaa;
  cursor: auto;
}

/* Navigation link styles */
nav a {
  padding: 5px 10px;
  text-decoration: none;
  margin-right: 10px;
  margin-top: 10px;
  display: inline-block;
  background-color: #eee;
  border-radius: 4px;
}
nav a:visited, a:link {
  color: #607D8B;
}
nav a:hover {
  color: #039be5;
  background-color: #CFD8DC;
}
nav a.active {
  color: #039be5;
}

/* everywhere else */
* {
  font-family: Arial, Helvetica, sans-serif;
}


/* Navigation link styles */
nav a {
  padding: 5px 10px;
  text-decoration: none;
  margin-right: 10px;
  margin-top: 10px;
  display: inline-block;
  background-color: #eee;
  border-radius: 4px;
}
nav a:visited, a:link {
  color: #607D8B;
}
nav a:hover {
  color: #039be5;
  background-color: #CFD8DC;
}
nav a.active {
  color: #039be5;
}

/* everywhere else */
* {
  font-family: Arial, Helvetica, sans-serif;
}


/* AppComponent's private CSS styles */
h1 {
  font-size: 1.2em;
  margin-bottom: 0;
}
h2 {
  font-size: 2em;
  margin-top: 0;
  padding-top: 0;
}
nav a {
  padding: 5px 10px;
  text-decoration: none;
  margin-top: 10px;
  display: inline-block;
  background-color: #eee;
  border-radius: 4px;
}
nav a:visited, a:link {
  color: #334953;
}
nav a:hover {
  color: #039be5;
  background-color: #CFD8DC;
}
nav a.active {
  color: #039be5;
}

对应修改app.component.html为:

<div class="app">
  <h1>{{title}}</h1>
  <nav>
    <a routerLink="/dashboard">Dashboard</a>
    <a routerLink="/heroes">Heroes</a>
  </nav>
  <router-outlet></router-outlet>
  <app-messages></app-messages>
</div>

1.10 改写dataService

在根目录(**/zlux/toh-pt6)新建文件nodeServer/src/server.ts。

import { Router } from 'express-serve-static-core'

const express = require('express')

class Server {
  private router: Router;

  constructor() {
    let router = express.Router();

    router.get('/heroes', function (req, res) {
      const heroes = [
        { id: 11, name: 'Dr Nice' },
        { id: 12, name: 'Narco' },
        { id: 13, name: 'Bombasto' },
        { id: 14, name: 'Celeritas' },
        { id: 15, name: 'Magneta' },
        { id: 16, name: 'RubberMan' },
        { id: 17, name: 'Dynama' },
        { id: 18, name: 'Dr IQ' },
        { id: 19, name: 'Magma' },
        { id: 20, name: 'Tornado' }
      ];
      res.set('Content-Type', 'application/json');
      res.send(heroes);
    });

    this.router = router;
  }

  getRouter(): Router {
    return this.router
  }
}

exports.serverRouter = function () {
  return new Promise(function (resolve, reject) {
    let server = new Server();
    resolve(server.getRouter());
  });
}

在目录**/zlux/toh-pt6/nodeServer新建package.json

{
  "name": "top-pt6-zowe-app-server",
  "version": "1.0.0",
  "description": "Dataservice of Tour of Heros",
  "scripts": {
    "start": "tsc --watch",
    "build": "tsc"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/express": "~4.16.0",
    "@types/node": "~8.10.23",
    "typescript": "~2.9.0"
  }
}

在目录**/zlux/toh-pt6/nodeServer新建tsconfig.json

{
  "compileOnSave": true,
  "include": [
    "src/*.ts",
  ],
  "compilerOptions": {
    "outDir": "../lib",
    "sourceMap": true,
    "module": "es2015",
    "target": "es5",
    "moduleResolution": "node",
    "types": [
      "node",
      "express"
    ],
    "lib": [
      "es2018",
      "dom"
    ]
  }
}

在目录**/zlux/toh-pt6/nodeServer下执行命令:

npm install
npm start

修改**/zlux/toh-pt6/pluginDefinition.json:

{
    "identifier": "org.openmainframe.zowe.tourOfHeroes",
    "apiVersion": "1.0.0",
    "pluginVersion": "0.0.1",
    "pluginType": "application",
    "webContent": {
        "framework": "angular2",
        "launchDefinition": {
            "pluginShortNameKey": "tourOfHeroes",
            "pluginShortNameDefault": "Tour of Heroes",
            "imageSrc": "assets/icon.png"
        },
        "descriptionKey": "",
        "descriptionDefault": "",
        "isSingleWindowApp": true,
        "defaultWindowStyle": {
            "width": 1300,
            "height": 500
        }
    },
    "dataServices": [
        {
            "type": "router",
            "name": "api",
            "serviceLookupMethod": "external",
            "fileName": "server.js",
            "routerFactory": "serverRouter",
            "dependenciesIncluded": true
        }
    ]
}

修改**/zlux/toh-pt6/src/app/hero.service.ts为:

import { Injectable, Inject } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';

import { Observable, of } from 'rxjs';
import { catchError, map, tap } from 'rxjs/operators';

import { Hero } from './hero';
import { MessageService } from './message.service';

import { Angular2InjectionTokens } from "../../../zlux-app-manager/virtual-desktop/src/pluginlib/inject-resources";

const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};

@Injectable({ providedIn: 'root' })
export class HeroService {

  heroesUrl: string;

  constructor(
    private http: HttpClient,
    private messageService: MessageService,
    @Inject(Angular2InjectionTokens.PLUGIN_DEFINITION) private pluginDefinition: ZLUX.ContainerPluginDefinition,
    ) { 
      this.heroesUrl = ZoweZLUX.uriBroker.pluginRESTUri(this.pluginDefinition.getBasePlugin(), 'api', 'heroes');  // URL to web api
    }

  /** GET heroes from the server */
  getHeroes (): Observable<Hero[]> {
    return this.http.get<Hero[]>(this.heroesUrl)
      .pipe(
        tap(_ => this.log('fetched heroes')),
        catchError(this.handleError<Hero[]>('getHeroes', []))
      );
  }

  /** GET hero by id. Return `undefined` when id not found */
  getHeroNo404<Data>(id: number): Observable<Hero> {
    const url = `${this.heroesUrl}/?id=${id}`;
    return this.http.get<Hero[]>(url)
      .pipe(
        map(heroes => heroes[0]), // returns a {0|1} element array
        tap(h => {
          const outcome = h ? `fetched` : `did not find`;
          this.log(`${outcome} hero id=${id}`);
        }),
        catchError(this.handleError<Hero>(`getHero id=${id}`))
      );
  }

  /** GET hero by id. Will 404 if id not found */
  getHero(id: number): Observable<Hero> {
    const url = `${this.heroesUrl}/${id}`;
    return this.http.get<Hero>(url).pipe(
      tap(_ => this.log(`fetched hero id=${id}`)),
      catchError(this.handleError<Hero>(`getHero id=${id}`))
    );
  }

  /* GET heroes whose name contains search term */
  searchHeroes(term: string): Observable<Hero[]> {
    if (!term.trim()) {
      // if not search term, return empty hero array.
      return of([]);
    }
    return this.http.get<Hero[]>(`${this.heroesUrl}/?name=${term}`).pipe(
      tap(_ => this.log(`found heroes matching "${term}"`)),
      catchError(this.handleError<Hero[]>('searchHeroes', []))
    );
  }

   Save methods //

  /** POST: add a new hero to the server */
  addHero (hero: Hero): Observable<Hero> {
    return this.http.post<Hero>(this.heroesUrl, hero, httpOptions).pipe(
      tap((newHero: Hero) => this.log(`added hero w/ id=${newHero.id}`)),
      catchError(this.handleError<Hero>('addHero'))
    );
  }

  /** DELETE: delete the hero from the server */
  deleteHero (hero: Hero | number): Observable<Hero> {
    const id = typeof hero === 'number' ? hero : hero.id;
    const url = `${this.heroesUrl}/${id}`;

    return this.http.delete<Hero>(url, httpOptions).pipe(
      tap(_ => this.log(`deleted hero id=${id}`)),
      catchError(this.handleError<Hero>('deleteHero'))
    );
  }

  /** PUT: update the hero on the server */
  updateHero (hero: Hero): Observable<any> {
    return this.http.put(this.heroesUrl, hero, httpOptions).pipe(
      tap(_ => this.log(`updated hero id=${hero.id}`)),
      catchError(this.handleError<any>('updateHero'))
    );
  }

  /**
   * Handle Http operation that failed.
   * Let the app continue.
   * @param operation - name of the operation that failed
   * @param result - optional value to return as the observable result
   */
  private handleError<T> (operation = 'operation', result?: T) {
    return (error: any): Observable<T> => {

      // TODO: send the error to remote logging infrastructure
      console.error(error); // log to console instead
      console.error(this.heroesUrl);
      console.error(this.http);
      // TODO: better job of transforming error for user consumption
      this.log(`${operation} failed: ${error.message}`);

      // Let the app keep running by returning an empty result.
      return of(result as T);
    };
  }

  /** Log a HeroService message with the MessageService */
  private log(message: string) {
    this.messageService.add(`HeroService: ${message}`);
  }
}

修改**/zlux/toh-pt6/tslint.json, 注意extends的相对路径:

{
  "extends": [
    "../zlux-app-manager/virtual-desktop/plugin-config/tslint.base.json"
  ]
}

修改**/zlux/toh-pt6/tsconfig.json, 注意extends的相对路径:

{
  "extends": "../zlux-app-manager/virtual-desktop/plugin-config/tsconfig.strict.json",
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "module": "es2015",
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "importHelpers": true,
    "target": "es5",
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2018",
      "dom"
    ]
  }
}

1.11 启动zlux

在目录**/zlux/zlux-app-server/bin下执行自己的启动zlux server命令,我的命令如下:

./nodeServer -s 5000

到此,angular到zowe plug-in的转换就基本成功了,你应该可以看到你精心挑选的icon.png出现在zowe.ZLUX的菜单里,双击试试效果吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值