Nhảy tới nội dung

Tạo giao diện cơ bản với ZMP Framework

Tạo một số giao diện thường dùng với ZMP Framework

Form

  1. Sử dụng List component với các properties:
  • form: Bật chế độ form container
  • id: form id để truy cập đến form, thao tác với các form api
  • onSubmit: Thêm handler sự kiện submit form khi người dùng click/press lên Button với type="submit" bên trong List container
  1. Khai báo các Input cần sử dụng bên trong List container

  2. Sử dụng form api để thao tác với form:

  • Lấy form data dưới dạng object
import { zmp } from "zmp-framework/react";
zmp.form.convertToData(form);
  • Điền nội dung form từ data
import { zmp } from "zmp-framework/react";

zmp.form.fillFromData(form, data);

Ví dụ

Tạo một form có giao diện như sau

  • Get Form Data: lấy data của form khi user submit

  • Fill form: Điền form với data cụ thể

    demo form

Import

import React from "react";
import {
List,
ListInput,
Button,
Box,
zmp,
Radio,
Text,
Checkbox,
} from "zmp-framework/react";

Tạo form layout

  <List
style={{ listStyle: "none" }}
form
id="my-form"
noHairlines
onSubmit={handleOnSubmitForm}
>
{/* text input */}
<ListInput
label="Full name"
type="text"
placeholder="Enter your full name"
clearButton
info="Your full name"
name="fullName"
pattern="^[a-zA-Z]{2,30}$"
required
errorMessage="Invalid"
validate
></ListInput>
{/* number input */}
<ListInput
label="Age"
type="number"
min={1}
placeholder="Enter your age"
clearButton
info="your age"
validate
errorMessage="Invalid age, please try agian"
name="age"
required
></ListInput>
{/*select */}
<ListInput
label="City"
type="select"
placeholder="Select your city"
name="city"
validate
>
<option value="1">Hồ Chí Minh</option>
<option value="2">Hà Nội</option>
</ListInput>
{/* radio */}
<Box mx={0}>
<Text size="small" className="text-color-nl500">
Gender
</Text>
<Box>
<Radio name="gender" value="male" defaultChecked label="Male" />
</Box>
<Box>
<Radio name="gender" value="female" label="Female" />
</Box>
</Box>
{/* checkbox */}
<Box mx={0}>
<Text size="small" className="text-color-nl500">
Hobbies
</Text>
<Box>
<Checkbox name="hobbies" label="reading" value="Reading" />
</Box>
<Box>
<Checkbox name="hobbies" value="cycling" label="Cycling" />
</Box>
<Box>
<Checkbox name="hobbies" value="cooking" label="Cooking" />
</Box>
<Box>
<Checkbox name="hobbies" value="gardening" label="Gardening" />
</Box>
</Box>
{/* actions */}
<Box>
<Button type="submit" typeName="secondary" responsive>
Get Form Data
</Button>
</Box>
<Box>
<Button typeName="secondary" responsive onClick={handleFillForm}>
Fill Form
</Button>
</Box>
</List>
});

Implement các handler

const handleOnSubmitForm = (e) => {
e.preventDefault();
const data = zmp.form.convertToData("#my-form");
console.log(data);
};

const handleFillForm = (e) => {
e.preventDefault();
zmp.form.fillFromData("#my-form", {
fullName: "John",
age: 20,
city: 2,
gender: "male",
hobbies: ["cooking", "reading"],
});
};

Preview

preview

Sử dụng Zalo quét mã QR trên để xem

Bottom Navigation Bar

  1. Để tạo bottom navigation bar sử dụng Tabbar component, với property bottom, có thể tuỳ chỉnh chiều cao, font size thông qua style prop

  2. Cần đặt Tabbar bên trong <Page> container

Lưu ý

Cần đặt trực tiếp Tabbar component bên trong Page/View, nếu tạo custom component từ Tabbar thì cần set displayName = "zmp-toolbar" cho custom component

const MyTabbar = () => {};
MyTabbar.displayName = "zmp-toolbar";
export default MyTabbar;
  1. Sử dụng component Link để tạo các tabbar item, để navigate có thể sử dụng props href hoặc thêm event handler cho sự kiện click thông qua prop onClick và sử dụng api navigate để tuỳ chỉnh chuyển trang, sử dụng tabLinkActive để xác định tab item nào đang được active

Ví dụ

Tạo 1 layout như sau:

  • Bottom tabbar gồm 3 tabs: Home, Friends, Profile. Khi press vào Home sẽ về trang chủ, Friends chuyển đến trang Friends, Profile chuyển đến trang Profile

    demo navigation bar

Tạo Bottom Navigation Bar component

/src/components/bottom-navigationbar.jsx

Import
import React from "react";
import { Tabbar, Link, zmp } from "zmp-framework/react";

Tạo Layout

Tab active khi currentTab truyền từ props match mới path của từng tab, Thêm icon với iconZMP props, xem thêm icons tại đây

import React from "react";
import { Tabbar, Link, zmp } from "zmp-framework/react";

const BottomBar = ({ currentTab = "/" }) => {
const navigateToTab = (path) => {
if (!path) {
return;
}
zmp.view.main.router.navigate(path, {
animate: false,
browserHistory: false,
});
};

return (
<Tabbar style={{ height: "60px", fontSize: "12px" }} bottom={true}>
<Link
tabLinkActive={currentTab === "/"}
onClick={() => navigateToTab("/")}
iconZMP="zi-home"
>
Home
</Link>
<Link
tabLinkActive={currentTab === "/friends/"}
onClick={() => navigateToTab("/friends/")}
iconZMP="zi-group"
>
Friends
</Link>
<Link
tabLinkActive={currentTab === "/profile/"}
onClick={() => navigateToTab("/profile/")}
iconZMP="zi-user-circle"
>
Profile
</Link>
</Tabbar>
);
};

BottomBar.displayName = "zmp-toolbar";

export default BottomBar;

Import và sử dụng tại các Page cần dùng

/src/pages/index.jsx

import React from "react";
import { Page, Box, Card, Title, Text } from "zmp-framework/react";
import BottomBar from "../components/bottom-navigationbar";
const HomePage = () => {
return (
<Page name="home">
{/* Page content */}
<BottomBar currentTab="/" />
<Box>
<Card inset>
<Title size="normal">ZMP UI Sample app</Title>
<Text>Open tab Friends to view list and search sample</Text>
<Text>Open tab Profile to view form sample</Text>
</Card>
</Box>
</Page>
);
};
export default HomePage;
mẹo

Có thể kết hợp sử dụng store để lưu tab đang active, và khai báo Tabbar tại View, Tabbar sẽ áp dụng cho tất cả các page bên trong view, xem thêm về Tabbar tại đây

<View main className="safe-areas" url="/" preloadPreviousPage={false}>
<BottomBar currentTab={currentActiveTab} />
</View>

Preview

preview

Sử dụng Zalo quét mã QR trên để xem

Virtual List

Sử dụng List component với props:

  • virtualList: chuyển chế độ virtual list
  • virtualListParams: Các virtual list parameters, xem chi tiết tại đây

ListItem sẽ dùng để render các item của list, prop virtualListIndex để virtual list biết index của item

Searchlist

Sử dụng Searchbar component với các properties:

  • searchContainer: selector của container của list cần search, khi dùng virtual list là ".virtual-list"
  • searchItem: selector của item cần search, khi search trên list item sẽ là "li"
  • searchIn: selector element chứa phần Nội dung search, ví dụ cần search phần title của list: ".item-title"

Ví dụ

Tạo 1 layout như sau:

Layout bao gồm 1 search bar và phần List danh sách bạn bè: demo form

Mock data

Ví dụ tạo 1 list data giả có 100 phần tử, chứa thông tin title, subtitle, avatar

// src/store.js

import { createStore } from "zmp-core/lite";
const store = createStore({
state: {
friends: () => {
let items = [];
for (let i = 1; i <= 100; i += 1) {
let name = Math.random().toString(36).substring(5);
items.push({
title: name,
subtitle: `Subtitle ${i}`,
avatar: name.substring(0, 2),
});
}
return items;
},
},
getters: {
friends({ state }) {
return state.friends;
},
},
actions: {
addFriends({ state }, product) {
state.friends = [...state.friends, product];
},
},
});

export default store;
import React, { useState } from "react";
import {
Page,
Card,
Searchbar,
List,
ListItem,
Box,
Avatar,
useStore,
} from "zmp-framework/react";
import BottomBar from "../components/bottom-navigationbar";

const Friends = () => {
const [vlData, setVlData] = useState({
items: [],
});

// lấy giữa liệu đã tạo từ store
const items = useStore("friends");

// implement phần search
const searchAll = (query, searchItems) => {
const found = [];
for (let i = 0; i < searchItems.length; i += 1) {
if (
searchItems[i].title.toLowerCase().indexOf(query.toLowerCase()) >= 0 ||
query.trim() === ""
)
found.push(i);
}
return found; // trả về mảng index của các kết quả khớp
};

// phần render của virtual list
const renderExternal = (vl, newData) => {
setVlData({ ...newData });
};

// Giao diện
return (
<Page className="page-friends">
{/* Bottom navigation bar đã tạo ở hướng dẫn trên */}
<BottomBar currentTab="/friends/" />
{/* Phần search bar */}
<Box m="0" p="4">
<Searchbar
searchContainer=".virtual-list"
searchItem="li"
searchIn=".item-title"
/>
</Box>
bar
<Card title="Friends List" className="list-card">
{/* Phần List */}
<List
className="searchbar-found"
medialList
virtualList
virtualListParams={{
items,
searchAll,
renderExternal,
height: 80,
}}
>
<ul>
{/* Render các items */}
{vlData.items.map((item, index) => (
<ListItem
key={index}
mediaItem
link="#"
title={item.title}
subtitle={item.subtitle}
style={{ top: `${vlData.topPosition}px` }}
virtualListIndex={items.indexOf(item)}
>
<Avatar slot="media">{item.avatar}</Avatar>
</ListItem>
))}
</ul>
</List>
</Card>
</Page>
);
};
export default Friends;

Mở rộng

mẹo

Để tạo layout có phần search bar được giữ nguyên không bị cuộn theo trang, chỉ cuộn phần list container, cần thêm css tạo layout cuộn trang phần list container, và truyền scrollableParentEl cho virtualListParams với giá trị là css selector của phần list container

Ví dụ với layout trên, sử dụng flex layout, ta thêm đoạn css sau:

.page-friends .page-content {
display: flex;
flex-direction: column;
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -ms-flexbox;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-webkit-flex-direction: column;
-moz-box-orient: vertical;
-moz-box-direction: normal;
-ms-flex-direction: column;
}
.page-friends .list-card {
-webkit-box-flex: 1;
-webkit-flex: 1;
-moz-box-flex: 1;
-ms-flex: 1;
flex: 1;
overflow: auto;
}
.page-friends .list-card .zmp-card-body {
height: calc(100% - 48px);
}
.page-friends .list-card .searchbar-found {
height: 100%;
}

Sau đó để cuộn trang bên trong ".list-card", thêm scrollableParentEl cho virtualListParams trở thành:

<List
className="searchbar-found"
medialList
virtualList
virtualListParams={{
items,
searchAll,
renderExternal,
height: 80,
scrollableParentEl: ".list-card",
}}
>
{/* ...*/}
</List>

Preview

preview

Sử dụng Zalo quét mã QR trên để xem