纯前端实现保存表单数据功能

最近在用管理后台配置数据时,发现辛辛苦苦配置好的表单无缘无故地被覆盖,之后了解到由于我们都是在同一台开发机上做开发,难免会遇到其他同学做数据变更时覆盖掉自己的配置数据。于是我决定在表单配置里增加一项“配置操作”功能来解放自己双手以及惠及他人。

用什么方式保存?

  1. 找后端同学去帮忙做保存?
  2. 把配置数据都保存到 localStorage?
  3. 把配置数据都保存到本地文本?

然而看到后端同学繁忙的景象之下,默默地放弃了,所以忽略第一点。如果把数据都保存到 localStorage,那么我是不是还要做一个界面来管理这个配置数据的版本呢,而且还可以选中某个版本快速还原,但这些都需要一定的工作量,localStorage 的数据也不方便导出给别的同学。如果我只用前端技术直接把配置文件保存到本地,那前面两个问题都不存在了,还会带来一个好处就是:拿到这些文件,发布到现网时我可以直接导入,而后端同学只需要运行创建表文件和上传相关的java文件就足够了,减少后端同学的工作量。

实现方式

回想时以前做过的一个需求:当用户点击链接时是下载一个PDF文件,而不是直接使用自带的PDF阅读器打开。

使用a标签的download属性(Chrome和FF已支持),如:

<a href="/uploadfolder/lalala.pdf" download="filename.pdf">点击下载</a>

这明显需要服务器来支持。我们知道href属性可以是一个链接,也可以是一个锚点、伪协议。

但也可以是blobURI、dataURI、fileURI

如果要实现前端保存文本,那么使用dataURI即可实现。 dataURI 的语法结构如下:

data:[<mediatype>][;base64],<data>

如果想了解更多关于data URI的知识,可以点击这里

下载指定的文本的需求就可以快速实现了:

<a href="data:text/plain,text_text_text_text" download="filename.txt">点击下载</a>

如果想通过调用的方式来触发,需要稍微封装:

function downloadText (fileName, textCtx) {
    var aTag;

    if (fileName && textCtx) {
        aTag = document.createElement('a');
        aTag.setAttribute('href', 'data:text/plain,' + textCtx);
        aTag.setAttribute('download',  fileName);
        aTag.click();
    }
}

虽然管理后台不需要支持IE,但这里顺便扯一下IE,IE要达到该效果需要借助execCommand

基本思路是创建一个隐藏iframe并添加到页面上,把需要保存的内容写到iframe内并调起iframe的execCommand命令来保存页面。

var ifr = document.createElement('iframe');

ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.contentWindow.(textCtx);
ifr.contentWindow.document.execCommand('SaveAs', false, fileName);
document.body.removeChild(ifr);

如果你想了解更多关于IE下的execCommand,可以点击这里

整理上述:

function downloadText (fileName, textCtx) {
    var aTag, ifr;

    if (fileName && textCtx) {
        if (!~navigator.userAgent.indexOf('MSIE')) {
            aTag = document.createElement('a');
            aTag.setAttribute('href', 'data:text/plain,' + textCtx);
            aTag.setAttribute('download',  fileName);
            aTag.click();
        } else {
            ifr = document.createElement('iframe');
            ifr.style.display = 'none';
            document.body.appendChild(ifr);
            ifr.contentWindow.(textCtx);
            ifr.contentWindow.document.execCommand('SaveAs', false, fileName);
            document.body.removeChild(ifr);
        }
    }
}
//let's go 
downloadText('lala.txt', 'dolemifaso');

关于读取

使用FileReader可以快速实现,基本思路是 input -> onchange -> new FileReader() -> onload -> readAsDataURL

关于FileRader和相关例子, 可以点击这里

关于发送

这里使用了FormDataXMLHttpRequest 如果你想了解更多关于FormData, 可以点击这里

function postForm (formData) {
    var separator, postTarget, rKV, oForm, oReq, keyVals;

    if (formData && ~formData.indexOf(separator)) {
        separator = '____|____'; //每个字段的链接符,如: 'a:1____|____b:{"c":"d"}'
        postTarget = 'http://test.com/update.do';
        rKV = /([^:]+):([\s\S]+)/;
        oForm = new FormData();
        oReq = new XMLHttpRequest();
        keyVals = formData.split(separator); //切割为 [a:1, b:{"c":"d"}]

        keyVals.forEach(function (keyVal) {
                var pairs = keyVal.match(rKV);

                if (pairs) {
                    oForm.append(pairs[1], pairs[2]);
                }
        });

        //here we go
        oReq.open("POST", postTarget);
        oReq.send(oForm);
        oReq.onload = function (data) {
            var resText, resData;

            if (data.target.status === 200
                && data.target.readyState === 4) {
                resText = data.target.responseText;
                resData = JSON.parse(resText);

                if (resData.retcode === 0) {
                    alert('导入配置成功');
                } else {
                    alert('导入配置失败:' + resText);
                }
            }
        }
    } else {
        alert('文件格式不合法');
    }
}

全文完

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏每日一篇技术文章

VR+全景播放器+头控讲解-07

983
来自专栏小狼的世界

Kubernetes基础:编排调度的那些Controllers

Kubernetes提供了很多Controller资源来管理、调度Pod,包括Replication Controller、ReplicaSet、Deploym...

422
来自专栏前端吧啦吧啦

手把手教你全家桶之React(二)

913
来自专栏扎心了老铁

redis的sentinel主从切换(failover)与Jedis线程池自动重连

本文介绍如何通过sentinel监控redis主从集群,并通过jedis自动切换ip和端口。 1、配置redis主从实例 10.93.21.21:6379 10...

3766
来自专栏iOS 开发杂谈

浅谈 LinkMap 检查每个类占用大小

LinkMap 文件是 Xcode 产生可执行文件的同时生成的链接信息,用来描述可执行文件的构造成分,包括代码段 __TEXT 和数据段 __DATA 的分布情...

794
来自专栏运维小白

10.3 top命令

监控系统状态 top 命令,查看进程使用资源情况 top -c 显示详细的进程信息 top -bn1 静态显示所有进程 q 键退出,数字1显示所有核cpu,大写...

1655
来自专栏Gaussic

使用IntelliJ IDEA开发SpringMVC网站(三)数据库配置 顶

访问GitHub下载最新源码:https://github.com/gaussic/SpringMVCDemo

492
来自专栏用户2442861的专栏

使用IntelliJ IDEA开发SpringMVC网站(四)用户管理

转载请注明出处:Gaussic(一个致力于AI研究却不得不兼顾项目的研究生) 。

491
来自专栏Linux驱动

第2阶段——编写uboot之编译测试以及改进(3)

编译测试: 1.将写好的uboot复制到linux下面 2.make编译,然后将错误的地方修改,生成boot.bin (编译出错的解决方案:http://www...

1785
来自专栏C#

最好的.NET开源免费ZIP库DotNetZip(.NET组件介绍之三)

   在项目开发中,除了对数据的展示更多的就是对文件的相关操作,例如文件的创建和删除,以及文件的压缩和解压。文件压缩的好处有很多,主要就是在文件传输的方面,文件...

2517

扫码关注云+社区