[學習筆記] Cordova+AmazeUI+React 作個通信錄 系列文章javascript
目錄
前面已經完成了聯繫人列表,然而彷佛與 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
詳情頁面仍然須要顯示一個頁頭,標題就是姓名。以後的內容仍然用列表顯示。用 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 的代碼和 index.html 幾乎同樣,惟一的區別是github
<script type="text/jsx" src="js/index.jsx"></script>
換成了數據庫
<script type="text/jsx" src="js/detail.jsx"></script>
detail.jsx 中主要定義 3 個組件json
由於是靜態頁面,不能處理 GET 或 POST 參數,因此由地址欄的 HASH 傳遞聯繫人的 ID 參數,再在頁面中經過 AJAX 獲取數據,篩選出該詳情頁面須要顯示的聯繫人信息。而數據獲取就由 Page 組件處理(處理過程參考 index.jsx 中的 Page 組件。
以後由 Page 經過屬性方式向 Detail 傳遞聯繫人數據;而 Detail 則拆分數據項,仍然經過 props 方式,逐項向 DetailItem 傳遞數據。
在容錯方面,爲了簡化處理過程,若是沒能取得聯繫人數據,則用一個姓名叫「查無此人」的默認的聯繫人數據代替。
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 標籤屬性。
var DetailItem = React.createClass({ render: function() { return ( <A.ListItem> <span className="label">{this.props.label}</span> <span>{this.props.value}</span> </A.ListItem> ); } });
這個組件沒什麼懸念,只是直接把 label 的文本顯示出來而已。
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。
React.render(<Page />, document.body);
這時候,經過 http://localhost/detail.html#1001
這個地址,已經能夠看到數據顯示出來,只不過因爲沒有添加樣式,還不夠美觀。
按照 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 有點像繼承。
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 數組中應該是一個原型對象更貼切,嘗試
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>; } });
這個變動是在第 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> ); } });
美化一般放在最後,但不表明不須要,如今來加 className 和 CSS。
從效果上來看,主要是須要把 label 和後面的內容區分開來,順便參照列表頁面的列表樣式,作一些細節上的美化。
先爲 <A.List>
添加 className="person-detail"
,再爲 <A.ListItem>
添加 className="person-detail-item"
。className="label"
是有先見之明早就加了的。最後修改 CSS,去掉 li
的類限定,再加上 ul.person-detail
和 li.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; }
最終效果仍是比較使人滿意的