聯(lián)系人詳情

2018-05-25 23:28 更新

前面已經(jīng)完成了聯(lián)系人列表,然而似乎與 Cordova 沒啥關(guān)系,隨便找個 Web 服務(wù)器就可以跑。那么標(biāo)題上的 Cordova 就只是一個簡單的 Web 窗口而已么?

當(dāng)然不是,目前我們的數(shù)據(jù)是保存在 json 文件中的,以后會放在數(shù)據(jù)庫中,以便于實現(xiàn)數(shù)據(jù)的增刪改。數(shù)據(jù)庫選用 SQLite,操作 SQLite 必須使用 Android 提供的一些 API,這時候就需要通過 Cordova 的插件來實現(xiàn)了。甚至再往后添加二維碼等功能,都需要用到 Cordova。但是 Cordova 的 API 參與了開發(fā)之后,調(diào)試時就要部署在手機上進(jìn)行了,操作多有不變,所以先處理前端不需要 Cordova 的部分,待前端成熟之后再來連接 Cordova。

詳情頁面

聯(lián)系人列表完成之后,現(xiàn)在開始考慮詳情頁面。之所以需要詳情頁面,是因為還有一些不便于在列表中展示的信息(列表項顯示范圍有限),所以再在測試數(shù)據(jù)中添加更多的字段,比如性別、城市等

規(guī)劃

詳情頁面寫在 detail.html 中,對應(yīng)的在 js 目錄中創(chuàng)建一個 detail.jsx 保存 React 組件和腳本。樣式表暫時仍然使用 index.css,在里面添加詳情頁面所需要的樣式。所以新的 www 目錄結(jié)構(gòu)會是這樣

clipboard.png

詳情頁面仍然需要顯示一個頁頭,標(biāo)題就是姓名。之后的內(nèi)容仍然用列表顯示。用 HTML 表示大概會像這樣

  1. <ul>
  2. <li>
  3. <span class="label">姓名</span>
  4. <span>張三</span>
  5. </li>
  6. <li>
  7. <span class="label">電話</span>
  8. <span>13801234567</span>
  9. </li>
  10. <li>
  11. <span class="label">性別</span>
  12. <span></span>
  13. </li>
  14. <li>
  15. <span class="label">城市</span>
  16. <span>四川省綿陽市</span>
  17. </li>
  18. </ul>

代碼

detail.html

detail.html 的代碼和 index.html 幾乎一樣,唯一的區(qū)別是

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

換成了

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

detail.jsx

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

  1. Detail 組件渲染聯(lián)系人信息詳情列表
  2. DetailItem 組件渲染聯(lián)系某一項信息的內(nèi)容
  3. Page 組件渲染頁面內(nèi)容,包括頁頭和 Detail 組件等

因為是靜態(tài)頁面,不能處理 GET 或 POST 參數(shù),所以由地址欄的 HASH 傳遞聯(lián)系人的 ID 參數(shù),再在頁面中通過 AJAX 獲取數(shù)據(jù),篩選出該詳情頁面需要顯示的聯(lián)系人信息。而數(shù)據(jù)獲取就由 Page 組件處理(處理過程參考 index.jsx 中的 Page 組件。

之后由 Page 通過屬性方式向 Detail 傳遞聯(lián)系人數(shù)據(jù);而 Detail 則拆分?jǐn)?shù)據(jù)項,仍然通過 props 方式,逐項向 DetailItem 傳遞數(shù)據(jù)。

在容錯方面,為了簡化處理過程,如果沒能取得聯(lián)系人數(shù)據(jù),則用一個姓名叫“查無此人”的默認(rèn)的聯(lián)系人數(shù)據(jù)代替。

定義 Detail 組件

  1. var Detail = React.createClass({
  2. render: function() {
  3. var person = this.props.person;
  4. return (
  5. <A.List data-id={person.id}>
  6. <DetailItem label="姓名" value={person.name} />
  7. <DetailItem label="電話" value={person.tel} />
  8. <DetailItem label="性別" value={person.isMan ? "男" : "女"} />
  9. <DetailItem label="城市" value={person.city} />
  10. </A.List>
  11. );
  12. }
  13. });

注意,這里在 <A.List> 中傳入了一個 data-id 屬性,這個屬性在作為 React 對象屬性的同時,也會以標(biāo)簽屬性形式渲染到 HTML 中——React 會把 data- 前綴的屬性直接渲染為 HTML 標(biāo)簽屬性。

DetailItem 組件

  1. var DetailItem = React.createClass({
  2. render: function() {
  3. return (
  4. <A.ListItem>
  5. <span className="label">{this.props.label}</span>
  6. <span>{this.props.value}</span>
  7. </A.ListItem>
  8. );
  9. }
  10. });

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

Page 組件

  1. var Page = React.createClass({
  2. defaultPerson: {
  3. id: "0000",
  4. name: "查無此人",
  5. phone: "00000000000"
  6. },
  7. getInitialState: function() {
  8. return {
  9. person: null
  10. };
  11. },
  12. componentDidMount: function() {
  13. $.getJSON("/js/data.json").then(function(data) {
  14. if (this.isMounted()) {
  15. this.setState({
  16. person: data.filter(function(p) {
  17. return "#" + p.id === window.location.hash;
  18. })[0]
  19. });
  20. }
  21. }.bind(this));
  22. },
  23. render: function() {
  24. var person = this.state.person || this.defaultPerson;
  25. return (
  26. <div>
  27. <A.Header title={person.name} />
  28. <Detail person={person} />
  29. </div>
  30. );
  31. }
  32. });

Page 組件和之前列表頁面的 Page 組件一樣,在 componentDidMount() 中通過 AJAX 加載數(shù)據(jù)。之后,通過 window.location.hash 按 ID 精確查找聯(lián)系人,設(shè)置到 state 中。

顯示的時候,如果沒有找到 this.state.person,則使用默認(rèn)的“查無此人”聯(lián)系人信息。

渲染 Page

最后當(dāng)然不能忘了渲染根組件:Page。

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

這時候,通過 http://localhost/detail.html#1001 這個地址,已經(jīng)可以看到數(shù)據(jù)顯示出來,只不過由于沒有添加樣式,還不夠美觀。

clipboard.png

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

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

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

然后添加之后顯示出來的效果卻大大出乎意料。主要原因是 Amaze UI 將 li>a 的 display 設(shè)置為 block了,所以帶鏈接的電話圖標(biāo)會顯示在下一行。當(dāng)然通過修改 CSS 是可以解決的,但是我想用另一種方法來解決:點擊事件。

列表項上的點擊事件

React 是支付事件處理的,在其 Event System 一章中說明了事件處理的方式和注意事項。React 可以處理的事件分幾大類共計數(shù)十個,都在 Event System 中列舉出來了。這里需要用到的是 onClick 事件。

注意,下面說的內(nèi)容不是在 detail.jsx 而是在 index.jsx 中

首先為 Person 組件定義一個處理函數(shù),用于處理列表項被點擊后的動作:設(shè)置 window.location.href 跳轉(zhuǎn)到詳情頁。

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

可以看到,數(shù)據(jù)來源仍然可以是 this.props,同理,也可以是 this.state

之后在 <A.ListItem> 中綁定事件處理函數(shù)

  1. render: function() {
  2. var link = "tel:" + this.props.tel;
  3. return (
  4. <A.ListItem className="person" onClick={this.handleClick}>
  5. ...
  6. </A.ListItem>
  7. );
  8. }

注意到 onClick={this.handleClick} 的寫法,聯(lián)想到在 HTML 標(biāo)簽屬性中綁定事件處理函數(shù)的情況,函數(shù)在執(zhí)行時 this 指針會變?yōu)槿謱ο?(window)。這里是不是需要 bind(this) 呢?

不需要!

React 已經(jīng)處理了 this 的問題,所以這里只需要簡單綁定 this.handleClick,而在 handleClick 中使用 this 就是當(dāng)前組件對象,不會是全局對象。

如果有強迫癥,可能還需要給 <A.ListItem> 加個內(nèi)聯(lián)樣式:style={{ cursor: "pointer" }},不過我認(rèn)為沒必要,因為我們的目標(biāo)是手機 App,指哪點哪,完全沒有 hover 一說。

可點擊的詳情電話

要讓電話可點擊只需要加 <a href="tel:xxxxxx"> 即可。但是在詳情頁,每個數(shù)據(jù)項都是這個結(jié)構(gòu):

  1. <ListItem>
  2. <span>{label}</span>
  3. <span>{value}</span>
  4. </ListItem>

如果要加鏈接,就會變成下面的結(jié)構(gòu)

  1. <ListItem>
  2. <span>{label}></span>
  3. <span>
  4. <a href={href}>{value}</a>
  5. </span>
  6. </ListItem>

然后再在 Detail 中使用組件的時候多傳入一個 href 參數(shù)。

說起來,兩個 DetailItem 中的區(qū)別只有第 2 個的 <span> 中的內(nèi)容那一點,有沒有辦法可以重用呢?——可以試試 mixins,因為從 Reusable Components 的理解,mixins 有點像繼承。

嘗試(第 1 次失敗)

  1. var DetailItem = React.createClass({
  2. getValueContent: function() {
  3. return this.props.value;
  4. },
  5. render: function() {
  6. return (
  7. <A.ListItem className="person-detail-item">
  8. <span className="label">{this.props.label}</span>
  9. <span>{this.getValueContent()}</span>
  10. </A.ListItem>
  11. );
  12. }
  13. });

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

結(jié)果失敗了。再次閱讀 Reusable Components 中的示例,然后發(fā)現(xiàn) mixins 數(shù)組中應(yīng)該是一個原型對象更貼切,嘗試

第 2 次嘗試(再次失?。?/h3>

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

這次是因為重復(fù)定義了 constructor。DetailItem.prototype 中有一個 constructor,而 React.createClass() 又定義了一個。看來不能偷懶,只能定義個獨立對象了

終于成功的嘗試

  1. var detailBase = {
  2. render: function() {
  3. return (
  4. <A.ListItem className="person-detail-item">
  5. <span className="label">{this.props.label}</span>
  6. <span>{this.getValueContent()}</span>
  7. </A.ListItem>
  8. );
  9. }
  10. };
  11. var DetailItem = React.createClass({
  12. mixins: [detailBase],
  13. getValueContent: function() {
  14. return this.props.value
  15. }
  16. });
  17. var DetailLinkItem = React.createClass({
  18. mixins: [detailBase],
  19. getValueContent: function() {
  20. return <a href={this.props.href}>{this.props.value}</a>;
  21. }
  22. });

補充一下 Detail 中的變更

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

  1. var Detail = React.createClass({
  2. render: function() {
  3. var person = this.props.person;
  4. return (
  5. <A.List className="person-detail" data-id={person.id}>
  6. <DetailItem label="姓名" value={person.name} />
  7. <DetailLinkItem label="電話" value={person.tel} href={"tel:" + person.tel} />
  8. <DetailItem label="性別" value={person.isMan ? "男" : "女"} />
  9. <DetailItem label="城市" value={person.city} />
  10. </A.List>
  11. );
  12. }
  13. });

美化 Detail

美化通常放在最后,但不代表不需要,現(xiàn)在來加 className 和 CSS。

從效果上來看,主要是需要把 label 和后面的內(nèi)容區(qū)分開來,順便參照列表頁面的列表樣式,做一些細(xì)節(jié)上的美化。

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

  1. ul.person-list {
  2. margin-top: 0;
  3. }
  4. li {
  5. padding: 3px 6px;
  6. }
  7. li>.person-icon {
  8. margin-right: 6px;
  9. }
  10. li>.person-phone {
  11. margin-top: 4px;
  12. }
  13. ul.person-detail li {
  14. margin-bottom: 4px;
  15. border-top: 0;
  16. }
  17. li.person-detail-item .label {
  18. margin-right: 8px;
  19. padding: 2px 5px;
  20. background-color: #41afff;
  21. border-radius: 3px;
  22. color: white;
  23. }

最終效果還是比較令人滿意的

clipboard.png

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號