banner image

ReactJS cơ bản qua ví dụ thực tế - B6 Edit Item (P1)

Luồng xử lí của chúng ta ở bài này là khi người dùng click vào nút Edit sẽ sửa trực tiếp dòng đó, sửa xong bấm Save để lưu còn không thì bấm Cancel để hủy.

1. Lấy thông tin sản phẩm muốn sửa

Nếu các bạn đã đọc kĩ bài 5 xóa Item ở đây thì bài 6 sửa Item cũng có đôi phần giống giống như vậy. Chúng ta sẽ chỉ sửa phần tên và level của sản phẩm, còn sửa sản phẩm nào thì sẽ dựa vào id của nó. Trong file App.js ta tạo thêm các state để lưu các giá trị cần sửa, lưu ý level là kiểu số number

constructor(props) {    
    super(props);
    this.state = {
        ...
        indexEdit: 0,
        idEdit: '',
        nameEdit: '',
        levelEdit: 0
    }
}
Mở file App.js ta sẽ viết một hàm để nhận thông tin Item muốn sửa.

handleEditItem = (index,item) => {
    console.log(index,item);
}
Ta gắn hàm trên làm props của Component Item.

renderItem = () => {
    let {items} = this.state;
    return mapld(items,(item,index) => {
        return (
            <Item 
                ...
                handleEditItem={this.handleEditItem}
            />
        )
    });
}
Mở file Item.js vì nút Edit nằm trong file này. Ta sẽ viết một hàm cho sự kiện click Edit để gửi toàn bộ thông tin Item muốn sửa.

<button 
    type="button" className="btn btn-sm btn-warning marginR5"
    onClick={()=>this.props.handleEditItem(index,item)}
>
    Edit
</button>
Nếu test trên trình duyệt mà nó console ra giá trị index và item muốn sửa là ok rồi.
Trong file App.js giờ ta chỉ cần gắn các giá trị đó vào các biến state để lưu.

handleEditItem = (index,item) => {
    this.setState({
        indexEdit: index,
        idEdit: item.id,
        nameEdit: item.name,
        levelEdit: item.level
    });
}

2. Import Component ItemEdit

Trong file App.js hàm renderItem chúng ta đã lấy về một mảng các sản phẩm, sau đó check độ dài của mảng này, nếu bằng 0 thì hiển thị thông báo, nếu có thì bắt đầu duyệt mảng.

Trong quá trình duyệt mảng ta kiểm tra id của sản phẩm có trùng với idEdit không, nếu trùng thì sẽ load riêng một Component để edit sản phẩm.

Trong file App.js ta sẽ import thêm Component ItemEdit

import ItemEdit from './components/ItemEdit';
Tại hàm renderItem ta sẽ lấy state idEdit để kiểm tra lúc nào thì load Component ItemEdit

renderItem = () => {
    let {items,idEdit} = this.state;
    if(items.length === 0) {
        return <Item item={0} />
    }
    return mapld(items,(item,index) => {
        if(item.id === idEdit) {
            return (
                <ItemEdit />
            )
        }
        return (
            <Item 
                key={index}
                ...
            />
        )
    });
}
Tổng kết lại khi click vào nút Edit thì chúng ta đã load được Component Edit cho đúng dòng đó rồi, việc tiếp theo là load các giá trị vào.

3. Load số thứ tự và tên sản phẩm

Cần nhớ rằng tất cả các thông tin như số thứ tự, id sản phẩm, tên sản phẩm, level sản phẩm chúng ta đã lấy được từ Component Item, giờ chỉ việc chuyển chúng sang Component ItemEdit thông qua các state ở App.js
Trong file App.js hàm renderItem ta lấy các giá trị state và gửi vào ItemEdit

renderItem = () => {
    let {items,idEdit,indexEdit,nameEdit,levelEdit} = this.state;
    if(items.length === 0) {
        return <Item item={0} />
    }
    return mapld(items,(item,index) => {
        if(item.id === idEdit) {
            return (
                <ItemEdit 
                    key={index}
                    indexEdit={indexEdit}
                    nameEdit={nameEdit}
                    levelEdit={levelEdit}
                />
            )
        }
        return (
            <Item 
                key={index}
                ...
            />
        )
    });
}
Trong file ItemEdit.js có lẽ việc load đúng số thứ tự là dễ nhất nên chúng ta làm trước phần đó.

render() {
    return(
        <tr>
            <td className="text-center">
                {this.props.indexEdit}
            </td>
            ...
        </tr>
    )
}
Việc tiếp theo là chúng ta load tên sản phẩm vào giá trị của input, sau khi thêm xong và test thử chúng ta sẽ bị cảnh báo warning về sự kiện onChange của input. Việc đó tạm thời để phần sau giải quyết.

render() {
    return(
        <tr>
            <td className="text-center">
                {this.props.indexEdit}
            </td>
            <td>
                <input 
                    type="text" className="form-control" 
                    value={this.props.nameEdit} 
                />
            </td>
            ...
        </tr>
    )
}

4. Load level sản phẩm

Việc hiển thị level sản phẩm tương đối rắc rối ở chỗ giá trị của nó là số và tương ứng với mỗi số là một kiểu hiển thị label với tên khác nhau.

Trong project này chúng ta có 2 phần hiển thị level dưới dạng select - option là ở Component ItemEdit và ở Component Form thêm sản phẩm.

Theo cá nhân mình để tối ưu thì ta tạo một mảng chứa các giá trị level này, Component nào cần thì gửi mảng này sang.

Cách xử lí mảng này sẽ như sau:
  • Tạo một mảng rỗng
  • Duyệt mảng các sản phẩm và lọc lấy giá trị level
  • Nếu level chưa có thì add vào mảng rỗng
  • Nếu level có rồi thì không add nữa
Trong file App.js, tại constructor của nó chúng ta sẽ tạo mảng mới và sử dụng map để lọc giá trị level rồi push vào mảng đó.

class App extends Component {
    constructor(props) {    
        super(props);
        let arrayLevel = [];
        if(Items.length > 0) {
            for(let i = 0; i < Items.length; i++) {
                if(arrayLevel.indexOf(Items[i].level) === -1) {
                    arrayLevel.push(Items[i].level);
                }
            }
        }
        this.state = {
            ...
        }
Việc tiếp theo là ta sắp xếp cho mảng này có giá trị từ nhỏ đến lớn.

class App extends Component {
    constructor(props) {    
        ...
        if(Items.length > 0) {
            ...
        }
        arrayLevel.sort(function(a, b){return a - b});
        ...
Vậy là ta đã có một mảng arrayLevel chứa các giá trị level từ 0 - 1 - 2, tiện thì ta lưu state này luôn.

class App extends Component {
    constructor(props) {    
        ...
        this.state = {
            ...
            arrayLevel: arrayLevel
        }
Tại hàm renderItem ta lấy state arrayLevel và gửi cho Component ItemEdit.

let {items,idEdit,indexEdit,nameEdit,levelEdit,arrayLevel} = this.state;
...
<ItemEdit 
    key={index}
    indexEdit={indexEdit}
    nameEdit={nameEdit}
    levelEdit={levelEdit}
    arrayLevel={arrayLevel}
/>
Trong file ItemEdit.js ta viết một hàm có nhiệm vụ render các thẻ option tương ứng với mảng được nhận ở trên. Cách render các thẻ option sẽ như sau:
  • Lấy mảng arrayLevel từ props Component
  • Duyệt mảng arrayLevel
  • Với mỗi phần tử sẽ sinh ra một thẻ option tương ứng
Duyệt mảng mình lại dùng hàm map của ES6, viết xong hàm renderLevel thì mình gọi nó luôn ở phía dưới.

class ItemEdit extends Component {
    renderLevel = () => {
        let {arrayLevel} = this.props;
        return arrayLevel.map((level,index)=>{
            switch (level) {
                case 0:
                    return <option key={index} value={level}>Low</option>
                case 1:
                    return <option key={index} value={level}>Medium</option>
                default:
                    return <option key={index} value={level}>High</option>
            }
        });
    }
    
    render() {
        return(
            <tr>
                <td className="text-center">
                    {this.props.indexEdit}
                </td>
                <td>
                    <input 
                        type="text" className="form-control" 
                        value={this.props.nameEdit} 
                    />
                </td>
                <td className="text-center">
                    <select className="form-control">
                        {this.renderLevel()}
                    </select>
                </td>
                ...
            </tr>
        )
    }
}

export default ItemEdit;
Chúng ta đã render được list các option, giờ vấn đề là khi người dùng click sửa một sản phẩm có level là "high" thì chúng ta cũng phải để giá trị mặc định của select - option cũng là "high"

Việc này cũng không quá khó chúng ta chỉ cần gắn giá trị levelEdit vào thẻ select thì nó sẽ ra đúng.

<select 
    className="form-control"
    value={this.props.levelEdit}
>
    {this.renderLevel()}
</select>
Tạm thời ở bài này chúng ta chỉ làm các nhiệm vụ hiển thị giá trị vào input và select - option, các hoạt động thay đổi giá trị input hay select sẽ làm tiếp ở bài sau.

ReactJS cơ bản qua ví dụ thực tế - B6 Edit Item (P1) ReactJS cơ bản qua ví dụ thực tế - B6 Edit Item (P1) Reviewed by kentrung on March 08, 2018 Rating: 5

No comments:

Powered by Blogger.