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
- 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 trongList
container
Khai báo các Input cần sử dụng bên trong
List
containerSử 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ể
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

Sử dụng Zalo quét mã QR trên để xem
Bottom Navigation Bar
Để 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 propCần đặt
Tabbar
bên trong<Page>
container
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;
- 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 apinavigate
để 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
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;
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

Sử dụng Zalo quét mã QR trên để xem
Virtual List và Search Bar
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è:
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;
List và Search Bar
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
Để 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

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