Hey , you !


  • Home

  • Archives

Vue Form Input Bindings Fail?

Posted on 2018-10-04 | In vue

Preface

When I was using form validate in Vue, I found sometimes vue doesn’t render data which was modified by me. I even thought it was a bug. Anyway, let’s take a look.

Main

Here is a simple demo:

html,
body {
width: 100%;
height: 100%;
}

.app {
width: 100%;
height: 100%;
}
<div class="app" id="app">
<form>
<input
type="text"
id="positive-num"
:value.trim="positiveNum"
@input="oldValidate"
:placeholder="positiveNumTip"
/>
<p>{{positiveNumResTip}}{{positiveNum}}</p>
</form>
</div>
let app = new Vue({
el: "#app",
data: {
positiveNumTip: "please enter a positive num",
positiveNum: "",
positiveNumResTip: "validated and modified result: "
},
methods: {
oldValidate(event) {
let value = event.target.value;
let reg = /^[\d]+[.]?[\d]*$/;
let newVal = Number.parseFloat(value);
if (!value.match(reg)) {
if (!isNaN(newVal) || newVal > 0) {
this.positiveNum = newVal;
} else {
this.positiveNum = 1;
}
} else {
this.positiveNum = value;
}
}
}
});

When I was pressing dddddddddd, what did I got?

截图20181004204524.png

The first letter was replaced to 1 but the others not. And the most important is the result I got is always 1 which means this.positiveNum is 1 while the value in the input is not 1.

Why?

I even thought it was a bug until one day I met a similar problem which was solved in SO.

The key is the Lifecycle. The guide mentions it before but I didn’t understand it until now. Let’s see the picture again:

截图20181004205221.png

See?

The first time we change positiveNum to 1 and then we always change positiveNum to 1. So, vue wouldn’t re-render because the data doesn’t change. So, the connection between input and positiveNum was cut off until positiveNum isn’t equal to 1.

We can add updated to see how many times data has changed:

let app = new Vue({
el: "#app",
data: {
positiveNumTip: "please enter a positive num",
positiveNum: "",
positiveNumResTip: "validated and modified result: "
},
methods: {
oldValidate(event) {
let value = event.target.value;
let reg = /^[\d]+[.]?[\d]*$/;
let newVal = Number.parseFloat(value);
if (!value.match(reg)) {
if (!isNaN(newVal) || newVal > 0) {
this.positiveNum = newVal;
} else {
this.positiveNum = 1;
}
} else {
this.positiveNum = value;
}
}
},
updated() {
console.log("data updated"); //only triggered once
}
});

As explained before, you can only see 'data updated' once.

So, how can we solved this problem?

The key is still the Lifecycle. Vue wouldn’t re-render because data doesn’t change. So, we can update data after data has been changed and rendered. Understood? See code below:

<div class="app" id="app">
<form>
<input
type="text"
id="positive-num"
:value.trim="positiveNum"
@input="newValidate"
:placeholder="positiveNumTip"
/>
<p>{{positiveNumResTip}}{{positiveNum}}</p>
</form>
</div>
let app = new Vue({
el: "#app",
data: {
positiveNumTip: "please enter a positive num",
positiveNum: "",
positiveNumResTip: "validated and modified result: "
},
methods: {
oldValidate(event) {
let value = event.target.value;
let reg = /^[\d]+[.]?[\d]*$/;
let newVal = Number.parseFloat(value);
if (!value.match(reg)) {
if (!isNaN(newVal) || newVal > 0) {
this.positiveNum = newVal;
} else {
this.positiveNum = 1;
}
} else {
this.positiveNum = value;
}
},
newValidate(event) {
let value = event.target.value;
let reg = /^[\d]+[.]?[\d]*$/;
this.positiveNum = value;
this.$nextTick(() => {
if (!this.positiveNum.match(reg)) {
let newVal = Number.parseFloat(this.positiveNum);
if (!isNaN(newVal) || newVal > 0) {
this.positiveNum = newVal;
} else {
this.positiveNum = ""; //for better use I changed the wrong value to ''
}
}
});
}
},
updated() {
console.log("data updated");
}
});

See? I move the origin logic to the this.$nextTick(callback). Every time you press the wrong button, it will pass the wrong value to positiveNum and will be corrected in this.$nextTick(callback) which will make the logic run correctly. Also, you can see the updated log at the console.

Ending

Reference

  • change child-component checked state from parent-component synchronously fail

Better Way to Use orientationchange Event on Mobile

Posted on 2018-10-04 | In js

Preface

When I was using orientationchange event, I met a few bugs. So, I take it down.

Main

compatibility problem

When I was testing my code on my android, it was ok. However, it doesn’t work on my boss’s iPhone6. So, i have to change the code.

The origin code was like:

<div class="box" id="box">
html,
body {
width: 100%;
height: 100%;
}

.box {
width: 100%;
height: 100%;
background: pink;
}

.box.landscape {
background: lightblue;
}
let box = document.getElementById('box')
window.addEventListener('orientationchange', orientationChangeCb)
function orientationChangeCb(event) {
let isLand = isLandscape()
if (isLand) {
box.classList.add('landscape')
} else {
box.classList.remove('landscape')
}
}
function isLandscape() {
if ('orientation' in window) {
return Math.abs(window.orientation) === 90
} else {
return window.innerWidth > window.innerHeight
}
}

To be compatible with iPhone6, I use resize event instead.

However, the better way I think is :

let eventForOrientationChange =
'onorientationchange' in window ? 'orientationchange' : 'resize'
if (isMobile()) {
window.addEventListener(eventForOrientationChange, orientationChangeCb)
}

isMobile ?

Because onorientationchange is a mobile event, so if you try to run code below on your computer with chrome, you will get:

window.onorientationchange //undefined
'onorientationchange' in window //false

It seems a little weird but it’s true until chrome 69. That’s why I add isMobile() before I use window.addEventListener. In that case, we don’t have to listen for the resize event on desktop.

window.orientation or screen.orientation

According to mdn, window.orientation has been Deprecated. However, similar API screen.orientation has a big problem for compatibility. Safari and QQ doesn’t support. As of 2018.10.4, global support in caniuse is only 72.5%.

css only

If you just want to update style, you don’t have to use code above. CSS media queries
support code like:

@media (min-height: 680px), screen and (orientation: portrait) {
/* ...; */
}
@media (min-height: 680px), screen and (orientation: landscape) {
/* ...; */
}

Ending

Reference

  • detect viewport orientation, if orientation is portrait display alert message advising user of instructions

X-UA-Compatible and ie=edge

Posted on 2018-10-03 | In html

Preface

平时会用 vue 写新项目,老项目就在原有基础上更新。对于 vue 这种框架,使用官方的脚手架通常就避免了很多问题,就像平时用模板创建新的单页一样。

然而有时总是会遇到些不按模板走的代码,虽然跑起来也没有问题,但是放到有些浏览器上就有 bug 了,这个时候对既有模板的理解和掌握就很重要了。

Main

当我用 html 模板创建一个新单页时,拿到的页面是这样的,vue 也是类似,至少三个 meta 标签基本都是一样的:

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>

<body>
</body>

</html>

viewport 是在兼容移动端时才了解的内容,费了不少功夫。而 X-UA-Compatible 则是在遇到非常规代码的时候才想起来的。有一回改个老项目,用了 transform,在 ie11 上测试,没有用,而且在它的工具栏里样式表里根本看不到我写的代码,这个时候我就好奇了,这是 ie11 啊,怎么会不支持 transform 呢?然后我瞄了一下开发者工具,大概是这样的:

截图20181003201618.png

然后我就好奇了,为什么会是 ie7 模式呢?我明明装的是 ie11 浏览器啊,然后脑袋一闪,好像明白了什么,看了看 html ,果然没有:

<meta http-equiv="X-UA-Compatible" content="ie=edge">

加上之后,再刷新,网页就正常了。

在这件事之前,我知道这个东西,但是因为是用的模板,一直没有遇到过这个问题,而且通常来说,我会以为,我既然是在 ie11 里打开的,默认你也没有理由用 ie11- 的文档模式去渲染啊,结果就错了。后来看到 SO 上高票答主大概是这么解释 ie 的行为的:

ie 会用它认为最好的方式去渲染页面,如果没有上面那行代码的话

此外,ie11 已经开始废弃上面那个了,如果不兼容 ie 的话,其实上面的代码也可以不用写了,不过目前为止 html 模板和 vue 的模板都还是默认支持的。而上面的那行代码实际意思呢,就是:

Edge:始终以最新的文档模式来渲染页面。忽略文档类型声明。对于 IE8,始终保持以 IE8 标准模式渲染页面。对于 IE9,则以 IE9 标准模式渲染页面。

当然 ie 还可以等于其他值,不过其他值大多都是老版本,目前而言,都没有必要去纠结了,譬如:

  • “IE=edge”
  • “IE=11”
  • “IE=EmulateIE11”
  • “IE=10”
  • “IE=EmulateIE10”
  • “IE=9”
  • “IE=EmulateIE9
  • …

Ending

Reference

  • What does <meta http-equiv=“X-UA-Compatible” content=“IE=edge”> do?
1…101112…18

xianshenglu

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