[學習筆記] Cordova+AmazeUI+React 作個通信錄 - 聯繫人詳情

[學習筆記] Cordova+AmazeUI+React 作個通信錄 系列文章javascript

目錄

  1. 準備
  2. 聯繫人列表(1)
  3. 聯繫人列表(2)
  4. 聯繫人詳情
  5. 單頁應用 (With Router)
  6. 使用 SQLite

傳送門:所有章節 示例代碼css


前面已經完成了聯繫人列表,然而彷佛與 Cordova 沒啥關係,隨便找個 Web 服務器就能夠跑。那麼標題上的 Cordova 就只是一個簡單的 Web 窗口而已麼?html

固然不是,目前咱們的數據是保存在 json 文件中的,之後會放在數據庫中,以便於實現數據的增刪改。數據庫選用 SQLite,操做 SQLite 必須使用 Android 提供的一些 API,這時候就須要經過 Cordova 的插件來實現了。甚至再日後添加二維碼等功能,都須要用到 Cordova。可是 Cordova 的 API 參與了開發以後,調試時就要部署在手機上進行了,操做多有不變,因此先處理前端不須要 Cordova 的部分,待前端成熟以後再來鏈接 Cordova。前端

詳情頁面

聯繫人列表完成以後,如今開始考慮詳情頁面。之因此須要詳情頁面,是由於還有一些不便於在列表中展現的信息(列表項顯示範圍有限),因此再在測試數據中添加更多的字段,好比性別、城市等java

規劃

詳情頁面寫在 detail.html 中,對應的在 js 目錄中建立一個 detail.jsx 保存 React 組件和腳本。樣式表暫時仍然使用 index.css,在裏面添加詳情頁面所須要的樣式。因此新的 www 目錄結構會是這樣react

clipboard.png

詳情頁面仍然須要顯示一個頁頭,標題就是姓名。以後的內容仍然用列表顯示。用 HTML 表示大概會像這樣git

<ul>
    <li>
        <span class="label">姓名</span>
        <span>張三</span>
    </li>
    <li>
        <span class="label">電話</span>
        <span>13801234567</span>
    </li>
    <li>
        <span class="label">性別</span>
        <span>男</span>
    </li>
    <li>
        <span class="label">城市</span>
        <span>四川省綿陽市</span>
    </li>
</ul>

代碼

detail.html

detail.html 的代碼和 index.html 幾乎同樣,惟一的區別是github

<script type="text/jsx" src="js/index.jsx"></script>

換成了數據庫

<script type="text/jsx" src="js/detail.jsx"></script>

detail.jsx

detail.jsx 中主要定義 3 個組件json

  1. Detail 組件渲染聯繫人信息詳情列表
  2. DetailItem 組件渲染聯繫某一項信息的內容
  3. Page 組件渲染頁面內容,包括頁頭和 Detail 組件等

由於是靜態頁面,不能處理 GET 或 POST 參數,因此由地址欄的 HASH 傳遞聯繫人的 ID 參數,再在頁面中經過 AJAX 獲取數據,篩選出該詳情頁面須要顯示的聯繫人信息。而數據獲取就由 Page 組件處理(處理過程參考 index.jsx 中的 Page 組件。

以後由 Page 經過屬性方式向 Detail 傳遞聯繫人數據;而 Detail 則拆分數據項,仍然經過 props 方式,逐項向 DetailItem 傳遞數據。

在容錯方面,爲了簡化處理過程,若是沒能取得聯繫人數據,則用一個姓名叫「查無此人」的默認的聯繫人數據代替。

定義 Detail 組件

var Detail = React.createClass({
    render: function() {
        var person = this.props.person;

        return (
            <A.List data-id={person.id}>
                <DetailItem label="姓名" value={person.name} />
                <DetailItem label="電話" value={person.tel} />
                <DetailItem label="性別" value={person.isMan ? "男" : "女"} />
                <DetailItem label="城市" value={person.city} />
            </A.List>
        );
    }
});

注意,這裏在 <A.List> 中傳入了一個 data-id 屬性,這個屬性在做爲 React 對象屬性的同時,也會以標籤屬性形式渲染到 HTML 中——React 會把 data- 前綴的屬性直接渲染爲 HTML 標籤屬性。

DetailItem 組件

var DetailItem = React.createClass({
    render: function() {
        return (
            <A.ListItem>
                <span className="label">{this.props.label}</span>
                <span>{this.props.value}</span>
            </A.ListItem>
        );
    }
});

這個組件沒什麼懸念,只是直接把 label 的文本顯示出來而已。

Page 組件

var Page = React.createClass({
    defaultPerson: {
        id: "0000",
        name: "查無此人",
        phone: "00000000000"
    },
    getInitialState: function() {
        return {
            person: null
        };
    },
    componentDidMount: function() {
        $.getJSON("/js/data.json").then(function(data) {
            if (this.isMounted()) {
                this.setState({
                    person: data.filter(function(p) {
                        return "#" + p.id === window.location.hash;
                    })[0]
                });
            }
        }.bind(this));
    },
    render: function() {
        var person = this.state.person || this.defaultPerson;

        return (
            <div>
                <A.Header title={person.name} />
                <Detail person={person} />
            </div>
        );
    }
});

Page 組件和以前列表頁面的 Page 組件同樣,在 componentDidMount() 中經過 AJAX 加載數據。以後,經過 window.location.hash 按 ID 精確查找聯繫人,設置到 state 中。

顯示的時候,若是沒有找到 this.state.person,則使用默認的「查無此人」聯繫人信息。

渲染 Page

最後固然不能忘了渲染根組件:Page。

React.render(<Page />, document.body);

這時候,經過 http://localhost/detail.html#1001 這個地址,已經能夠看到數據顯示出來,只不過因爲沒有添加樣式,還不夠美觀。

clipboard.png

在列表頁面上添加到詳情頁面的鏈接

按照 Amazi UI React 文檔,在列表頁面上添加到詳情頁面的鏈接,只須要在 Person 組件中爲 <A.ListItem> 添加 href 屬性便可,

<A.ListItem className="person" href={"detail.html#" + this.props.id}>
</A.ListItem>

而後添加以後顯示出來的效果卻大大出乎意料。主要緣由是 Amaze UI 將 li>a 的 display 設置爲 block了,因此帶連接的電話圖標會顯示在下一行。固然經過修改 CSS 是能夠解決的,可是我想用另外一種方法來解決:點擊事件。

列表項上的點擊事件

React 是支付事件處理的,在其 Event System 一章中說明了事件處理的方式和注意事項。React 能夠處理的事件分幾大類共計數十個,都在 Event System 中列舉出來了。這裏須要用到的是 onClick 事件。

注意,下面說的內容不是在 detail.jsx 而是在 index.jsx 中

首先爲 Person 組件定義一個處理函數,用於處理列表項被點擊後的動做:設置 window.location.href 跳轉到詳情頁。

var Person = React.createClass({
    handleClick: function(event) {
        window.location.href = "detail.html#" + this.props.id;
    },
    render() { ... }
});

能夠看到,數據來源仍然能夠是 this.props,同理,也能夠是 this.state

以後在 <A.ListItem> 中綁定事件處理函數

render: function() {
        var link = "tel:" + this.props.tel;
        return (
            <A.ListItem className="person" onClick={this.handleClick}>
            ...
            </A.ListItem>
        );
    }

注意到 onClick={this.handleClick} 的寫法,聯想到在 HTML 標籤屬性中綁定事件處理函數的狀況,函數在執行時 this 指針會變爲全局對象 (window)。這裏是否是須要 bind(this) 呢?

不須要!

React 已經處理了 this 的問題,因此這裏只須要簡單綁定 this.handleClick,而在 handleClick 中使用 this 就是當前組件對象,不會是全局對象。

若是有強迫症,可能還須要給 <A.ListItem> 加個內聯樣式:style={{ cursor: "pointer" }},不過我認爲不必,由於咱們的目標是手機 App,指哪點哪,徹底沒有 hover 一說。

可點擊的詳情電話

要讓電話可點擊只須要加 <a href="tel:xxxxxx"> 便可。可是在詳情頁,每一個數據項都是這個結構:

<ListItem>
    <span>{label}</span>
    <span>{value}</span>
</ListItem>

若是要加連接,就會變成下面的結構

<ListItem>
    <span>{label}></span>
    <span>
        <a href={href}>{value}</a>
    </span>
</ListItem>

而後再在 Detail 中使用組件的時候多傳入一個 href 參數。

提及來,兩個 DetailItem 中的區別只有第 2 個的 <span> 中的內容那一點,有沒有辦法能夠重用呢?——能夠試試 mixins,由於從 Reusable Components 的理解,mixins 有點像繼承。

嘗試(第 1 次失敗)

var DetailItem = React.createClass({
    getValueContent: function() {
        return this.props.value;
    },
    render: function() {
        return (
            <A.ListItem className="person-detail-item">
                <span className="label">{this.props.label}</span>
                <span>{this.getValueContent()}</span>
            </A.ListItem>
        );
    }
});
var DetailLinkItem = React.createClass({
    mixins: [DetailItem],
    getValueContent: function() {
        return <a href={this.props.href}>{this.props.value}</a>;
    }
});

結果失敗了。再次閱讀 Reusable Components 中的示例,而後發現 mixins 數組中應該是一個原型對象更貼切,嘗試

第 2 次嘗試(再次失敗)

var DetailLinkItem = React.createClass({
    mixins: [DetailItem.prototype],
    getValueContent: function() {
        return <a href={this.props.href}>{this.props.value}</a>;
    }
});

此次是由於重複定義了 constructor。DetailItem.prototype 中有一個 constructor,而 React.createClass() 又定義了一個。看來不能偷懶,只能定義個獨立對象了

終於成功的嘗試

var detailBase = {
    render: function() {
        return (
            <A.ListItem className="person-detail-item">
                <span className="label">{this.props.label}</span>
                <span>{this.getValueContent()}</span>
            </A.ListItem>
        );
    }
};

var DetailItem = React.createClass({
    mixins: [detailBase],
    getValueContent: function() {
        return this.props.value
    }
});

var DetailLinkItem = React.createClass({
    mixins: [detailBase],
    getValueContent: function() {
        return <a href={this.props.href}>{this.props.value}</a>;
    }
});

補充一下 Detail 中的變動

這個變動是在第 1 次嘗試的時候就修改了的,主要是處理「電話」那一項時用 <DetailLinkItem> 代替了 <DetailItem>

var Detail = React.createClass({
    render: function() {
        var person = this.props.person;

        return (
            <A.List className="person-detail" data-id={person.id}>
                <DetailItem label="姓名" value={person.name} />
                <DetailLinkItem label="電話" value={person.tel} href={"tel:" + person.tel} />
                <DetailItem label="性別" value={person.isMan ? "男" : "女"} />
                <DetailItem label="城市" value={person.city} />
            </A.List>
        );
    }
});

美化 Detail

美化一般放在最後,但不表明不須要,如今來加 className 和 CSS。

從效果上來看,主要是須要把 label 和後面的內容區分開來,順便參照列表頁面的列表樣式,作一些細節上的美化。

先爲 <A.List> 添加 className="person-detail",再爲 <A.ListItem> 添加 className="person-detail-item"className="label" 是有先見之明早就加了的。最後修改 CSS,去掉 li 的類限定,再加上 ul.person-detailli.person-detail-item 的樣式

ul.person-list {
    margin-top: 0;
}

li {
    padding: 3px 6px;
}

li>.person-icon {
    margin-right: 6px;
}

li>.person-phone {
    margin-top: 4px;
}

ul.person-detail li {
    margin-bottom: 4px;
    border-top: 0;
}

li.person-detail-item .label {
    margin-right: 8px;
    padding: 2px 5px;
    background-color: #41afff;
    border-radius: 3px;
    color: white;
}

最終效果仍是比較使人滿意的

clipboard.png

相關文章
相關標籤/搜索