#19 Creating A List View Component Using TSX
Sunday, October 13, 2019
In this article we will focus on creating a new list view component that we can use when we want to display a list of data in our application. Using a new component will make it easier to make sure that we create a unified look and feel throughout our entire application. Now we could already do that using pug and sass but the component will also allow us to handle behavior. At least for now we will just make sure that our component can respond to the user clicking on an item. We will accomplish all of this in a very general way by using TSX and allowing for a render function to be passed into the list view for any item that we wish to display.
Parts
- Part 45: Adjusting Shares
- Part 44: Plan Percentages
- Part 43: Home Securities
- Part 42: Updating Plans
- Part 41: Plan Details View
- Part 40: Portfolio Getters
- Part 39: Portfolio Plan
- Part 38: Portfolio Category
- Part 37: Account Securities
- Part 36: Account Transfer
- Part 35: View Account Security
- Part 34: Updating Deposit
- Part 33: View Account Deposit
- Part 32: Display Account Details
- Part 31: Account Getters
- Part 30: Deposits And Securities
- Part 29: Add Accounts Details
- Part 28: Refactoring Accounts
- Part 27: Add Security Models
- Part 26: Edit Security Details
- Part 25: View Security Details
- Part 24: Navigating To Details
- Part 23: Getters Validation
- Part 22: Query Parameters
- Part 21: Tab Entries
- Part 20: Tab Header
- Part 19: List View
- Part 18: Vuex Getters
- Part 17: End Domain Model
- Part 16: Start Domain Model
- Part 15: Pop Routes
- Part 14: Push Routes
- Part 13: Removing Accounts
- Part 12: Vuex (Decorators)
- Part 11: Vuex (Accounts)
- Part 10: The App Bar (Settings)
- Part 9: Remove Consumer Rxjs
- Part 8: The App Bar (Back)
- Part 7: Structuring Our App
- Part 6: Animation Between Views
- Part 5: Navigation Fade
- Part 4: Navigation Requests
- Part 3: Fade Animations (cont.)
- Part 2: Fade Animations
- Part 1: Splash Screen
Code Snippets
security-category-model.ts (1:12)
...
export class SecurityCategoryModel {
...
public get abbreviation() {
const text = { text: "undefined undefined"};
const territory = (this.territory || text).text.charAt(0);
const type = (this.type || text).text.charAt(0);
const segment = (this.segment || text).text
.split(" ")
.reduce((prev, cur) => `${prev}${cur.charAt(0)}`, "");
return `${territory}${type}${segment}`;
}
...
}
ListView.vue (4:11)
<script lang="tsx">
import { Component, Vue } from "vue-property-decorator";
@Component
export default class ListView extends Vue {
private render() {
return (
<ul>
<li>List View</li>
</ul>
);
}
}
</script>
Securities.vue (5:01)
<script lang="ts">
...
import ListView from "@/components/ListView.vue";
@Component({
components: {
ListView,
},
})
export default class Securities extends Vue {
...
}
</script>
Securities.vue (5:27)
<template lang="pug">
ListView/
</template>
ListView.vue (5:42)
<script lang="tsx">
import { ... Prop, ... } from "vue-property-decorator";
@Component
export default class ListView extends Vue {
@Prop() private readonly items!: any[];
@Prop() private readonly keyFn!: (item: any) => number;
@Prop() private readonly onClick!: (item: any) => void;
@Prop() private readonly renderFn!: (item: any) => JSX.Element;
private idFn(item: any) {
return typeof (this.keyFn) !== "undefined"
? this.keyFn(item)
: item.id;
}
private render() {
if (typeof(this.items) === "undefined") {
return;
}
return (
<ul>
{this.items.map((x) => {
return <li key={this.idFn(x)}>
<a class="list-item-content" href="#void" onClick={() => this.onClick(x)}>
<div class="list-item-text">{this.renderFn(x)}</div>
<span>〉</span>
</a>
</li>
})}
</ul>
);
}
}
</script>
Securities.vue (11:56)
<script lang="tsx">
...
export default class Securities extends Vue {
...
private onClickCategory(category: SecurityCategoryModel) {
console.log(category);
}
private onClickSecurity(security: SecurityModel) {
console.log(security);
}
private renderFnCategory(category: SecurityCategoryModel) {
return (
<div>
<label>{category.text}</label>
<small>{category.abbreviation}</small>
</div>
);
}
private renderFnSecurity(security: SecurityModel) {
return (
<div>
<label>{security.symbol}</label>
<small>{security.category!.abbreviation}</small>
</div>
);
}
}
</script>
Securities.vue (14:35)
<template lang="pug">
div
ListView(
v-bind:items="securities"
v-bind:onClick="onClickSecurity"
v-bind:renderFn="renderFnSecurity")/
ListView(
v-bind:items="categories"
v-bind:onClick="onClickCategory"
v-bind:renderFn="renderFnCategory")/
</template>
Advertisement
ListView.vue (16:45)
<style lang="sass" scoped>
@import "../bitters/variables"
.list-item-content
display: flex
align-items: center
padding: 0.75rem 0.5rem
text-decoration: none
border-bottom: 1px solid $light-gray
> *
pointer-events: none
.list-item-text
flex: 1
</style>
index.ts (18:56)
...
export * from "@/store/security-descriptor";
...
export * from "@/store/security-types";
...
Securities.vue (19:31)
<script lang="tsx">
...
import {
...
ISecurityMarketsModelState,
...
SecurityDescriptor,
...
STATE_SECURITY_MARKETS,
State,
} from "@/store";
enum Descriptors {
Markets,
}
...
export default class Securities extends Vue {
...
@State(STATE_SECURITY_MARKETS) private readonly marketState!: ISecurityMarketsModelState;
private readonly descriptorMarkets = Descriptors.Markets;
private get markets() { return this.marketState.items; }
...
private onClickDescriptorFactory(which: Descriptors) {
return (descriptor: SecurityDescriptor) => {
this.onClickDescriptor(which, descriptor)
}
}
private onClickDescriptor(which: Descriptors, descriptor: SecurityDescriptor) {
console.log(`${Descriptors[which]} ${descriptor.id}`);
}
...
private renderFnDescriptor(descriptor: SecurityDescriptor) {
return (
<div>
<label>{descriptor.text}</label>
</div>
);
}
...
}
</script>
Securities.vue (24:14)
<template lang="pug">
div
...
ListView(
v-bind:items="markets"
v-bind:onClick="onClickDescriptorFactory(descriptorMarkets)"
v-bind:renderFn="renderFnDescriptor")/
</template>
Securities.vue (25:02)
<script lang="tsx">
...
import {
...
ISecuritySegmentModelState,
ISecurityTerritoryModelState,
ISecurityTypeModelState,
...
STATE_SECURITY_SEGMENTS,
STATE_SECURITY_TERRITORIES,
STATE_SECURITY_TYPES,
...
} from "@/store";
enum Descriptors {
...
Segments,
Territories,
Types,
}
...
export default class Securities extends Vue {
...
@State(STATE_SECURITY_SEGMENTS) private readonly segmentState!: ISecuritySegmentModelState;
@State(STATE_SECURITY_TERRITORIES) private readonly territoryState!: ISecurityTerritoryModelState;
@State(STATE_SECURITY_TYPES) private readonly typeState!: ISecurityTypeModelState;
...
private readonly descriptorSegments = Descriptors.Segments;
private readonly descriptorTerritories = Descriptors.Territories;
private readonly descriptorTypes = Descriptors.Types;
private get segments() { return this.segmentState.items; }
private get territories() { return this.territoryState.items; }
private get types() { return this.typeState.items; }
...
}
</script>
Securities.vue (26:00)
<template lang="pug">
div
...
ListView(
v-bind:items="segments"
v-bind:onClick="onClickDescriptorFactory(descriptorSegments)"
v-bind:renderFn="renderFnDescriptor")/
ListView(
v-bind:items="territories"
v-bind:onClick="onClickDescriptorFactory(descriptorTerritories)"
v-bind:renderFn="renderFnDescriptor")/
ListView(
v-bind:items="types"
v-bind:onClick="onClickDescriptorFactory(descriptorTypes)"
v-bind:renderFn="renderFnDescriptor")/
</template>
Exciton Interactive LLC