2015年12月8日 星期二

ParseReact實作(六)--map成功了!

在前面卡住這麼久之後,我終於成功了!其實問題就卡在,我不知道只要create之後,他就會自動去trigger query,這樣一來,就算我在create的同時做了near function,他也會直接去trigger query,我不用去想說怎麼更新誰先更新的問題!
所以,如前幾篇實作所說的,我一樣在createItem裡面做near function,找出那十個之後,我放到一個array裡面


接下來把這個array的東西放到table裡面,方便以後讀取(用來畫地圖上的點)


然後我在上面呼叫map的地方傳入nearest這個array,之前我一直以為,根本還沒捉到點怎麼去找最近的十個點,現在這個根本不是問題!因為你create的當下他就會去trigger了!
很好!爸爸都設定好之後,我們就來看小孩接什麼吧~來看到map.js
先把location接進來,在這邊我想確認是不是成功地成為了array所以給了幾個console.log
其實在成功之前我失敗了幾次,原因是因為我讀進來的東西是geopoint,我不知道怎麼分別得到他們的lat、lng,這個等一下再加入地標的時候我會說明

呈現的結果會是

表示我們傳進來的東西都有接到喔~非常好
接下來就是要把它加到我們的地圖上面了!我們使用的是gmap裡面的addmarker function,由於我們讀進來的是array,我用迴圈包住之後一個一個讀進來加上去


原來是在lat、lng的地方一直抓不到點,因為我原本以為是用parse geopoint的方法去抓(也就是location[i][1].lat)後來恍然大悟!我用的這個component 是react欸!!!!哪來的parse!!!馬上去看我的array,喔~~~原來我的table裡面是長這樣子啊



想當然就是用latitude去get拉!

結果


只要一refresh,不用輸入,他會先去抓最新的那個做顯示,然後等你輸入之後會再更新













之後再點每個地標,就可以顯示出來電話!耶~~~~~~我要哭了!!!!!!

2015年12月6日 星期日

Parse CloudCode入門

到現在為止,接觸parse的時間也來到了一年,實習期間就要結束了,這一年來我遇到了很好的主管,很肯教我也很有耐心,讓我從最陌生開始接觸到現在可以獨立架出雛形,至少還算是熟悉,在結束實習之前,我把所有我做過的網站都做一個分享和整理,遇到的困難不計其數可是我沒辦法全部都記得了哈哈哈,有哪裡有問題可以提出來,我會用我菜菜的腦袋想辦法的!
首先,用過parse的都知道,一開始要先去網站註冊,


建立好你的project之後,利用terminal去下指令。

$ parse new MyCloudCode

Email: oprah519519@gmail.com
Password:
1:MyApp
2:MyOtherApp
Select an App: 1
$ cd MyCloudCode

接下來你就可以在MyCloudCode這個子目錄下面做事情了!

目前重要的有兩件事:第一:你要有host name(不然怎麼打開你的網站) 第二:你要知道怎麼樣更新上傳到server

第一件事我們在parse的網站上面就可做到,到你project裡面的setting,側欄旁邊有hosting



然後在裡面打入你想要的url name



ok!
第二件事就是要上傳更新,剛剛我們terminal已經在MyCloudCode這個子目錄下面了,先在只要

$ parse deploy

就可以上傳到server了!
接下來我們就要開始我們的網站,跳過那些hello world(因為對我來說那些就跟system.out.prin一樣沒意義),我們直接開始架網站

https://parse.com/tutorials

裡面的



把程式碼下載回來之後,我們先來看一下他的架構,這個部分會再開始更改程式碼時好好介紹,現在就先說要怎麼樣假好網站為主。



首先我們把這些anyimg-master裡面的移動到我們剛剛建立的MyCloudCode

資料夾裡面,因為我們是在這個子目錄夾裡面去更新的,然後再把layout.ejs打開



裡面有這一行,於是到我們project的setting中的key中去找,填上去之後,到terminal parse deploy

並且在瀏覽器中輸入你剛剛設定的網址




馬上架好一個可以上傳圖片且含有登入註冊功能的網站!so easy! 接下來我會逐篇說明我利用這個延伸做的網站更有不同值得介紹的功能,還有parse的開發環境、用ejs的理由、還有同步非同步之類之類的觀念,會在接下來的project一一介紹,遇見的困難也會寫出來,閉經所有的開發過程都是多麽不容易啊~~~~~



2015年12月2日 星期三

ParseReact library

有鑒於實在是卡住太久了,決定回去看library,一打開.....襪靠.....到底是要我看到民國幾年.....對於功力淺薄的我看一個就要看好久,更別說是全部
所以我覺得先從最熟悉的Mutation.js開始看起

首先,先來找dispatch()

看來就是一個promise,沒做(noop,jquery的語法)就回傳空的,如果有的話,我們就去呼叫UpdateChannel.issueMutation(this, options || {})
(this是指什麼)
於是跳到了UpdateChannel.js


在這邊定義了target是從傳入的mutation中得到的,傳入的mutation就是createclass的this
先看option有沒有東西,如果沒有waitForServer的話就return performOptimisticMutation()
有的話就呼叫MutationExecutor.execute(從MutationExecutor.js來的,反正應該就是執行)

我們看見他做了兩件事:第一:把 Mutation 指給 current local state
第二:再通知server Mutation這個事件
如果這個request成功這個改變就會被local state承認
如果失敗就會倒退回去

先來看performOptimisticMutation()

在這裡先讀進剛剛產生的target(由mutation產生的),還有mutation,batch是options.batch(這什麼?)
當我們使用destroy,會呼叫ObjectStore的fetchSubscribers,如果不是呼叫destroy,就給一個空的latest,當我們使用create這個動作,會去呼叫ObjectStore的getLatest,在教得到的latest拿去比對queries,結果指給fields,再將這些結果傳給UpdateChannel 的pushUpdates


接下來繼續,宣告一個p的promise,如果MutationExecutor執行成功,就會直接把subscribers和changes傳給update
在裡面宣告三個變數
subscribers是去看ObjectStore.fetchSubscribers,如果有store[id]就回傳Object.keys(store[id].queries)如果有localSubscribers[id]就回傳 return Object.keys(localSubscribers[id]);都沒有就回傳空的,就是回傳符合這個object的queryhash
(store和localSubscribers的不同是什麼?)
delta是把傳進來的那個mutation(就是this)用Delta.js的generateDelta,餵給他MutationExecutor成功的結果(是什麼?Delta是什麼?用來幹嘛?)、changes是用ObjectStore.commitDelta,餵給他剛剛產生的delta,可以產生最新的object state還有changed的array key,如果上述都完成,就把上面產生的subscriberschanges傳給pushUpdates
目前subscribers表示的意思是有被儲存的object的queryhash
changes表示的事和delta比對之後,最新的object state和改變的array key

那如果失敗了,(表示MutationExecutor執行失敗),就會退回去把target刪了,首先先用ObjectStore.fetchSubscribers()去抓target,抓到了之後,如果我們是呼叫create這個動作,就會利用SubscriptionManager.js去把subscribers中的每個query hash去get subscription(subscription裡面包含什麼內容?)之後把它刪掉;如果不是create這個動作,會先宣告一個noop,(new Delta(target, {})是啥),然後呼叫ObjectStore.resolveMutation(),他可以回傳最新的object state還有改變的array key,然後再把這個傳給pushUpdates(subscribers, changes);

很好,終於開始看怎麼pushUpdates了
 pushUpdates()做的就是,當有一個object更改,會把這個object push到所有的subscribers,首先,會先看現在的subscribers,看剩下的誰還是match的,然後再用changed field去抓新的subscribers,再把新的match的object加上去。
首先,先看latest的部分是不是null(前面的performOptimisticMutation()可以找到),如果是就表示是Destroy,所以把current subscribers中的刪掉(刪掉誰?)

 這邊開始是去找object是不是還match這個query,然後利用changed keys去找現在match的query。
首先我們先把subscribers一個一個讀進來,如果matchesQuery,且目前的changes.id和changes.latest.id不同,就要使用Create method,就是第二個if的地方,把舊的changes.id移掉之後,把新的changes.latest加上去
如果是一樣的,就直接push到subscription就好
如果根本就不matchesQuery,直接移掉

接下來是potential的部分,(誰是potential....),SubscriptionManager的queriesForFields()回傳的是Object.keys(queries),看起來就是特定限制中的query list,應該是新的部分,如果match query就加上去,如果是user的話是看LocalSubscriptions(我先不管他)

現在轉過來細細看中間的ObjectStore.js,是一個parse object的cache,他可以存取最新版本的server data,並將他們用pending mutation 堆疊起來。

store{}是拿來放每個object最新的state,也就是suscribed queries的hash,localSubscribers = {}是拿來放只有存在local的object的queries hash
pendingMutations = {}是存每個object的pending mutations


storeObject()是利用data.id來把object放到store裡面
首先把data傳進來,queries是空的,如果有store[data.id]就產生queries給store[data.id].queries,如果沒有話就直接指定空的queries給data,並回傳他的id
(有誰呼叫storeObject()?)

removeObject()是用id來把object從suscribed裡面移除(
把剛剛產生的store[id].queries依照key放到subscribed(這是一個list),刪掉 store[id]之後再把subscribed這個是放query hashes的list

Object.keys(store[id].queries)是什麼?剛剛有產生的應該是最上面宣告Object.defineProperty(exports, '__esModule', {
  value: true
});
誰是__esModule?有什麼class可以使用?)



addSubscriber()是將object Id和query hash連起來
如果有store[id]而且query是有hash過的,就給他true,表示這個object matches query,或是有localSubscribers[id]而且query也有hash過,也給他true,表示這個object matches query,如果都沒有,就只給localSubscribers[id]

removeSubscriber()跟上面相反,把object Id和query hash連不起來的刪了,
如果有store[id],就把queries的hash刪掉,如果是有localSubscribers[id],就把localSubscribers[id][hash]刪了,而且如果Object.keys(localSubscribers[id]).length < 1就把它完全從list裡面刪掉。


fetchSubscribers()回傳所有match的query hash,如果有store[id]就回傳store[id]的query hashes的list,如果有localSubscribers[id]就回傳localSubscribers的query hashes的list,都沒有就回傳null

看library根本就不是人做的事情啊==,我認真快中風,而且我在有限的時間內沒辦法認真細細看完,可是研讀上述程式碼之後我大略有懂他程式的邏輯

這裏用一張圖來表示



2015年11月18日 星期三

ParseReact實作(五)--query出10個最近的點,顯示在map上面

其實在第二篇我們就寫過,找出十個最近的點,利用的是parse query的near(),可是問題來了!我們現在輸入一個點就會更新一個點,所以TodoItem(存輸入地址的table)跟Taipei(存所有診所的table)不能同時運作,因為我們要等輸入的更新過後才能query他去找最近的點,我們上一篇看到observe()只有一次,可是又只能在裡面query

我現在基本的想法是,將結果的10個點組成array傳入Map.js,在Map.js中把addMarker()放在迴圈中,如此就可在同一地圖上畫出很多點。

我嘗試將死的點餵給他,(每次都先從最笨的開始哈哈哈哈)


出現的結果,點擊還能有文字內容


目前我的問題是,我將讀進來的點存到parse的table,並同時利用這個點去query.near(),找到10個點之後我將它存成一個2D array。

第一:我要怎麼通知TodoList我的array已經產生好了?console.log上面顯示的順序看來,每次還沒產生完新的object,Map的lat、lng就已經被render了啊!!!

第二:如果這樣的方法不可行,我就必須在observe()裡面就產生query,那query裡面可以傳參數嗎?例如:

new Parse.Query('Taipei').near("location",new Parse.GeoPoint( lastItem.lat, lastItem.lng))

結果:

雖然結果是不行,但是我覺得是因為看不懂lastItem是什麼,畢竟他是一個query,如果可以把輸入的點keep住,這個方法或許可行。

第三:以程式順序來說,observe()的query是最先執行的,然後再透過render去呼叫其他需被傳入的參數,可是!!!!為什麼明明在後面被呼叫的 ParseReact.Mutation.Create可以trigger到query?因為輸出的query是有增加新object的,這表示其實query不用更新嗎?只要mutation到他就會自己被更新嗎?


結合以上第二第三,我們將點定義為global變數,然後在query中使用它


定義一個新的nearbyItems來使用near(),並且在render的時候多一個nearbyItems的map來傳最近的十個點給Map,現在的問題是:
Map不能分開,我原來的想法是在lastItem傳兩個參數進去,在nearbyItems再傳兩個進去,這樣一來中心點跟需要被加上去的十個點可以分開處理,可是結果好像不行,有這樣的訊息

於是我把兩個呼叫Map的四個參數全部補滿
雖然可以了可是這麼做會有很多問題:
第一:會傳五次lat lng給map,可是新的紀錄會一直洗掉前一個,變成只出現一張地圖卻有10張空白地圖的位置,因為實際上我們只要傳一次lat lng就好了啊幹嘛傳10次

第二:LastGeoPoint完全沒有更改到,前面一開始的宣告只是為了可以去query near的點,但是接下來在lastItem內做overwrite的時候就沒辦法了,global變數完全不為所動,以至於接下來沒辦法去用LastGeoPoint

第三:map不能拆開,一次就要給他全部需要接收的參數,不能這邊給兩個下一次再給另外兩個,問題是就是要不同的query才能給

我真的是快被卡死了.................



2015年11月17日 星期二

ParseReact實作(四)--輸入地址就更新地圖

在上一篇我們指定死的座標給地圖成功之後,現在我們要動態去更改它的座標位置,當然第一步我們要先把Map.js中的程式碼全部都變成繼承TodoList.js傳入的參數,將原來的數字部分都改成this.prop.參數名稱。



再從TodoList.js去處理傳入的參數。
首先,第一個問題就是,我們是要從parse的table中取出資料,而不是在輸入的時候讀入資料,TodoList.js的結構是先query再render顯示
那observe()是哪裡來的?又是做什麼?
我們來看到ParseReact的document

https://github.com/ParsePlatform/ParseReact/tree/master/docs/api

在當中我們可以看到有細分許多,例如ParseReact.Mixin 還有 ParseReact.Mutation等等,看過之後我在Mixin中發現了observe(),意思就是說ParseReact中有一個Mixin的class中有一個observe function,追溯下去他的功能,我們在library中的Mixin.js中定義出observe()是一個query的必要function,傳入的參數是props跟state,也就是query起初的定義function,你要是不定義會有error message

'Components using ParseReact.Mixin must declare an observe() method.'

前提是!!!!這是export default class TodoList extends ParseComponent 才能用的喔,如果是一般的React .createclass是沒有這個function可以使用的。

在上面的程式碼可以看到我們將TodoItem這個加入條件後的query指給items,然後在render的地方使用this.data.items.map(function(i) show出所有的items,可是我只有一個地圖啊,我只能傳一次參數給map不然會show出超多map,要怎麼樣讓query只傳一個object給items呢?

沒錯!!!!利用parse的limit(數量),在這之前我們也試過用first(),可是first是一個需要回傳的function,我們其實只需要一個條件就夠了,沒辦法在裡面再加一個function,於是我們就在observe裡面放了兩個query~~~~一個給List show出來用,一個給Map的參數用。



然後在render的地方傳給他們


其實到這裡我還是有兩個問題:
第一:傳給map()的是object(也就是i),為什麼只要傳給他object就可以自動找出誰是lat誰是lng呢?
object log出來長這樣

第二:observe()是建立了query沒有錯,可是在哪裡執行?沒有query.find()也沒有query.first(),到底怎麼執行的?

但是還是成功了~~~~

耶嘿~~~~~~~~
請專業的大家幫我想一想到底答案是啥,我會萬分億分感謝您!!!!

2015年11月10日 星期二

ParseReact實作(三)--將gmap放入react.js中

想show出固定座標的gmap,但是show出這個component之前需要先initialize,所以我在TodoList中加入initial function 





出現這樣的訊息

Warning: getInitialState was defined on TodoList, a plain JavaScript class. This is only supported for classes created using React.createClass. Did you mean to define a state property instead?

由於TodoList為了要使用observe(),是經由extend ParseComponent的class
                                  

所以我想把initial的部分放到map裡面

先搞懂component的life cycle

Mounting (插入真實DOM節點)掛載流程

1.getInitialState():當物件被調用時此方法會在寫入 DOM 之前被觸發,通常用來管理狀態的元件可以用這個方法初始化資料。
2.componentWillMount:當元件內部的結構處理完畢準備寫入 DOM 之前觸發。
3.componentDidMount(DOMElement rootNode):當元件被寫入 DOM 之後觸發。當初始化需要操作 DOM 元素就可以用這個方法。

Updating 更新流程

1.componentWillReceiveProps(nextProps):已掛載的元件收到新的 props 時被觸發。在這個方法裡你通常會去比較 this.props 和 nextProps 然後再用 this.setState 去改變狀態。
2.shouldComponentUpdate(nextProps, nextState):這個函式需要回傳一個布林值,當元件判斷是否需要更新 DOM 時會被觸發。你可以在這個方法裡面去比較 this.props,this.state,nextProps,nextState 來決定是否需要更新,回傳 false 則會跳過此次觸發不更新,如果你什麼都不回傳預設會當做 false 。
3.componentWillUpdate:例如在上面 shouldComponentUpdate 你回傳了 true ,元件確定要更新了,在準備更新前這個方法會被觸發。
4.componentDidupdate(prevProps, prevState, rootNode):更新後觸發。


Unmounting 卸載流程

componentWillUnmount():當元件準備要被移除或破壞時觸發。
掛載後才能使用的方法

所以我把code改成這樣 




結果



這表示其實在todolist中,是有render到<Map/>這個class的,
我不信邪,把console log加入所有function中
果不其然

表示都有跑啊!!!gmap你到底死哪裡去啦
為什麼沒瓣法show出元件呢?救救我.........

11/16更新
找到答案了!!!!其實元件週期沒有錯,錯在於傳入gmap的時候就必須設定他的長跟寬。
這麼說好了,我們現在是在react裡面使用gmap.js去產生一個圖檔(地圖)然後傳給react的元件,去render他,使他出現在頁面上。
所以我們將render的部分改成



於是.........



出來了!!!!此刻我真的好想哭.........


其實會要發現這個小問題真的不容易,有時我常常debug一直會在自己的圈圈裡打轉,認為問題就是出在哪裡哪裡,卻總是忘記應該要從最底層開始,這次有主管的幫忙,我們從最底層的html開始改起,把map這個tag直接放在html裡面,在網頁上找到這個tag卻發現!這個map居然是1451*0!所以沒有設定他的長跟寬是永遠顯示不出來的!

大家要照步驟來喔~~~~~bug是很難找滴


引用:http://andyyou.logdown.com/tags/ReactJS