Skip to content

Commit

Permalink
Fix partial-numeric string prop parsing
Browse files Browse the repository at this point in the history
Internally Vue was using `parseFloat` for this,
  and `parseFloat('1abc') === 1` rather than `NaN` like it is for
  non-numeric strings

The fix was lifted from vuejs/core#4393,
  so I'll double check whether that's been merged by the time
  the rest of this is ready
  • Loading branch information
alexcs-bsft committed Aug 26, 2021
1 parent 867900d commit e1a338e
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 28 deletions.
2 changes: 2 additions & 0 deletions demo-app/_vue-components/index.js
@@ -1,10 +1,12 @@
import { defineNatively } from '@blueshift/ng-interop';

import PropTester from './prop-tester.vue';
import UserItem from './user-item.vue';
import UserList from './user-list.vue';


defineNatively([
PropTester,
UserItem,
UserList,
]);
64 changes: 64 additions & 0 deletions demo-app/_vue-components/prop-tester.vue
@@ -0,0 +1,64 @@
<script>
export default {
name: 'prop-tester',
props: {
title: {
type: String,
},
likes: {
type: Number,
},
isPublished: {
type: Boolean,
},
commentIds: {
type: Array,
},
author: {
type: Object,
},
callback: {
type: Function,
},
promise: {
type: Promise,
}, // or any other constructor
},
computed: {
propsJSON() {
const props = {
title: this.title,
likes: this.likes,
isPublished: this.isPublished,
commentIds: this.commentIds,
author: this.author,
callback: this.callback,
promise: this.promise,
};
console.debug('Props changed:', props);
return JSON.stringify(props, null, 2);
},
},
};
</script>

<template>
<section>
<h2 class="vue-heading">Props Testing</h2>
<pre class="props-tester" v-html="propsJSON" />
</section>
</template>

<style scoped>
.props-tester {
border: solid 1px #AB47bc;
background-color: #292D3E;
color: #A6ACCD;
font-family: 'Fira Code Retina', 'Source Code Pro', monospace;
font-size: 16px;
border-radius: .25em;
padding: .5em;
margin: 0;
}
</style>
42 changes: 24 additions & 18 deletions demo-app/_vue-components/user-list.vue
@@ -1,28 +1,34 @@
<template>
<section>
<h2 style="border-left: 4px solid var(--c-vue); padding-left: 8px;">
<section style="display: grid; grid-template-columns: 1fr 1fr; align-content: center;">
<h2 class="vue-heading">
{{ headingText }}
<button @click="fetchUsers" :disabled="loading">Fetch Users</button>
</h2>
<button
style="place-self: baseline left;"
@click="fetchUsers"
:disabled="loading"
>Fetch Users</button>
<div>
<user-item
v-if="selectedUser"
:user="selectedUser"
selected
unselectable
></user-item>
<ul>
<li v-for="user in _users">
<user-item
:user="user"
:selected="user.id === selectedId"
@selectMe="onSelect"
></user-item>
</li>
</ul>
</div>
<pre>
aNumber: {{ aNumber+1 }}
aBoolean: {{ aBoolean }}
</pre>
<user-item
v-if="selectedUser"
:user="selectedUser"
selected
unselectable
></user-item>
<ul>
<li v-for="user in _users">
<user-item
:user="user"
:selected="user.id === selectedId"
@selectMe="onSelect"
></user-item>
</li>
</ul>
</section>
</template>

Expand Down
21 changes: 18 additions & 3 deletions demo-app/home/home.controller.js
Expand Up @@ -4,11 +4,26 @@ class HomeCtrl {
'UserService',
];

name = 'AngularJS inside ng-wrapper vue component';
users = [];
selectedId = null;
testProps = {
title: '1string',
likes: 4,
is_published: true,
comment_ids: ['a1', '2b', 'c3'],
author: {
name: 'Alex',
published: true,
post_count: 10,
},
callback: function testCb() {},
promise: Promise.resolve(10),
};


constructor($scope, UserService) {
this.name = 'AngularJS';
this.UserService = UserService;
this.users = [];
this.selectedId = null;
this.$scope = $scope;
}

Expand Down
13 changes: 11 additions & 2 deletions demo-app/home/home.html
Expand Up @@ -7,7 +7,7 @@ <h1>Hello {{ $ctrl.name }}!</h1>
</p>
<div class="list-container">
<section class="ng1">
<h2 style="border-left: 4px solid var(--c-ng1); padding-left: 8px;">
<h2 class="vue-heading">
AngularJS (control)
</h2>
<user-item
Expand Down Expand Up @@ -50,10 +50,19 @@ <h2 style="border-left: 4px solid var(--c-vue); padding-left: 8px;">
</ul>
</section>
<v-user-list
style="grid-area: 2 / 1;"
style="grid-area: 2 / span 2;"
ng-prop-heading_text="'Vue Component using DI'"
ng-prop-a_boolean="true"
ng-prop-a_number="1"
ng-prop-users="$ctrl.users"
></v-user-list>
<v-prop-tester
ng-prop-title="$ctrl.testProps.title"
ng-prop-likes="$ctrl.testProps.likes"
ng-prop-is_published="$ctrl.testProps.is_published"
ng-prop-comment_ids="$ctrl.testProps.comment_ids"
ng-prop-author="$ctrl.testProps.author"
ng-prop-callback="$ctrl.testProps.callback"
ng-prop-promise="$ctrl.testProps.promise"
></v-prop-tester>
</div>
15 changes: 10 additions & 5 deletions demo-app/style.css
@@ -1,23 +1,28 @@
:root {
--c-ng1: #B52E31;
--c-vue: #42b983;
}

h1,
p {
font-family: Lato;
font-family: 'Helvetica Neue', Arial, sans-serif;
}

#app {
width: 800px;
max-width: 100vw;
margin: 0 auto;
}
h2 {
margin-top: 0;
}

.list-container {
display: grid;
width: 100%;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
}

.vue-heading {
border-left: 4px solid var(--c-vue);
padding-left: 8px;
}

.user {
Expand Down
16 changes: 16 additions & 0 deletions lib/vue-wrapper/index.js
Expand Up @@ -378,6 +378,10 @@ export function defineGuardedCustomElement(options) {
})
}

_setAttr(key) {
this._setProp(camelize(key), parseNumber(this.getAttribute(key)), false);
}

_setProp(key, val, shouldReflect = true) {
if (key?.startsWith('ng')) {
// Skip any attributes that start with `ng-`
Expand Down Expand Up @@ -479,3 +483,15 @@ export function defineGuardedCustomElement(options) {

return customElementClass;
}

/**
* @TODO: this can just be imported from the library once https://github.com/vuejs/vue-next/pull/4393 is merged
* @param {string|null} value
* @return {string|number}
*/
export function parseNumber(value) {
// for Number('') and Number(null) as they both become 0
if (!value) return '';
const casted = Number(value);
return value === 'NaN' || !Number.isNaN(casted) ? casted : value;
}

0 comments on commit e1a338e

Please sign in to comment.