博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用Angular与TypeScript构建Electron应用(二)
阅读量:5973 次
发布时间:2019-06-19

本文共 4920 字,大约阅读时间需要 16 分钟。

本文所有代码都可以在找到。你可以通过commit历史来查看这些代码是如何一步一步构建的。如果有任何问题,也可以在github的issue上提出。

接前文,现在我们搭建好了一系列的环境,创建了一些初始的代码,是时候开始工作了。

在这篇文章中我们主要负责创建登录界面与主界面,涉及篇幅关系我们不再使用远程服务端来交互,而是创建一些模拟的登录请求,当然,与服务端的交互方法可以在此系列文章后面几篇找到。OK,这里我们希望前端能够像QQ或微信一样,先展示一个登录界面,在登录成功后带领我们打开一个长时间停留的主界面,我们先理清需要做那几件事:

  1. 在Angular中创建路由,包括登录界面与主界面。

  2. 创建browser相关代码,给登录与跳转提供通信反馈。

  3. 在登录成功后我们关闭登录界面跳转至主界面。

Angular创建前端页面

由于我们安装了angular-cli,所以每次创建各类文件时都可以通过cli的方式来解决,这很方便,也降低了Angular的学习成本,如果对此不明白,可以看这里的。

1, 创建组件与路由

首先在src/app的路径下创建2个组件: login与main。好吧,你需要输入ng g component login来创建这个组件,但在之后我们就不再讨论这些细节,我只会告诉该怎么做一件事。

其次我们在src/app的路径下创建一个路由app.routing.ts,我们希望它可以做好两件事,根据URL进行页面的导航,在没有权限时对相应的导航进行保护。具体代码可以参照Angular的官方文档,但我猜你们懒得看,代码如下:

import {NgModule} from '@angular/core'import {Routes, RouterModule} from '@angular/router'import {LoginComponent} from './login/login.component'import {MainComponent} from './main/main.component'export const appRoutes: Routes = [   {path: '', component: LoginComponent},   {path: 'login', component: LoginComponent},   {path: 'main', component: MainComponent}]@NgModule({   imports: [RouterModule.forRoot(appRoutes)],   exports: [RouterModule]})export class AppRoutingModule {}

ok,这很简单,和我们熟悉的Angular1.x或react-route也没有太大区别。但是要让路由运行起来还要做两件事,第一是将路由在app.module.ts中注册,在module上挂载文件,Angular在编译时才会将文件引入进来,第二是在app.component.html中增加路由插座。

#### 2, 创建样式与逻辑

现在,我们为前端页面添加一些样式与逻辑,这此的commit记录在,现在我们需要为登录界面添加逻辑与路由保护。

登录页面样式

登录可以提交用户名与密码用作验证,这时候可以借助Angular的模板语法来快速的完成它们:

我们希望所有严格的逻辑或涉及数据库的问题都放在主进程解决,那么确认登录需要与electron主进程进行交互,以便于主进程来切换窗口。当然,在实际业务中你可以选择把服务器的交互放在Angular中来做,也可以在electron发起一个request。现在我们按下面几步来操作:

  1. 在login组件文件夹下创建login.service.ts,别忘了将服务添加到组件的providers依赖项中!

  2. src/index.html文件中添加var electron = require('electron'),别忘了script标签。

  3. src/app下添加shared文件夹,用来存放一些共用的组件与逻辑。在这里创建一个名为ipc-renderer的服务,并将它注册到app.component.ts中。具体代码如下:

import {Injectable} from '@angular/core        declare let electron:any;        @Injectable()        export class IpcRendererService {                constructor (){}                                private ipcRenderer = electron.ipcRenderer                on (message: string, done){                        return this.ipcRenderer.on(message, done);                }                send (message: string, ...args){                        this.ipcRenderer.send(message, args);                }                api (action: string, ...args) {                        this.ipcRenderer.send('api', action, ...args);                        return new Promise((resolve, reject) => {                                    this.ipcRenderer.once(`${action}reply`, (e, reply, status) =>{                                            if (!reply){                                                    return reject(status)                                            }                                            return resolve(reply)                                    })                            })                }                dialog (action: string, ...args) {                        this.ipcRenderer.send('dialog', action, ...args);                }                sendSync (message: string, ...args){                        return this.ipcRenderer.sendSync(message, arguments);                }        }

这里我们通过ipcRenderer与electron交互,ipc-renderer就是Angular中用来通信的公共服务,这个服务模块理论上共享的,而且我们也只希望它被实例化一次,所以将它注入在app.component.ts中。这样每次子组件需要服务时不必在providers中标明它,而是直接在constructor中注入即可。这很重要,特别是你想要在一个类中保存一些即时的数据信息,希望只存在一个实例用来共享时很有用。

可以看出来,api这个方法是我们增加的一个有意思的方法,这里我们可以作出一些参数上的约定,便于监听事件时做出更好的反馈。

#### 3, 监听与反馈

这时,api的第一个参数被约定为action,用于描述这个API事件的用途,每一个API事件都会发起一次apiName+reply的事件用于回复。在Angular的公共服务中,我们不妨先把它转化为我们熟悉的Promise,再返回给每一个具体的组件服务,当然你也可以直接把它用作做fromEvent的Observable,但在这里,我们希望它看起来像是一个http服务,便于大家更好的理解它们工作的方式。

实际上,你可以选择一些成熟electron数据通信库或框架来解决这些复杂的问题,但在第一次请不要这样做,这就像上手使用Rails一样,虽然做的很快,但对你并没有多少益处。

这里有一些复杂,如果你希望对照当时的代码来学习,可以看这一次的。

ok,大家也可以想象的到,现在要做的是在electron中新建一个事件接收器,处理一些逻辑并且将它们返回,在根文件夹下新建browser/ipc/index.js并且填充基础的代码:

const {ipcMain} = require('electron')const api = require('./api')ipcMain.on('api', (event, actionName, ...args) =>{    const reply = (replayObj, status = 'success') =>{        event.sender.send(`${actionName}reply`, replayObj, status);    }    if (api[actionName]){        api[actionName](Object.assign({reply: reply}, event), ...args)    }})

假设现在有一个browser/ipc/api文件作为处理器,以上代码做的事情即是确定一个Action,并且监听事件,为event合并一个名为reply的方法,用于返回数据。根据此,我们再创建这个虚拟的browser/ipc/api文件:

module.exports = {    login: (e, user) =>{        // todo something        e.reply({msg: 'ok'})    }}

怎么样?现在看起来一切都完成了!每次当我们在loginService中调用this.ipcRendererService.api时,相应的数据就会被传达至对应的事件(看起来它更像一个路由)上,我们在nodejs环境中做一些操作,比如储存session,更新数据库,抓取新闻,向远程服务器发送一条信息等等。

最关键的是我们也能用轻而易举的方式来得到想要的数据,回复数据也足够简单,e.reply({msg: 'ok'})就像是express中的res.xxx({});一样,整个项目也变得层次分明。等到有一天我们需要下载、上传、显示系统原生提示框、读取一个文件等等之类的功能时,只需要将Action名替换一下,在api文件夹下新增几段逻辑即可。

这一小节文章有些琐碎和复杂,登录成功与跳转等等逻辑不妨放在下一节中再讲。大家可以尝试阅读github的源码,考虑它有哪些问题是值得优化的。在后面几节中,我们再来讨论如何优化这些逻辑。

注:原文首次载于。需要演示项目请看

转载地址:http://afdox.baihongyu.com/

你可能感兴趣的文章
543
查看>>
thymeleaf标签
查看>>
linux 上做mysql的数据备份
查看>>
初来咋到,记个号。
查看>>
ASA防火墙17 验证防火墙连通性
查看>>
突破Linux内核模块校验机制(转)
查看>>
小结Servlet/JSP技术点
查看>>
JAVA经典算法40题(13)
查看>>
SpringBoot使用JDBC实现增删改查
查看>>
百度地图显示标记
查看>>
记一下好用的js(三)
查看>>
国内Maven私服仓库
查看>>
spring springmvc 头文件
查看>>
关于代码风格的一些见解
查看>>
find,vim的编程设置,SUID,SGID,SBIT,软.硬链接,重定向
查看>>
IPSEC ××× LAN-to-LAN
查看>>
第七章 获取时间日期格式和延时:date命令、sleep命令
查看>>
SVN 图标显示异常
查看>>
mysql字符集与校对规则---大小写敏感那点事
查看>>
CentOS Linux自动备份MySQL数据库到远程FTP服务器并删除指定日期前的备份Shell脚本(二)...
查看>>