Hey , you !


  • Home

  • Archives

arrayBuffer, text, and Blob Transformation in Ajax

Posted on 2019-09-04 | In js

In some cases, we might need to fetch and handle some unusual data from a server, for example, Excel file, arrayBuffer, etc. If the backend has a united error handler things can be more complicated. In that case, the response got from backend can be a json, arrayBuffer or other types. For example,

  • Normally the frontend would get a Excel file
  • If some error happens in the backend or formData is not valid or the session expired, the backend would return a json like
{
message: "unknown error!",
status: 500
};

So, how can we handle the unknown response type in ajax?

fetch

If we are using fetch, the code can be like

import { saveAs } from "file-saver";

fetch("http://www.your-api.com/getExcel", {
credentials: "include",
headers: {
accept: "application/json, text/plain, */*",
"content-type": "application/json;charset=UTF-8"
},
referrer: "",
referrerPolicy: "no-referrer-when-downgrade",
body: "{}",
method: "POST",
mode: "cors"
})
.then(response => {
return response
.clone()
.json()
.catch(er => {
return response.blob();
});
})
.then(response => {
if ({}.toString.call(response) !== "[object Blob]") {
return alert(response.message);
}
return saveAs(
new Blob([response], {
type:
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"
}),
"file.xls"
);
});

The key is the Response api which can help us transform unknown data to json or blob or other types. However, you might need to take care of the clone api if you want to use the Response more than once.

XMLHttpRequest

If you are using xhr(i.e. XMLHttpRequest), you have to set the response type before sending the request. For example,

const xhr = new XMLHttpRequest();
xhr.open("POST", "http://www.your-api.com/getExcel");
xhr.responseType = "blob";
xhr.withCredentials = true;
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = e => {
if (xhr.readyState === 4) {
console.log(xhr.response);
}
};
xhr.send('{"truck_team_id":[]}');

And the type of xhr.response is decided by xhr.responseType which means we will get a blob object with xhr.responseType = "blob" even if the server returns json. In this case, we might need to know some ways to transform data between blob, text, arrayBuffer, etc.

Luckily, the Blob api gives us many choices. The transformation between arrayBuffer, blob and text can be

// arrayBuffer => text;

new Blob([arrayBuffer]).text(result => {
console.log(result); //text
});
// text => arrayBuffer;

new Blob([text]).arrayBuffer(result => {
console.log(result); // arrayBuffer
});

And if not using Blob, we can still use TextDecoder and TextEncoder to do the transformation job between arrayBuffer and text.

// arrayBuffer to text
new TextDecoder().decode(arrayBuffer);
// text to arrayBuffer
new TextEncoder().encode(text);

Universality

Considering the universality, the transformation between arrayBuffer, blob and text can be useful in many scenarios. Worthy of attention!

Issue

Source

Reference

Associated Validation in Vue

Posted on 2019-08-27 | In vue

Scenario

Let’s see this gif:

And collapsed code:

Click here to expand code
<script src="//unpkg.com/vue/dist/vue.js"></script>
<script src="//unpkg.com/element-ui@2.11.0/lib/index.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<div id="app">
<el-form :inline="true" ref="form" :rules="rules" :model="formData">
<el-form-item label="startTime:" prop="startTime">
<el-date-picker
v-model="formData.startTime"
type="datetime"
placeholder="choose startTime"
value-format="yyyy-MM-dd HH:mm:ss"
></el-date-picker>
</el-form-item>
<el-form-item label="endTime:" prop="endTime">
<el-date-picker
v-model="formData.endTime"
type="datetime"
placeholder="choose endTime"
value-format="yyyy-MM-dd HH:mm:ss"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">submit</el-button>
</el-form-item>
</el-form>
</div>
@import url("//unpkg.com/element-ui@2.11.0/lib/theme-chalk/index.css");
var Main = {
data() {
return {
formData: {
startTime: "",
endTime: ""
},
rules: {
startTime: [{ validator: this.startEndTimeValidator }],
endTime: [{ validator: this.startEndTimeValidator }]
}
};
},
watch: {
// "formData.startTime"() {
// this.validateField("endTime");
// },
// "formData.endTime"() {
// this.validateField("startTime");
// }
},
methods: {
startEndTimeValidator(rule, value, callback) {
let {
formData: { startTime, endTime }
} = this;
startTime = moment(startTime, "YYYY-MM-DD HH:mm:ss");
endTime = moment(endTime, "YYYY-MM-DD HH:mm:ss");
if (startTime > endTime) {
return callback(
new Error(`start time shouldn't be larger than end time!`)
);
}
callback();
},
validateField(...args) {
if (typeof this.$refs.form === "undefined") {
return;
}
this.$refs.form.validateField(...args);
},
onSubmit() {
console.log("submit!");
}
}
};
var Ctor = Vue.extend(Main);
new Ctor().$mount("#app");

When user selected a smaller endTime, we show the message:

start time shouldn’t be larger than end time!

If user changes the endTime with a larger one everything is okay. However, user could also choose a smaller startTime to avoid this message.

But the behavior of changing startTime wouldn’t trigger the endTime validation. So, user would still see the error message though he did the right thing.

That’s what we are going to talk about:

Associated Validation.

Implementation

At the very beginning, I have tried adding similar logic in the validator. However, it fails because of dead loop.

To avoid dead loop, I add this

var Main = {
// ...
watch: {
"formData.startTime"() {
this.validateField("endTime");
},
"formData.endTime"() {
this.validateField("startTime");
}
}
// ...
};

Em, not so good but works.

Issue

Source

Reference

How to Prevent Collapse of Empty Rows in HTML table

Posted on 2019-08-27 | In js

If you have met empty rows in HTML table or have seen this question

Prevent collapse of empty rows in HTML table via CSS

you might know what I am saying. See this image:

Above answers can’t handle strings like " ". In this case, we still need the help of js. Just like the idea of css,

// prevent empty table rows collapse
.cell:empty:after {
content: "\00a0";
}

We can write a function like

function falsyToNbsp(value, falsyValues = [null, undefined, "", NaN]) {
value = typeof value === "string" ? value.trim() : value;
return falsyValues.includes(value) ? "\u00a0" : value;
}

You can use it in vue filters like

import { falsyToNbsp } from "utils";
export default {
// ...
filters: {
falsyToNbsp
}
// ...
};

or with other libs.

Issue

Source

Reference

1234…18

xianshenglu

54 posts
14 categories
142 tags
© 2023 xianshenglu
Powered by Hexo
|
Theme — NexT.Muse v5.1.4