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

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

用什么方式保存?

  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 条评论
登录 后参与评论

相关文章

来自专栏大数据

Scrapy入门

Scrapy是一个基于Python的网络爬虫,可以用来从网站提取信息。它快速简单,可以像浏览器一样浏览页面。

2141
来自专栏从零开始学自动化测试

pytest文档15-使用自定义标记mark

pytest可以支持自定义标记,自定义标记可以把一个web项目划分多个模块,然后指定模块名称执行。app自动化的时候,如果想android和ios公用一套代码时...

482
来自专栏小特工作室

基于Cef内核的多店铺登录器(含源码)

        公司是做电商的,在速卖通平台上开了若干店铺,每天都需要登录店铺打理,如:发货提交、获取运单号等。多个店铺的情况下,同时使用浏览器就会非常繁琐,如...

24410
来自专栏企鹅号快讯

优化Profiler中Others的耗时……

我们将从日常技术交流中精选若干个开发相关的问题,建议阅读时间15分钟,认真读完必有收获。如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。 内存 Q:我的...

1989
来自专栏.NET开发者社区

一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar](九)

前言 童鞋们,大家好 我是专注.NET开发者社区建设的实践者Rector。 首先,为自己间隔了两个星期五再更新本系列文章找个不充分的理由:Rector最近工作,...

2435
来自专栏一个小程序员的成长笔记

常用js,css文件统一加载方法,并在加载之后调用回调函数

为了方便资源管理和提升工作效率,常用的js和css文件的加载应该放在一个统一文件里面完成,也方便后续的资源维护。所以我用js写了以下方法,存放在“sourceC...

3097
来自专栏夏时

使用PHP抓取Bing每日图像并为己所用

1133
来自专栏葡萄城控件技术团队

ActiveReports 报表应用教程 (8)---交互式报表之动态过滤

用户可以使用葡萄城ActiveReports报表参数 (Parameters)集合把数据提供给报表中的文本框或图表,也可以选择数据的一个子集显示到报表的特定区域...

1628
来自专栏owent

初识Rust

虽然我主要使用C++,但是最近也想学点现代化的新语言。初步想的是从golang和Rust里先选一个。

1034
来自专栏dotnet core相关

WCF 入门 (21)

其实不太了解为什么第21集才讲这个Binding,下面都是一些概念性的东西,不过作为一个入门视频,了解一下也无妨吧。

725

扫码关注云+社区