Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.

Diploma Thesis:Angular vs React

123 vues

Publié le

V in VDOM or V MVC
Modern trends in front-end web development

Publié dans : Logiciels
  • Soyez le premier à commenter

  • Soyez le premier à aimer ceci

Diploma Thesis:Angular vs React

  1. 1. ΠΑΝΕΠΙΣΤΗΜΙΟ ΠΕΛΟΠΟΝΝΗΣΟΥ ΣΧΟΛΗ ΟΙΚΟΝΟΜΙΑΣ ΔΙΟΙΚΗΚΗΣ ΚΑΙ ΠΛΗΡΟΦΟΡΙΚΗΣ ΤΜΗΜΑ ΠΛΗΡΟΦΟΡΙΚΗΣ ΚΑΙ ΤΗΛΕΠΙΚΟΙΝΩΝΙΩΝ Πτυχιακή Εργασία Μανουσάκης Ευάγγελος 2025201200060 Καλλίνης Άγγελος 2025201200038 π βλ πω : π υ θ λ λΕ ι έ ν Ε ίκο ρος Κα ηγητής Τσε ίκας Νικό αος V in MVC ή V in VDOM. VS Σύγχρονες τάσεις στο front-end development Σεπτέμβριος 2018
  2. 2. 2
  3. 3. Copyright © Καλλίνης ‘Αγγελος και Μανουσάκης Ευάγγελος, 2018. Με επιφύλαξη παντός δικαιώματος . All rights reserved. Απαγορεύεται η αντιγραφή, αποθήκευση και διανομή της παρούσας εργασίας, εξ ολοκλήρου ή τμήματος αυτής, για εμπορικό σκοπό. Επιτρέπεται η ανατύπωση, αποθήκευση και διανομή για σκοπό μη κερδοσκοπικό, εκπαιδευτικής ή ερευνητικής φύσης, υπό την προϋπόθεση να αναφέρεται η πηγή προέλευσης και να διατηρείται το παρόν μήνυμα. Ερωτήματα που αφορούν τη χρήση της εργασίας για κερδοσκοπικό σκοπό πρέπει να απευθύνονται προς τον συγγραφέα. Οι απόψεις και τα συμπεράσματα που περιέχονται σε αυτό το έγγραφο εκφράζουν το συγγραφέα και δεν πρέπει να ερμηνευθεί ότι αντιπροσωπεύουν τις επίσημες θέσεις του Πανεπιστημίου Πελοποννήσου. 3
  4. 4. 4
  5. 5. Περίληψη Η πτυχιακή εργασία αυτή, ασχολείται με τη React και την Angular, δυο frameworks που χρησιμοποιούνται τα τελευταία χρόνια στην ανάπτυξη fullstack εφαρμογών. Θα επιχειρηθεί η ανάπτυξη τεσσάρων εφαρμογών δυο για web και δυο για mobile και θα γίνει η σύγκρισή τους με σκοπό να εξαχθούν κάποια συμπεράσματα. Ο αναγνώστης θα εισαχθεί στο πρώτο κεφάλαιο στις πιο βασικές έννοιες των frameworks σε μορφή tutorial με όσο γίνεται πιο ξεκάθαρο και άμεσο τρόπο. Επίσης θα περιγραφούν και κάποια βοηθητικά εργαλεία όπως είναι για παράδειγμα μια βάση δεδομένων. Τα δυο επόμενα κεφάλαια ασχολούνται το καθένα με ένα framework και έχουν το ίδιο μοτίβο. Πρώτα γίνεται η εγκατάσταση και η παραμετροποίηση όλων των εμπλεκόμενων εργαλείων ώστε να ξεκινήσει κάποιος να αναπτύσσει, μετά εξηγείται λεπτομερώς ο κώδικας -πολλές φορές και με τη μορφή σχολίων- και στη συνέχεια υπάρχουν εικόνες με λεζάντες που δείχνουν ό,τι ακριβώς υλοποιήθηκε. Το θέμα των εφαρμογών είναι ένας ενισχυμένος event planner, που θα έχει κάποιες βασικές λειτουργίες δηλαδή, ταυτοποίηση μέσω Facebook, εισαγωγή/διαγραφή πλάνου, παρουσίαση κατάστασης πλάνων, ολοκήρωση πλάνων μέσω GPS κινητού και παρουσίαση τοίχου πλάνων με την εμφάνιση των πλάνων όλων των χρηστών, ευελπιστώντας σε μια ολοκληρωμένη παρασίαση των frameworks. Από απλές μέχρι αρκετά σύνθετες έννοιες, οι λειτουργίες των εφαρμογών θα παρουσιάσουν στην πράξη τις δυνατότητες και αδυναμίες, τα πλεονεκτήματα και μειονεκτήματά τους. Προφανώς δεν παρουσιάζεται το σύνολο των χαρακτηριστικών αλλά ένα μέρος τους, είναι όμως αρκετά ώστε η σύγκριση να έχει νόημα και να είναι ουσιώδης. Λέξεις κλειδιά JavaScript, Fullstack, V-DOM, MVC, Google Maps, Geolocation, Firebase, Ionic, React Native, Facebook Aυθεντικοποίηση 5
  6. 6. 6
  7. 7. Abstract This bachelor project, deals with React and Angular, two frameworks that are mainly used in the latest years for fullstack development. We’ll embark on developing four applications of which two will cover web platforms and two will cover mobile platforms, aiming to extract conclusions through comparison. The reader will be introduced to the most basic concepts of those frameworks in the first chapters’ tutorial, with the easiest and direct means possible. The next chapters deal one with each framework and have the same pattern. Also some auxilliary development tools such as the database will be described. At first the installation and configuration of all the tools takes place so one can start developing, secondly there is a full explanation of the source code- many times in a comment-like way- and finally, labeled pictures show what’s been developed. Subject of those applications will be a reinforced event planner, which will have some basic features namely, Facebook authentication, Create/Delete plan, plan status, plan completion via mobile GPS service and wall of all the plans posted by all the users, hoping for a complete presentation of the frameworks. From easy to complex concepts, these applications’ features will show in practice the strengths and weaknesses, their advantages and disadvantages. Obviously not the whole set of features of each framework is being shown, only a part of them, but they are enough to make this comparison meaningful and essential. Keywords JavaScript, Fullstack, V-DOM, MVC, Google Maps, Geolocation, Firebase, Ionic, React Native, Facebook Authentication 7
  8. 8. 8
  9. 9. Περιεχόμενα Σελίδα ΚΕΦΑΛΑΙΟ 1 1.1 Στόχος/Σκοπός πτυχιακής.....…...………………………………………………………………10-11 1.2 Λίγα λόγια για τη React js.……..……………..…..…..….…..…..…..………..………11-16 1.3 Λίγα λόγια για την Angular.……..………………..…….………………….…….…….16-22 1.4 Βοηθητικά εργαλεία που θα χρησιμοποιηθούν…..…………………………………….22-28 ΚΕΦΑΛΑΙΟ 2 (React) 2.1 Περιγραφή της demo εφαρμογής (React)………………………………………………..29-31 2.2 SETUP περιβάλλοντος....….…….….……..…..…..…..…..…..…..…..…..….….………31-36 2.3 Δομικά χαρακτηριστικά της εφαρμογής (components/παραδείγματα)...………………...36-85 2.4 Τρέξιμο εφαρμογής....……………………………………………………………………85-91 ΚΕΦΑΛΑΙΟ 3 (Angular) 3.1 Περιγραφή της demo εφαρμογής (Angular)...…………………………………………..93-95 3.2 SETUP περιβάλλοντος......……………….…….…….….…….….……….…………….95-98 3.3 Δομικά χαρακτηριστικά της εφαρμογής (components/παραδείγματα).………………...98-131 3.4 Τρέξιμο εφαρμογής……………………………………………………………………131-138 ΚΕΦΑΛΑΙΟ 4 4.1 Γενικά πορίσματα/συμπεράσματα πάνω στα δυο frameworks..………………………139-141 4.2 Πλεονεκτήματα μειονεκτήματα και σύγκριση των frameworks….…………………...141-145 ΒΙΒΛΙΟΓΡΑΦΙΑ-ΠΗΓΕΣ..........................................................................................................146 9
  10. 10. Κεφάλαιο 1 1.1 Στόχος/Σκοπός πτυχιακής. Στις μέρες μας, ο κόσμος των υπολογιστών γνωρίζει ραγδαία εξέλιξη. Αυτό συνεπάγεται συνεχείς αλλαγές και βελτιώσεις που αφορούν όλους τους κλάδους της επιστήμης των υπολογιστών. Ένας απ’ αυτούς είναι και ο τομέας της ανάπτυξης εφαρμογών, που αφορά το σύνολο των συσκευών από κινητά μέχρι επιτραπέζιους υπολογιστές. Η έννοια του front-end development υπάρχει για πολλά χρόνια, αλλά είναι ακόμα και σήμερα ίσως η ταχύτερα αναπτυσσόμενη. Κατά καιρούς έχουν εισαχθεί καινοτομίες που αφορούν το σχεδιασμό και τη λειτουργικότητα των εφαρμογών και πολλές γλώσσες και frameworks έχουν κάνει την εμφάνισή τους στο χώρο, δίνοντας το καθένα, μια νοοτροπία ανάπτυξης και σχεδιασμού. Πολλές είναι και οι ανάγκες τόσο για διευκόλυνση της ανάπτυξης μιας εφαρμογής από τους προγραμματιστές, όσο και για την άνετη και εύκολη πρόσβαση από τους χρήστες. Η JavaScript ειδικότερα, είναι η πλέον διαδεδομένη και αποδεκτή γλώσσα για web- development ενώ η Android βιβλιοθήκη της JAVA χρησιμοποιείται για ανάπτυξη κινητών εφαρμογών. Το γεγονός αυτό έχει δημιουργήσει την ανάγκη για ανάπτυξη cross-platform γλωσσών και frameworks, δυο εκ των οποίων θα μελετήσουμε εκτενώς σε αυτή την εργασία. Η Angular και η React που αποτελούν το αντικείμενο μελέτης, είναι δυο front-end frameworks που αναπτύσσονται ραγδαία. Είναι η τελευταία λέξη της τεχνολογίας όσον αφορά τον συγκεκριμένο τομέα και συνεχώς ενημερώνονται και βελτιώνονται. Το γεγονός ότι έχουν πολλά κοινά στοιχεία είναι αυτό που προκαλεί τη μεταξύ τους “αψιμαχία” και σύγκριση. Αυτή τη μεταξύ τους σύγκριση θα προσπαθήσουμε με παραδείγματα να κάνουμε, και να δώσουμε μια πιο σαφή εικόνα, ίσως και μια απάντηση στο ερώτημα για το τι τελικά επικρατεί, τα πλεονεκτήματα και τα μειονεκτήματά τους, γενικά συμπεράσματα και μελλοντικές δυνατότητες. Ο τρόπος με τον οποίο θα γίνει η σύγκριση είναι απλώς ένα πείραμα μεταξύ test εφαρμογών. Θα αναπτύξουμε συνολικά τέσσερις εφαρμογές, δύο για το framework της React και δυο για το Angular (web,mobile). Οι εφαρμογές θα εκτελούν απλές λειτουργίες και η ανάπτυξή τους θα επιδεικνύεται με απλό και κατανοητό τρόπο από τα αρχικά στάδια (εγκατάσταση,παραμετροποίηση) μέχρι το deployment τους στον server. Η βασική ιδέα μας υποδεικνύει την ανάπτυξη τεσσάρων όμοιων εφαρμογών αλλά σε διαφορετικές πλατφόρμες. Η εφαρμογή θα ζητάει απ’ τον χρήστη τα στοιχεία του για να εγγραφεί, και στη συνέχεια θα του εμφανίζει έναν τοίχο παρόμοιο με του Facebook όπου θα εμφανίζονται πλάνα τόσο δικά του όσο και των υπόλοιπων χρηστών. Θα του δίνεται επίσης η δυνατότητα να δημιουργήσει ένα καινούριο πλάνο με τίτλο, περιγραφή και τοποθεσία που θα απεικονίζεται μέσω Google Maps. Επίσης δίνεται η δυνατότητα διαγραφής απ’ τη λίστα, φιλτράρισμα στα ιδιωτικά πλάνα, καθώς και τον έλεγχο της κατάστασης του συγκεκριμένου πλάνου δηλαδή αν έχει πραγματοποιηθεί ή όχι. Η αντίστοιχη mobile εφαρμογή θα εκτελεί τις 10
  11. 11. ίδιες λειτουργίες αλλά με μία επιπλέον, την πραγματοποίηση ενός πλάνου. Αυτό επιτυγχάνεται με τη χρήση του GPS της συσκευής. Και οι τέσσερις εφαρμογές θα επικοινωνούν μεταξύ τους μέσω της online βάσης δεδομένων, Firebase. 1.2 Λίγα λόγια για τη React Η React είναι μια βιβλιοθήκη JavaScript που χρησιμοποιείται στην ανάπτυξη User Interfaces. H ανάπτυξή της γίνεται από το Facebook αλλά και από μια κοινότητα προγραμματιστών και άλλων εταιριών. Η React χρησιμοποιείται ως δομικό στοιχείο για την ανάπτυξη single-page ή κινητών εφαρμογών. Πιο περίπλοκες εφαρμογές της, συνήθως απαιτούν τη χρήση επιπρόσθετων βιβλιοθηκών για διαχείριση των states, routing και αλληλεπίδρασης με ένα API. H γλώσσα δημιουργήθηκε από τον Jordan Walke, έναν προγραμματιστή λογισμικού στο Facebook. Επηρεάστηκε από το XHP που ήταν ένα component framework για τη γλώσσα PHP. Αρχικά εισήχθη στη ροή του Facebook το 2011 και αργότερα στο Instagram.com το 2012. Αργότερα, το 2015 παρουσιάστηκε η React Native η οποία επέτρεπε την ανάπτυξη εφαρμογών σε Android, iOS και Windows. Βασικές Έννοιες της React Εισαγωγή στη JSX H JSX είναι μια συντακτική επέκταση για τη JavaScript. Η χρήση της συνιστάται μαζί με τη React για να περιγραφεί το UI. Η JSX μπορεί να μοιάζει με κάποιο template αλλά είναι καθαρά JavaScript. Η JSX μας παράγει στοιχεία React. const element = <h1>Hello, world!</h1>; Στο παρακάτω παράδειγμα δηλώνεται μεταβλητή name και μετά χρησιμοποιείται μέσα σε JSX εσωκλείοντας τη σε άγκιστρα. Αυτό αποτελεί βασική JSX σύνταξη const name = 'Josh Perez'; const element = <h1>Hello, {name}</h1>; Φυσικά μέσα στα άγκιστρα μπορεί να μπει οποιαδήποτε έγκυρη πρόταση της JavaScript. Π.χ: 2+2, user.firstName,formatName(user).Επίσης μπορεί να χρησιμοποιηθεί σε βρόχους if,for κτλ, για τη δήλωση attributes, ακόμα και την αναπαράσταση objects πχ. const element = { type: 'h1', props: { 11
  12. 12. className: 'greeting', children: 'Hello, world!' } }; Element Rendering Η έννοια της απεικόνισης των στοιχείων πάνω στο DOM, ή element rendering είναι απ’ τις πιο σημαντικές για τη React καθώς αποτελούν τα δομικά συστατικά των components, μιας πιο σύνθετης έννοιας που θα εξετάσουμε παρακάτω. Ένα element κατά βάση είναι η περιγραφή του τι βλέπουμε στην οθόνη, και όποτε χρειάζεται, το DOM αναλαμβάνει να μας το δείξει. Για να ξεκινήσουμε την απεικόνιση των elements, απλά δηλώνουμε ένα root element και κάνουμε χρήση μιας ειδικής μεθόδου που είναι υπεύθυνη να εμφανίζει όλα τα elements. Αν η εφαρμογή είναι εξ’ ολοκλήρου React, τότε το root πρέπει να είναι μοναδικό. <div id="root"></div> στο html αρχείο και const element = <h1>Hello, world</h1>; ReactDOM.render(element, document.getElementById('root')); για το rendering. Η μέθοδος render μπορεί να καλείται πολλές φορές αλλά το DOM θα εμφανίσει μονάχα ό,τι άλλαξε και δε θα σπαταλήσει πόρους για ό,τι έμεινε ίδιο. Components και Props Τα components όπως δηλώνει και το όνομά τους είναι πιο σύνθετα δομικά στοιχεία που αποτελούνται από elements. Λειτουργούν σαν συναρτήσεις της JavaScript και επιστρέφουν οτιδήποτε θα εμφανιστεί τελικά στην οθόνη. Ο πιο απλός τρόπος δήλωσης ενός component είναι σαν συνάρτηση και ξεκινούν πάντα με κεφαλαίο γράμμα. function Welcome(props) { return <h1>Hello, {props.name}</h1>; } Είθισται και έχει επικρατήσει η δήλωση των components με χρήση του ECMAScript. class Welcome extends React.Component { render() { 12
  13. 13. return <h1>Hello, {this.props.name}</h1>; } } H μέθοδος render είναι μια απ’ τις μεθόδους του κύκλου ζωής και θα ασχοληθούμε παρακάτω. Τώρα, το κάθε component μπορεί να έχει, σύμφωνα με την αντικειμενοστρεφή λογική, τα δικά του χαρακτηριστικά, η props. Πχ function Welcome(props) { return <h1>Hello, {props.name}</h1>; } const element = <Welcome name="Sara" />; ReactDOM.render( element, document.getElementById('root') ); Το render καλεί το στοιχείο <Welcome name="Sara" />.Καλείται το Welcome component με το prop {name: 'Sara'} . H δουλεία του Welcome είναι να επιστρέφει πάντα Hello, {props.name} δηλαδή Hello, Sara. Η έννοια της Κατάστασης (State) και Μέθοδοι κύκλου ζωής (Lifecycle Methods) Αποτελεί μοναδικό χαρακτηριστικό της React και ίσως το πιο βασικό στη λειτουργία της. Χωρίς καταστάσεις δεν έχουμε παρά μόνο ένα στατικό web app, εκτός ίσως αν καλούσαμε πολλές φορές τη render. Η χρήση των states είναι σχεδόν υποχρεωτική σε κάποια ή και όλες τις φάσεις της ανάπτυξης και επιτρέπει τη δυναμικότητα των εφαρμογών. Η εντολή state μπορεί να χρησιμοποιηθεί όπως και η props. Το state δεν είναι κάτι παραπάνω από μια ειδική μεταβλητή που τη χειριζόμαστε όποτε θέλουμε να αλλάξει κάτι σ’ αυτό που βλέπουμε στην οθόνη. Πρώτα δηλώνεται το component του ρολογιού ως εξής class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } 13
  14. 14. Αυτή η πολύ απλή δήλωση μας εισάγει για πρώτη φορά την έννοια του state, και υποδεικνύει ότι ένα ρολόι θα κατασκευάζεται με ένα state που θα έχει ένα πεδίο date του οποίου η τιμή εισάγεται από την ημερομηνία του συστήματος. H μέθοδος super με όρισμα props είναι κλασσική δήλωση σε constructor όταν θέλουμε το component μας να “βλέπει” τα props που εισάγονται. Έτσι, οποιαδήποτε στιγμή, γράφοντας this.state.date έχουμε μια κατάσταση για την ημερομηνία. render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } } ReactDOM.render( <Clock />, document.getElementById('root') ); Κάπου εδώ εισάγονται οι έννοιες των lifecycle methods που είναι βασικές μέθοδοι ελέγχου του τι εισάγεται και τι εξάγεται από και προς το DOM. Όταν κάποιο component πρόκειται να εμφανιστεί για πρώτη φορά στην οθόνη, τότε μιλάμε για mounting του component στο DOM και την ευθύνη για το πότε και αν και πότε θα γίνεται το mounting την έχουν ειδικές μέθοδοι που ανήκουν στις μεθόδους του κύκλου ζωής. class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } componentDidMount() { } componentWillUnmount() { } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> 14
  15. 15. </div> ); } } Η μέθοδος componentDidMount() καλείται αυτόματα όταν το DOM κάνει render το περιεχόμενο του component. Όταν η δουλεία του render τελειώσει σειρά έχει η componentWillUnmount (). To παράδειγμα του ρολογιού για να δουλέψει χρειάζεται μια αλλαγή κάθε δευτερόλεπτο, και το κατάλληλο σημείο για να αλλάξει το ρολόι μας είναι ακριβώς όταν το DOM τελειώσει με το rendering του περιεχομένου του component. Επίσης η componentWillUnmount() θα καθαρίζει το ρολόι. componentDidMount() { this.timerID = setInterval( () => this.tick(), 1000 ); } componentWillUnmount() { clearInterval(this.timerID); } Είναι σημαντικό να μπορούμε να αλλάζουμε το state. Αυτό γίνεται με ειδική μέθοδο που λέγεται setState. tick() { this.setState({ date: new Date() }); } Κάθε δευτερόλεπτο καλείται η tick και αλλάζει το πεδίο date του state του Clock, στην καινούρια ώρα. Όταν θέλουμε να αλλάξουμε ένα state αυτό γίνεται μόνο με τη setState και όχι με τον τελεστή ‘=’. Η μοναδική περίπτωση που το παραπάνω γίνεται με το ‘=’ είναι στον κατασκευαστή του component και ποτέ άλλοτε. 15
  16. 16. Composition Η React μας δίνει έναν τρόπο να δηλώσουμε την κληρονομικότητα. Αυτό γίνεται με ένα ειδικό πεδίο του props που ονομάζεται children. function FancyBorder(props) { return ( <div className={'FancyBorder FancyBorder-' + props.color}> {props.children} </div> ); } function WelcomeDialog() { return ( <FancyBorder color="blue"> <h1 className="Dialog-title"> Welcome </h1> <p className="Dialog-message"> Thank you for visiting our spacecraft! </p> </FancyBorder> ); } ReactDOM.render( <WelcomeDialog />, document.getElementById('root') ); Οτιδήποτε υπάρχει στο JSX tag με όνομα FancyBorder μεταχειρίζεται από την React σαν ένα children prop. Αφού το FancyBorder εμφανίζει props.children μέσα στο div, τα στοιχεία που πέρασαν, εμφανίζονται στο τελικό αποτέλεσμα. 1.3 Λίγα λόγια για την Angular Η Angular( συχνά αναφέρεται σαν ‘Angular 2+’ ή ‘Angular v2 και πάνω’) είναι μία open-source front-end πλατφόρμα web εφαρμογών βασισμένη σε Typescript, με επικεφαλής την ομάδα της Angular στη Google και από μία κοινότητα προγραμματιστών και άλλων επιχειρήσεων. Η Angular είναι μία πλήρης επανεγγραφή από την ίδια ομάδα που δημιούργησε την Angular JS. Αρχικά, η επανεγγραφή της Angular JS ονομάστηκε Angular 2 από την ομάδα ανάπτυξης, αλλά αυτό προκάλεσε σύγχυση στους developers. Για να διαλευκανθεί το ζήτημα η 16
  17. 17. ομάδα ανακοίνωσε ότι πρέπει να χρησιμοποιούνται διαφορετικοί όροι για κάθε framework, με την Angular JS να αναφέρεται στις εκδόσεις 1.Χ και την Angular χωρίς το JS στις εκδόσεις 2 και πάνω. Η Angular είναι μία πλατφόρμα και framework για τη δημιουργία εφαρμογών client σε HTML και Typescript. Η Angular είναι γραμμένη σε Typescript. Υλοποιεί βασική και προαιρετική λειτουργικότητα ως ένα set βιβλιοθηκών Typescript, τις οποίες κάνουμε import στις εφαρμογές μας. Υπακούει στο μοντέλο Model View Controller ή MVC. Ng Modules Τα βασικά δομικά στοιχεία μίας Angular εφαρμογής είναι τα NgModules, τα οποία προσφέρουν μία συλλογή τρόπων δήλωσης των components. Μία Angular εφαρμογή ορίζεται από ένα set με NgModules. Κάθε εφαρμογή έχει πάντα ένα root module το οποίο επιτρέπει bootstrapping, και τυπικά έχει πολλά ακόμα feature modules. Παρακάτω φαίνεται ένα απλό root Ng Module. import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; @NgModule({ imports: [ BrowserModule ], providers: [ Logger ], declarations: [ AppComponent ], exports: [ AppComponent ], bootstrap: [ AppComponent ] }) export class AppModule { } Components Ένα component ελέγχει ένα κομμάτι οθόνης που το αποκαλούμε View. Ορίζουμε τη λογική του component μέσα σε μία κλάση. Η κλάση αλληλεπιδρά με το View μέσω ενός API από properties και methods. export class HeroListComponent implements OnInit { heroes: Hero[]; selectedHero: Hero; constructor(private service: HeroService) { } ngOnInit() { this.heroes = this.service.getHeroes(); } selectHero(hero: Hero) { this.selectedHero = hero; } } 17
  18. 18. Στο παράδειγμα βλέπουμε τη δήλωση δύο properties (heroes , selectedHero), επίσης έχουμε και το selectedHero() το οποίο είναι ένα method. Αυτή η μέθοδος κάνει set ένα selectedHero (property), όταν ο χρήστης κάνει click σε ένα hero από την λίστα. Το component λαμβάνει τους heroes από το service(θα αναλυθεί παρακάτω), το οποίο είναι μία Typescript παράμετρος στον constructor. Η Angular δημιουργεί(create), ανανεώνει(update) και καταστρέφει(delete) components καθώς ο χρήστης “κινείται” μέσα στην εφαρμογή. Αυτό επιτυγχάνεται με τη χρήση lifecycle μεθόδων όπως η ngOnInit(). Στο παρακάτω παράδειγμα φαίνονται τα πιό σημαντικά options που έχει το @Component. @Component({ selector: 'app-hero-list', templateUrl: './hero-list.component.html', providers: [ HeroService ] }) export class HeroListComponent implements OnInit { /* . . . */ } selector: Λέει στην Angular να δημιουργήσει και να εισάγει ένα instance αυτού του component όταν βρει το συγκεκριμένο tag(στην περίπτωσή μας ‘app-hero-list’) μέσα στο template HTML. TemplateUrl: Μας δείχνει το HTML του συγκεκριμένο component. Μπορεί να είναι ένα path όπως φαίνεται στο παράδειγμα ή μπορούμε να γράψουμε inline HTML. Providers:Ένας πίνακας από providers για τα services που χρειάζεται το component. Θα πούμε περισσότερα γι΄ αυτό παρακάτω. Template Μία ακόμα έννοια που χρειάζεται επισήμανση είναι τα templates. Κατά τον ορισμό ενός View σε ένα component χρειαζόμαστε και το πεδίο template, το οποίο στην πράξη είναι μια φόρμα HTML που “λέει” στην Angular πώς να απεικονίσει το component. Ένα template φαίνεται σαν κανονική HTML εκτός από το γεγονός ότι μπορεί να περιέχει Αngular template σύνταξη. Αυτό μπορεί να αλλάζει την απεικόνιση του component ανάλογα με τη λογική της εφαρμογής. <h2>Hero List</h2> <p><i>Pick a hero from the list</i></p> <ul> <li *ngFor="let hero of heroes" (click)="selectHero(hero)"> {{hero.name}} </li> </ul> <app-hero-detail *ngIf="selectedHero" [hero]="selectedHero"></app- hero-detail> 18
  19. 19. Σε αυτό το template βλέπουμε στοιχεία από την κλασσική HTML όπως το <h2> και <p>, αλλά και template-syntax όπως το *ngFor, (click), {{hero.name}} και *ngIf. Συγκεκριμένα το *ngFor δείχνει στην Angular να “τρέξει” μια λίστα. Τα {{hero.name}}, (click) και [hero] κάνουν bind δεδομένα του προγράμματος, ανάλογα με το input του χρήστη. Τέλος το <app-hero-detail> tag αντιπροσωπεύει ένα καινούριο component. Αυτό το component ορίζει το View του πεδίου(child) hero-detail. Data Binding H Angular υποστηρίζει two-way data binding. Αυτό είναι ένας μηχανισμός με τον οποίο γίνεται ο συντονισμός ανάμεσα στα στοιχεία του template και του component. <li>{{hero.name}}</li> <app-hero-detail [hero]="selectedHero"></app-hero-detail> <li (click)="selectHero(hero)"></li> Στo παράδειγμα το {{hero.name}} εμφανίζει την τιμή του hero.name property μέσα σε <li>. To [hero] είναι ένα property binding με το οποίο περνάμε την τιμή του selectedHero από τον γονέα(parent) στο παιδί(child). To (click) είναι ένα event binding. Όταν ο χρήστης κάνει click πάνω σε ένα hero τότε καλείται η μέθοδος του component selectedHero(). Pipes Τα pipes στην Angular μας επιτρέπουν να δηλώνουμε με τι τρόπο θα εμφανίζονται κάποιες τιμές μέσα σε ένα template π.χ. (ημερομηνία). Μία κλάση η οποία έχει το @Pipe, ορίζει μία συνάρτηση που μεταμορφώνει τις τιμές εισόδου σε τιμές εξόδου έτσι ώστε να εμφανιστούν σε ένα View. Για να ορίσουμε μία τιμή μεταμόρφωσης(transformation value) σε ένα template χρησιμοποιούμε το “|”. {{value | pipe_name}} Επίσης μπορούμε να βάλουμε και arguments σε ένα pipe, τα οποία καθορίζουν με ποιόν τρόπο θα γίνει ο μετασχηματισμός(transformation) π.χ. <!-- Default format: output 'Jun 15, 2015'--> <p>Today is {{today | date}}</p> <!-- fullDate format: output 'Monday, June 15, 2015'--> <p>The date is {{today | date:'fullDate'}}</p> <!-- shortTime format: output '9:43 AM'--> <p>The time is {{today | date:'shortTime'}}</p> Services και Dependency Injection 19
  20. 20. Το service είναι μία ευρεία κατηγορία που μπορεί να περιλαμβάνει τιμή,συνάρτηση ή χαρακτηριστικό που μπορεί να χρειαστεί. Το service τυπικά είναι μία κλάση που έχει ένα συγκεκριμένο και καλά ορισμένο σκοπό. Πρέπει να κάνει κάτι συγκεκριμένο και να το κάνει καλά. Ένα component μπορεί να αναθέσει διάφορες λειτουργίες σε ένα Service, τέτοιες όπως να φέρει δεδομένα από κάποιο server, να επικυρώσει το input ενός χρήστη ή να κάνει καταγραφή κατευθείαν στην κονσόλα. Ορίζοντας τέτοια επεξεργαστικά καθήκοντα σε ένα injectable service class ,κάνουμε αυτά τα καθήκοντα(tasks) διαθέσιμα σε όλα τα components. Ακολουθεί ένα παράδειγμα με services. export class Logger { log(msg: any) { console.log(msg); } error(msg: any) { console.error(msg); } warn(msg: any) { console.warn(msg); } } Επίσης τα services μπορούν να βασίζονται σε άλλα services. Ακολουθεί παράδειγμα. export class HeroService { private heroes: Hero[] = []; constructor( private backend: BackendService, private logger: Logger) { } getHeroes() { this.backend.getAll(Hero).then( (heroes: Hero[]) => { this.logger.log(`Fetched ${heroes.length} heroes.`); this.heroes.push(...heroes); // fill cache }); return this.heroes; } } Σε αυτό το παράδειγμα φαίνεται πως η HeroService χρησιμοποιεί δύο ακόμα services τη logger , που όπως είδαμε παραπάνω χρησιμοποιείται για να εμφανίζει στην κονσόλα και τη BackendService η οποία χρησιμοποιείται για πάρουμε τους heroes. Dependency Injection(DI) Το DI είναι συνδεδεμένο με την Angular και χρησιμοποιείται παντού για να προσφέρει στα νέα components τα services ή οτιδήποτε άλλο χρειάζεται. Μπορούμε να κάνουμε inject ένα service σε ένα component δίνοντάς του έτσι πρόσβαση στο συγκεκριμένο service. Για να ορίσουμε μία κλάση ως service χρησιμοποιούμε το @Injectable() έτσι ώστε να προμηθεύσουμε τα metadata,τα οποία επιτρέπουν στην Angular να το κάνει inject σε ένα component σαν dependency. Ένα dependency δεν χρειάζεται να είναι αναγκαστικά service, μπορεί να είναι μία συνάρτηση ή μία τιμή. Όταν δημιουργείται μία νέα κλάση στην Angular,καθορίζεται τι services και dependencies θα έχει. Αύτο φαίνεται στον constructor του component. π.χ. 20
  21. 21. constructor(private service: HeroService) { } Εδώ φαίνεται ότι το component με τον παραπάνω constructor θα μπορεί να χρησιμοποιεί ένα service με το όνομα HeroService. Για κάθε service πρέπει να γίνεται εγγραφή τουλάχιστον ενός provider. Η εγγραφή των providers μπορεί να γίνεται στα metadata του service (δηλαδή μέσα στο @Injectable()) ή στο @NgModule ή στα metadata του @Component. Όταν χρησιμοποιούμε το Angular CLI command για να δημιουργήσουμε ένα service (ng generate service), η Angular από μόνη της περιλαμβάνει τα metadata του provider στον @Injectable(). @Injectable({ providedIn: 'root', }) Όταν κάνουμε εγγραφή ενός provider με ένα συγκεκριμένο NgModule,τότε το ίδιο instance του service είναι διαθέσιμο σε όλα τα components αυτού του NgModule. @NgModule({ providers: [ BackendService, Logger ], ... }) Observable Τα observables είναι μια λίγο πιο προχωρημένη αλλά και εξαιρετικά σημαντική έννοια στην Angular. Τα observable προσφέρουν υποστήριξη στη μεταφορά μηνυμάτων μεταξύ publishers(πομπός) και subscribers(δέκτη) στην εφαρμογή. Τα Observable έχουν σημαντικά πλεονεκτήματα σε σχέση με άλλες τεχνικές όσον αφορά τον χειρισμό event, τον ασύγχρονο προγραμματισμό, και τον χειρισμό πολλαπλών τιμών. Directives Τα directives είναι οδηγίες για το πώς θα εμφανίζεται οτιδήποτε στην οθόνη. Τα τρια είδη των directives είναι comments, structural directives και attribute directives. Η βασική σύνταξη είναι η εξής: 21
  22. 22. <p appHighlight>Highlight me!</p> στο html αρχείο import { Directive } from '@angular/core'; @Directive({ selector: '[appHighlight]' }) export class HighlightDirective { constructor() { } } στο .ts αρχείο Decorators To χαρακτηριστικό αυτό έρχεται απ’ την Typescript και δίνει τη δυνατότητα να κοσμούμε τις κλάσεις μας. Στο παρακάτω παράδειγμα δηλώνουμε τον decorator course @course class Person { firstName; lastName; constructor(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } } function course(target) { Object.defineProperty(target.prototype, 'course', {value: () => "Angular 2"}) } 1.4 Βοηθητικά εργαλεία που θα χρησιμοποιηθούν 22
  23. 23. Για να γίνει δυνατή η υλοποίηση μιας τέτοιας σύγκρισης, είναι επιτακτική η ανάγκη χρήσης και τρίτων υπηρεσιών. Στο πλαίσιο της εργασίας μας δόθηκε η ευκαιρία να ασχοληθούμε όχι μόνο με τη React και την Angular αλλά και με μια ποικιλία υπηρεσιών και εφαρμογών για προγραμματιστές που στοχεύουν να κάνουν τη ζωή λίγο πιο εύκολη. Node.JS To Node.js είναι ένα ανοικτού κώδικα και cross-platform περιβάλλον που εκτελεί JavaScript και εκτός browser. Το Νode.js μας επιτρέπει τη χρήση εργαλείων από τη γραμμή εντολών (command line) καθώς και server-side scripting. Αυτός είναι ο τρόπος που εκτελούνται τα frameworks με τα οποία ασχολούμαστε. Κάθε ξεχωριστή βιβλιοθήκη, κάθε macro, κάθε καινούριος κατάλογος και αρχείο, γίνεται από εδώ. NPM To NPM είναι ένας μηχανισμός διαμοίρασης κώδικα JavaScript που βασίζεται στο Node.js. Το npm μας επιτρέπει να κατεβάζουμε βιβλιοθήκες και να τις εγκαθιστούμε στο project μας. Λειτουργεί διαβάζοντας τα τοπικά dependencies από ένα αρχείο που συνήθως έχει την ονομασία package.json. Εκεί περιγράφονται ονόματα και εκδόσεις βιβλιοθηκών. Οι εντολές ξεκινούν στο τερματικό με τη λέξη npm και δίπλα ένα καθοριστικό π.χ install ακολουθούμενο προαιρετικά από το όνομα της βιβλιοθήκης που θέλουμε να εγκαταστήσουμε. Δηλαδή όταν γράψουμε το command line >npm install geolib τότε, ανοίγει σύνδεση με το online-registry του npm και ψάχνει τη βιβλιοθήκη geolib. Αν υπάρχει νεότερη έκδοση απ’ αυτή που αναγράφεται στο αρχείο pacakge.json ή αν δεν υπάρχει καθόλου, τότε ο κώδικας κατεβάζεται και εγκαθίσταται σε κατάλληλο κατάλογο μέσα σε υποκατάλογο του τρέχοντος project. Η βάση δεδομένων 23
  24. 24. Προφανώς, οι 4 εφαρμογές που θα υλοποιηθούν δε θα στηρίζονται σε στατικά δεδομένα από κάποιο αρχείο (mock file), αλλά θα εισάγουν τα δεδομένα από μια βάση δεδομένων η οποία θα είναι κοινή πλατφόρμα για όλες. Αυτό πρακτικά σημαίνει ότι θα επικοινωνούν μεταξύ τους, μοιραζόμενες τα ίδια δεδομένα. Η ανάγκη για χρήση των δεδομένων από κινητές συσκευές, μας ανάγκασε να ξεφύγουμε από την ιδέα μιας τοπικής βάσης δεδομένων που θα ήταν βασισμένη σε Apache και θα ήταν γραμμένη σε SQL. Η Google μας παρέχει μια δωρεάν υπηρεσία που ονομάζεται Firebase. Η Firebase είναι μια online υπηρεσία βάσης δεδομένων. Αποθηκεύει τα δεδομένα μας σε μορφή JSON tree. Επιτρέπει εισαγωγή,διαγραφή και τροποποίηση δεδομένων. Η εισαγωγή μπορεί να γίνει είτε απευθείας, μέσω του console ή μέσω της βιβλιοθήκης σε JavaScript που παρέχεται από τη Firebase. Εμείς αφού θέλουμε δυναμικές καταχωρήσεις δεν μπορούμε παρά να ακολουθήσουμε τη δεύτερη τεχνική. Η Firebase διατηρεί ένα root element που είναι ο πατέρας όλων. Ύστερα, ανάλογα με το ποια μέθοδο χρησιμοποιούμε μπορούμε να δημιουργήσουμε κλαδιά ή φύλλα. Για κάθε κλάδο ή φύλλο, η Firebase φροντίζει να δίνει ένα δυναμικό και μοναδικό hash key για να μπορούμε να τα αναγνωρίζουμε, όταν για παράδειγμα χρειαστεί να διαβάσουμε μια συγκεκριμένη τιμή. Το ίδιο ισχύει και για τις υπόλοιπες λειτουργίες, με αρχή το μοναδικό κλειδί και χρησιμοποιώντας τις κατάλληλες μεθόδους που παρέχει η βιβλιοθήκη της Firebase. Η μορφή της Firebase: 24
  25. 25. Παράδειγμα κώδικα από τη βιβλιοθήκη function writeUserData(userId, name, email, imageUrl) {   firebase.database().ref('users/' + userId).set({     username: name,     email: email,     profile_picture : imageUrl   }); } Το παραπάνω παράδειγμα δηλώνει μια συνάρτηση η οποία κάνει χρήση της μεθόδου set πάνω σε ένα reference της βάσης. To reference είναι το hash key του πατέρα πάνω στον οποίο θα γραφούν νέα πεδία username,email και profile_picture. Firebase Authentication Όπως είπαμε και προηγουμένως, η Firebase είναι μια δυνατή online λύση για βάση δεδομένων που στοχεύει σε κινητές εφαρμογές. Όμως δε μένει μόνο εκεί, καθώς παρέχει και υπηρεσίες ταυτοποίησης χρηστών. H Firebase δίνει δυνατότητα σύνδεσης χρηστών ποικιλοτρόπως, από e-mail μέχρι GitHub. Εμείς σε όλες τις εφαρμογές θα χρησιμοποιούμε το Facebook Authentication. Θα δείξουμε παρακάτω πώς ενεργοποιούμε τη δυνατότητα στο Firebase console. Facebook OΑuth Εκτός από τους χρήστες Facebook που θα είναι εγγεγραμμένοι ως χρήστες του Firebase, είναι ανάγκη να ζητάμε τα στοιχεία των χρηστών και να τους δίνουμε τη δυνατότητα να συνδεθούν στις εφαρμογές μας, μέσω του λογαριασμού τους στο Facebook. Έτσι θα εισάγουμε στις εφαρμογές τμήμα κώδικα που μας παρέχει το Facebook ώστε να γίνεται η ταυτοποίησή τους. Babel To Babel είναι ένας compiler για JavaScript. Η React το χρησιμοποιεί για να μεταφράσει τη JSX σε JavaScript δηλαδή σε γλώσσα που καταλαβαίνει ο browser. Υπάρχουν επίσης και online compilers όμως εμείς δε θα τους χρησιμοποιήσουμε. TypeScript 25
  26. 26. Είναι γλώσσα για scripting της Microsoft που προσθέτει κάποιες στατικές εντολές στη JavaScript. Χρησιμοποιείται από την Angular. React Native Είναι η React για κινητά. Παρέχει δυνατότητα ανάπτυξης hybrid και cross-platform εφαρμογών απλά γράφοντας σε διαφορετικό αρχείο του κώδικα. Οι κύριες διαφορές της με την κανονική React είναι στο κομμάτι του View. Θα τη χρησιμοποιήσουμε στην ανάπτυξη της Android εκδοχής του project. Expo Το Expo είναι μια υπηρεσία που μας επιτρέπει να κάνουμε ένα test build της mobile έκδοσης για τη React. Αποτελείται από δύο κομμάτια. Τη βιβλιοθήκη που εγκαθίσταται μέσω NodeJS και την εφαρμογή Android που γίνεται τελικά το build. Δεν απαιτείται η εγκατάσταση του Android APK στον υπολογιστή μας καθώς το Expo μας παρέχει ό,τι χρειάζεται για το build μιας native Android εφαρμογής. Επίσης έχει τη δική του βιβλιοθήκη για Facebook Authentication που διευκολύνει αρκετά τη λειτουργία αυτή στο κινητό. Θα εξηγήσουμε παρακάτω το πώς “στήνεται” το περιβάλλον και πώς γίνεται το build. Ionic To Ιοnic είναι ένα framework που αποσκοπεί στο χτίσιμο μιας native εφαρμογής. Θα το χρησιμοποιήσουμε ως υπόβαθρο για την κινητή εφαρμογή της Angular. Αφορά πιο πολύ στο κομμάτι του View δηλαδή παρέχει έναν τρόπο δήλωσης ετικετών που περιγράφουν ένα κομμάτι της οθόνης. To πώς συμπεριφέρεται όμως το κάθε κομμάτι, το αναλαμβάνει η Angular εξολοκλήρου. <ion-menu [content]="content"> <ion-header> <ion-toolbar> <ion-title>Menu</ion-title> </ion-toolbar> </ion-header> <ion-content> <ion-list> <button ion-item (click)="openPage(homePage)"> Home </button> <button ion-item (click)="openPage(friendsPage)"> Friends </button> <button ion-item (click)="openPage(eventsPage)"> Events </button> <button ion-item (click)="closeMenu()"> Close Menu </button> </ion-list> </ion-content> </ion-menu> <ion-nav id="nav" #content [root]="rootPage"></ion-nav> 26
  27. 27. Το παραπάνω παράδειγμα εμφανίζει στην οθόνη ένα κουμπί με όνομα Toggle Menu το οποίο όταν πατηθεί μας εμφανίζει ένα μενού με τη μορφή drawer, και ένα menu button. Είναι χαρακτηριστικό της Ionic σύνταξης, που μοιάζει με HTML και υποδηλώνει το τι θα εμφανίζεται στην οθόνη. Για παράδειγμα το <ion-header> είναι μια ετικέτα με ειδική σημασία. Όπως δηλώνει και τ’ όνομά της, δεσμεύει χώρο στην οθόνη για μια επικεφαλίδα, στην προκειμένη η λέξη Menu. Cordova Τρέχει ένα επίπεδο πιο κάτω και είναι υπεύθυνο για το πακετάρισμα ή τη μετατροπή μιας HTML 5 εφαρμογής σε native εφαρμογή, Αndroid και iOS. Είναι ένα συστατικό στοιχείο του Ionic. Ionic DevApp Παρόμοιο με το Expo αλλά για την Angular. Παρέχει τη δυνατότητα για build εφαρμογών που έχουν ως βάση το Ionic και δεν απαιτεί την εγκατάσταση Android APK. RxJS RxJs η Reactive Extensions για JavaScript είναι μια βιβλιοθήκη που χρησιμοποιεί Observables για να διευκολύνει στη συγγραφή κώδικα που εκτελεί ασύγχρονες λειτουργίες ή είναι βασισμένος σε callbacks. H έννοια του Observable χρησιμοποιείται κατά κόρον στην Angular, ειδικά όταν χρειάζεται να παραλάβουμε δεδομένα από τη βάση δεδομένων. Παρακάτω θα αναλύσουμε περαιτέρω την έννοια των Observables. Chrome Device Toolbar 27
  28. 28. Είναι ένας εναλλακτικός τρόπος εμφάνισης της εφαρμογής όπως θα έτρεχε στο κινητό τηλέφωνο αλλά στον web browser. Google Maps API και Geolocation Είναι η βιβλιοθήκη της Google για τη χρήση χαρτών σε εφαρμογές JavaScript. Θα τη χρησιμοποιήσουμε αρκετά καθώς θα ενσωματώσουμε χάρτες ως κύρια λειτουργία των εφαρμογών. Το API είναι αρκετά απλό στη χρήση με απλές εντολές και μεθόδους της JavaScript όμως το κάθε εξεταζόμενο framework έχει τις δικές του ιδιοτροπίες όσον αφορά την εμφάνιση στοιχείων Google Maps. Τα πιο συνηθισμένα στοιχεία είναι ένας η περισσότεροι χάρτες και markers. Κομμάτι των εφαρμογών αποτελεί και το Geolocation που συνδυάζεται με τη χρήση GPS συστήματος. Αυτό απαιτεί συνδυασμό Google Maps με την εκάστοτε βιβλιοθήκη για εκμετάλλευση των δεδομένων από το GPS της κινητής συσκευής. ΣΗΜΕΙΩΣΗ : Με την τελευταία αναβάθμιση, για να λειτουργήσουν οι χάρτες χρειάζεται billing λογαριασμός δηλαδή συμπλήρωση αριθμού πιστωτικής/χρεωστικής κάρτας. 28
  29. 29. Κεφάλαιο 2 2.1 Περιγραφή demo εφαρμογής React Για τους στόχους του πειράματος αυτού θα υλοποιηθούν, όπως έχουμε ξαναπεί τέσσερις εφαρμογές, δυο για κάθε framework. Σ’ αυτήν την ενότητα θα ασχοληθούμε με τις δυο εφαρμογές που είναι γραμμένες σε React. Θα περιγράψουμε αναλυτικά τις δυο εφαρμογές σε ό,τι αφορά τις λειτουργίες, και τη δομή τους. Η Γενική ιδέα Θα αναπτύξουμε μια εφαρμογή που θα κρατάει τα πλάνα μας μαζί με χάρτη. Θα υπάρχει ένας τοίχος πολύ απλός-ιδέα από διάφορα social media που πάνω εκεί ο κάθε χρήστης “ποστάρει” το πού θέλει να πάει μαζί με τ’ όνομά του και ένα τίτλο ή περιγραφές. Φυσικά θα μπορούσαν να υπάρχουν και άλλα πεδία όμως δεν εξυπηρετούν κάποιο ιδιαίτερο σκοπό. Desktop Η εφαρμογή θα λειτουργεί ως ένας ενισχυμένος event planer. Και αυτό λόγω των δυνατοτήτων που δίνει η κινητή εφαρμογή, που ολοκληρώνει τη desktop. Ο κάθε χρήστης θα μπορεί να συνδέεται μέσω του λογαριασμού του Facebook. Από εκεί και μετά έχει κάποιες βασικές δυνατότητες που περιγράφονται παρακάτω. Είσοδος-Εγγραφή Με αυτή τη λειτουργία ο χρήστης μπορεί να συνδεθεί στην εφαρμογή μέσω των credentials του λογαριασμού του στο Facebook. Αν ο χρήστης δεν υπάρχει, τότε δημιουργείται στη Firebase αλλά θα πρέπει να δοθεί άδεια developer από το Developer Console του Facebook. Πατώντας το κουμπί Log-in εμφανίζεται ένα αναδυόμενο παράθυρο με Facebook Log-in και αν τα στοιχεία είναι σωστά τότε, εγκαθιδρύεται μια persistent σύνδεση για το χρήστη, και αυτός ανακατευθύνεται αυτόματα στην αρχική σελίδα ή Home. Home/Main Menu 29
  30. 30. Εδώ ο χρήστης μπορεί να βλέπει τα πλάνα που έχουν σηκώσει οι υπόλοιποι χρήστες. Δεν υπάρχει Friend system οπότε θεωρούμε πως όλοι είναι φίλοι με όλους. Επομένως όλοι οι χρήστες μπορούν να δουν όλα τα πλάνα. Εισαγωγή Πλάνου Ένα πλάνο αποτελείται από τρια βασικά στοιχεία. Ένα τίτλο, μια περιγραφή και μια θέση προορισμού στον χάρτη. Και τα τρία είναι προαιρετικά αλλά προτείνεται η εισαγωγή τους. Ο χρήστης μπορεί να σύρει τον marker πάνω στον χάρτη και πατώντας το κουμπί Add Plan το πλάνο εισάγεται στη βάση. Δεν υπάρχουν περιορισμοί όσον αφορά τον τίτλο και την περιγραφή αλλά καλό είναι να είναι μικρού μήκους. Το όνομα του τρέχοντος χρήστη εισάγεται αυτόματα ως πεδίο name του πλάνου. Αν κάποιος είναι στο guest mode δηλαδή δεν έχει συνδεθεί, τότε δε μπορεί να εισάγει πλάνα. MyPlans Εδώ ο χρήστης μπορεί να δει μόνο τα δικά του πλάνα. Να δει ποια έχει εκτελέσει και ποια όχι δηλαδή μπορεί να χρησιμοποιηθεί και ως ιστορικό. Στη mobile έκδοση, εμφανίζεται και η κατάσταση (status) του πλάνου, και δίπλα το ανάλογο εικονίδιο αν είναι τετελεσμένο ή σε αναμονή εκτέλεσης. Διαγραφή Πλάνου Ο χρήστης, από την ενότητα MyPlans μπορεί πατώντας το (Χ) να διαγράψει όποιο πλάνο επιθυμεί. Δεν υπάρχει ερώτηση πριν τη διαγραφή. Mobile Η κινητή εφαρμογή μπορεί να κάνει ό,τι κάνει και το desktop αλλά συμπληρώνει και μερικές ακόμα δυνατότητες. Ολοκλήρωση Πλάνου Ο χρήστης μπορεί πλέον να εκτελέσει τα πλάνα που εισήγαγε από το σταθερό υπολογιστή ή laptop του η ακόμα και απ’ το κινητό του. Όταν πατήσει το κουμπί της εκτέλεσης τότε ανοίγει μια καινούρια οθόνη με τον χάρτη, το marker του προορισμού αλλά και ένα επιπλέον marker που είναι η θέση που βρίσκεται ο χρήστης. Τη θέση λαμβάνει η εφαρμογή απ’ την υπηρεσία GPS της συσκευής που εκτελείται. Επίσης εμφανίζονται οι συντεταγμένες της θέσης του 30
  31. 31. χρήστη. Όταν ο χρήστης φτάσει σε απόσταση μικρότερη των 15 μέτρων τότε το πλάνο τερματίζεται αλλά υπάρχει και δυνατότητα χειροκίνητου τερματισμού. Όταν ένα πλάνο τερματίσει, ο χρήστης ανακατευθύνεται στην οθόνη των πλάνων του ώστε πιθανότατα να εισάγει κάποιο άλλο. 2.2 SETUP περιβάλλοντος Περιβάλλον React Θα δώσουμε μια σειρά βημάτων για το πώς στήνεται το περιβάλλον της React. Συνιστάται ιδιαίτερη προσοχή κατά την εκτέλεσή τους. Για να ξεκινήσει κάποιος μια React εφαρμογή θα πρέπει πρώτα να εγκαταστήσει το περιβάλλον του Node.js. Ο σύνδεσμος περιέχει το εκτελέσιμο αρχείο: https://nodejs.org/en/. Αρχικά πρέπει να δημιουργήσουμε τον root κατάλογο C:UsersusernameDesktop>mkdir reactApp Μετά θέλουμε το πιο βασικό ίσως αρχείο για την εφαρμογή, το package.json. Η παρακάτω εντολή μας το δημιουργεί C:UsersusernameDesktopreactApp>npm init Χρειαζόμαστε και τον Babel compiler για να μπορούμε να βλέπουμε τα αποτελέσματα μεταφρασμένα στον browser. C:UsersusernameDesktopreactApp>npm install -g babel C:UsersusernameDesktopreactApp>npm install -g babel-cli Το webpack και τα αρχεία για τον δοκιμαστικό server C:UsersusernameDesktopreactApp>npm install webpack –save C:UsersusernameDesktopreactApp>npm install webpack-dev-server –save Τα πακέτα της React και του React DOM 31
  32. 32. C:UsersusernameDesktopreactApp>npm install react –save C:UsersusernameDesktopreactApp>npm install react-dom –save Απαραίτητα babel plugins C:UsersusernameDesktopreactApp>npm install babel-core C:UsersusernameDesktopreactApp>npm install babel-loader C:UsersusernameDesktopreactApp>npm install babel-preset-react C:UsersusernameDesktopreactApp>npm install babel-preset-es2015 Πάμε να δημιουργήσουμε τα πρώτα αρχεία κώδικα της εφαρμογής, C:UsersusernameDesktopreactApp>type nul >index.html C:UsersusernameDesktopreactApp>type nul >App.jsx C:UsersusernameDesktopreactApp>type nul >main.js C:UsersusernameDesktopreactApp>type nul >webpack.config.js Ανοίγουμε το αρχείο με όνομα webpack.config.js και συμπληρώνουμε τον ακόλουθο κώδικα. Με αυτό τον κώδικα λέμε ότι το σημείο έναρξης είναι ένα αρχείο με όνομα main.js. Το output path είναι το μέρος που θα τρέχει ο server και η θύρα θα είναι η 8080. Επίσης περιέχονται οι ρυθμίσεις ώστε το Babel να ψάχνει .js αρχεία και να χρησιμοποιεί τα es2015 και react presets. var config = { entry: './main.js', output: { path:'/', filename: 'index.js', }, devServer: { inline: true, port: 8080 }, module: { loaders: [ { test: /.jsx?$/, exclude: /node_modules/, loader: 'babel-loader', query: { presets: ['es2015', 'react'] } 32
  33. 33. } ] } } module.exports = config; Στο package.json αρχείο αλλάζουμε το σημείο που λέει "test" "echo "Error: no test specified" && exit 1" το οποίο βρίσκεται μέσα στο αντικείμενο scripts σε "start": "webpack-dev-server – hot". Ο διακόπτης -hot κάνει αυτόματα τις αλλαγές στη σελίδα μας όταν εμείς πειράζουμε κάτι στον κώδικα. Δε χρειάζεται refresh της σελίδας. Στο αρχείο index.html ρυθμίζουμε το div id=”app” Στο αρχείο App.jsx εισάγουμε τον κώδικα για το πρώτο μας component import React from 'react'; class App extends React.Component { render() { return ( <div> Hello World!!! </div> ); } } export default App; Και στο αρχείο main.js import React from 'react'; import ReactDOM from 'react-dom'; import App from './App.jsx'; ReactDOM.render(<App />, document.getElementById('app')); Με την τελευταία γραμμή υποδεικνύουμε στο DOM να εμφανίζει το element με όνομα App και να το παίρνει από το element με id ‘app’. Αυτό σημαίνει πως το App είναι το Root element όλου του projcet και οτιδήποτε πρόκειται να εμφανιστεί, θα γραφεί εκεί. To DOM όπως έχουμε ξαναπεί είναι το κομμάτι της React υπεύθυνο για ό,τι εμφανίζεται στην οθόνη. Είμαστε έτοιμοι να τρέξουμε τον development server με την εντολή C:UsersusernameDesktopreactApp>npm start 33
  34. 34. Αμέσως ξεκινάει μια διαδικασία δέσμης εντολών που προέρχονται από το webpack. O πειραματικός server είναι στη διεύθυνση http://localhost:8080/. Φυσικά μπορούμε να αλλάξουμε τη θύρα σε οποιαδήποτε επιθυμούμε εφόσον είναι διαθέσιμη. Με αυτή τη διαδικασία είδαμε πώς ξεκινάμε από μηδενικής βάσης να στήσουμε το περιβάλλον της React και μια τετριμμένη εφαρμογή Hello World για να αντικρίσουμε την πρώτη εμφάνιση στο DOM. Περιβάλλον Firebase Είμαστε έτοιμοι να χτίσουμε την εφαρμογή που περιγράψαμε παραπάνω. Σχεδόν έτοιμοι διότι το κομμάτι της βάσης δεδομένων ακόμα δεν έχει φτιαχτεί. Θα χρειαστεί ένας λογαριασμός Google για να συνδεθούμε στην κονσόλα. 1.Στην εξής διεύθυνση https://firebase.google.com/ πατάμε Μετάβαση στην Κονσόλα. 2.Add project και συμπληρώνουμε Project Name και Project ID. 3.Μέσα στον κατάλογο του Project στο path /src δημιουργούμε κατάλογο config 4.Στο path / src / config δημιουργούμε το αρχείο config.js 5.Στην κονσόλα του Firebase διακρίνουμε ένα μενού αριστερά και τρεις επιλογές στο κέντρο. Επιλέγουμε το δεξιά εικονίδιο δηλαδή για web. 6.Στον πίνακα που θα εμφανιστεί πατάμε Copy 7.Paste στο αρχείο config.js 8.Μετασχηματίζουμε τον κώδικα ως εξής: export const DB_CONFIG = { apiKey: "AIzaSyAROAe2cYHN0gS_OeAenhlWzw2zAwTQI1o", authDomain: "omegalul-6fc0c.firebaseapp.com", databaseURL: "https://omegalul-6fc0c.firebaseio.com", projectId: "omegalul-6fc0c", storageBucket: "omegalul-6fc0c.appspot.com", messagingSenderId: "755991972067" }; Αργότερα θα γίνει το σχετικό import στα κατάλληλα αρχεία. 9.Από το περιβάλλον τερματικού πατάμε npm install --save firebase για να κατεβάσουμε και να εγκαταστήσουμε τη βιβλιοθήκη του Firebase στο project. ΣΗΜΑΝΤΙΚΟ: Για να μπορούμε να εκτελέσουμε CRUD λειτουργίες στη βάση, είναι υποχρεωτικό να τροποποιήσουμε τους κανόνες στο RULES tab. Αλλάζουμε την τιμή στο read και write από false σε true. Αν το πεδίο RULES δεν εμφανίζεται σημαίνει ότι η βάση δεν έχει δημιουργηθεί ακόμη. 34
  35. 35. Χρήση του Facebook Σε αυτό το κομμάτι θα δώσουμε τα βήματα για το πώς θα χρησιμοποιήσουμε το Login του Facebook σε συνδυασμό με τη Firebase. 1. Στο κεντρικό μενού της Κονσόλας που εμφανίζεται στα αριστερά πατάμε Authentication. 2. Απ’ το μενού Sign-In method πατάμε Facebook. 3. Στον πίνακα που θα εμφανιστεί ανοίγουμε το κουμπάκι Enable πάνω δεξία και κάνουμε copy to clipboard το link που υπάρχει κάτω κάτω. 4. Συνδεόμαστε στην κονσόλα του developer Facebook στο https://developers.facebook.com και δημιουργούμε ένα νέο App. 5. Στο μενού που θα εμφανιστεί αριστερά επιλέγουμε Settings. 6. Στην κατηγορία Client Oauth settings επιλέγουμε Yes στα εξής: Client Oauth Login, Web Oauth Login και Εmbedded Browser Oauth Login. Στο πεδίο Valid Oauth redirect URIs επικολλούμε το link του βήματος 3 και πατάμε Save Changes. 7. Από το dashboard παίρνουμε τα AppID και App Secret και τα τοποθετούμε στα κενά πεδία του πίνακα του βήματος 3. Πατάμε Save. Google Maps Install και Activation Είδαμε μέχρι στιγμής πώς να στήσουμε το περιβάλλον του framework, πώς να φτιάξουμε τη βάση και πώς να δεχόμαστε Facebook Log-In’s μέσω του Firebase. Ακόμη ένα σημαντικό κομμάτι του project είναι η χρήση Google Maps. Για να έχουμε στη διάθεσή μας τους χάρτες της Google, πρέπει να ζητήσουμε ένα API κλειδί. Αυτό το λαμβάνουμε από την κονσόλα της Google. 1. Συνδεόμαστε στη διεύθυνση https://console.cloud.google.com/google/maps-apis/ 2. Δημιουργούμε ένα project. Είναι αναγκαίο να συμπληρώσουμε και το billing account όταν ζητηθεί 3. Απ’ το μενού αριστερά επιλέγουμε APIs 4. Απ’ τις διαθέσιμες επιλογές διαλέγουμε Μaps Javascript API 5. Πατάμε στο κουμπί enable 6. Απ’ το μενού credentials βρίσκουμε το API key μας που θα χρειαστεί αργότερα. 35
  36. 36. Όσον αφορά τον κώδικα που θα χρησιμοποιήσουμε, υπάρχουν διάφορες υλοποιήσεις στο GitHub και για το παράδειγμά μας εγκαθιστούμε τη βιβλιοθήκη google-map-react. Από περιβάλλον τερματικού και αφού είμαστε στο φάκελο του project, γράφουμε npm install --save google-map-react Περιβάλλον React Native Για τη mobile έκδοση του project θα χρειαστούμε το Expo. Όπως έχουμε ξαναπεί το Expo μας δίνει τη δυνατότητα να τρέξουμε Native εφαρμογές στη συσκευή Αndroid. Θα χρειαστεί να εγκαταστήσουμε και τη βιβλιοθήκη React Native. 1. Επιλέγουμε διαφορετικό φάκελο για το Native project 2. Εγκαθιστούμε τη React Native : npm install -g create-react-native-app 3. Από το PlayStore κατεβάζουμε την εφαρμογή Expo. 4. Εγκαθιστούμε το περιβάλλον του Expo στον κατάλογο του project: npm install expo-cli – global 5. Πατώντας npm start στο τερματικό ανοίγει ο dev server εμφανίζει ένα QR code το οποίο σαρώνουμε με την εφαρμογή Expo που μόλις κατεβάσαμε. 2.3 Δομικά χαρακτηριστικά της εφαρμογής (components/παραδείγματα) Ήρθε η ώρα να εμβαθύνουμε στη desktop εφαρμογή και να εξετάσουμε λεπτομερώς το κάθε κομμάτι. Αρχικά θα εισάγουμε τις βασικές έννοιες της εφαρμογής σαν δομικά στοιχεία. Αυτό σημαίνει πως κάθε αρχείο κώδικα θα έχει συνήθως γραμμένο ένα component. Το κάθε component θα έχει με τη σειρά του τα δικά του attributes που θα χρησιμεύουν κυρίως στο να περνάνε τιμές από το parent-component στο child-component και αντίστροφα. Κάθε αρχείο .js η .jsx συνοδεύεται συνήθως από ένα .css αρχείο που περιέχει απλές δηλώσεις CSS για τη μορφοποίηση των στοιχείων της React. Η εφαρμογή όπως ξαναείπαμε σε παραπάνω ενότητα, είναι ένας ενισχυμένος event planner. Η λογική της React μας συστήνει, κάθε βασική λειτουργία να είναι ένα component. Ξεκινάμε με την παραδοχή πως σε έναν event planner, το δομικό συστατικό, είναι ένα πλάνο. Άρα δεν έχουμε παρά να δημιουργήσουμε έναν κατάλογο με όνομα Note μέσα στον κατάλογο / src του project. 36
  37. 37. Ένα Note θα αποτελείται από πεδία για τον τίτλο, την περιγραφή, τ’ όνομα χρήστη που το ανέβασε (εισάγεται αυτόματα) και φυσικά τον χάρτη με τον marker προόρισμού. Ένα αρχείο React πάντα ξεκινάει με τα βασικά imports import React, { Component } from 'react'; Ακολουθεί προαιρετικά ένας κατασκευαστής κλάσης, καθώς είπαμε ότι ένα component μπορεί να είναι μια κλάση. class Note extends Component{ constructor(props){ super(props); } } Η εντολή constructor(props) υποδεικνύει πως όταν κατασκευάζεται ένα αντικείμενο τότε θα έχει κάποια properties ενώ το super(props) εισάγει κληρονομικά τα όποια properties από προγόνους. Μέσα στον κατασκευαστή είμαστε υποχρεωμένοι να δηλώνουμε τις μεταβλητές μας. Αυτό γίνεται με τον τελεστή this και την τελεία (.) ακολουθούμενη από το όνομα που επιθυμούμε. Για παράδειγμα αν θέλουμε να πούμε πως ένα πλάνο θα έχει όνομα, γράφουμε this.name=”Walkabout”. Εδώ επειδή θα κληρονομούμε τις τιμές από τη φόρμα που ο χρήστης θα τις πληκτρολογεί, χρησιμοποιούμε το ειδικό πεδίο props. Θέλουμε όταν ένα πλάνο δημιουργείται να έχει τιμές από πριν και όχι να είναι άδειο, άρα τις τιμές θα τις κληρονομεί από κάποιον πρόγονο που θα δούμε στη συνέχεια. Για την ώρα, στον κατασκευαστή γράφουμε : this.noteContent = props.noteContent; Αυτό σημαίνει ότι κάθε πλάνο έχει ένα πεδίο noteContent που είναι ο τίτλος και η τιμή του θα είναι ίση με την αντίστοιχη τιμή στο props. Κληρονομείται η τιμή του τίτλου και θα δούμε αργότερα με ποιο τρόπο το parent-component δίνει την τιμή αυτή στο props. this.noteId = props.noteId; Ένα μοναδικό ID για κάθε πλάνο this.handleRemoveNote = this.handleRemoveNote.bind(this); 37
  38. 38. Η δήλωση αφορά μεθόδους που θα χρησιμοποιηθούν απ’ αυτό το component ενώ είναι γραμμένες μέσα σ’αυτό. Δένουμε δηλαδή τη μέθοδο handleRemoveNote σε αυτό το component. this.noteDesc=props.noteDesc; Πεδίο για την περιγραφή this.lat=props.lat; Πεδίο για το latitude του marker προορισμού στον χάρτη this.lng=props.lng; Πεδίο για το longitude του marker προορισμού στον χάρτη this.UsrName=props.UsrName; Πεδίο που περιέχει το όνομα χρήστη που ανέβασε το πλάνο. Συνολικά το constructor έχει ώς εξής: constructor(props){ super(props); this.noteContent = props.noteContent; this.noteId = props.noteId; this.handleRemoveNote = this.handleRemoveNote.bind(this); this.noteDesc=props.noteDesc; this.lat=props.lat; this.lng=props.lng; this.UsrName=props.UsrName; } Μιλήσαμε για μεθόδους του κύκλου ζωής, οι οποίες ρυθμίζουν το πώς θα συμπεριφέρεται το component σε σχέση με το DOM. Για παράδειγμα η componentDidMount θα περιέχει κώδικα που θα εκτελεστεί αμέσως αφού το DOM κάνει mount το component. Όμως η πιο σημαντική μέθοδος του κύκλου ζωής είναι η render(). Είναι εξαιρετικά σπάνιο ένα React component να μην έχει μέσα του κάποια στιγμή τη μέθοδο render(). Τώρα η render() έχει έναν ιδιότυπο τρόπο λειτουργίας καθώς στέλνει στο DOM ό,τι επιστρέφεται από το return. Αυτό που επιστρέφεται πάντα πρέπει να είναι ένα στοιχείο π.χ ένα <div></div> που ενδεχομένως να περιέχει μέσα του εμφωλευμένα στοιχεία. 38
  39. 39. render(){ return( <div className="note fade-in" > <span className="closebtn" onClick={() => this.handleRemoveNote(this.noteId)}> &times; </span> <div className="MyMap" > <div className="UsrName"><p style={{color:'#448496'}}>Posted by:</p><p style={{paddingLeft:'105px'}}>{this.UsrName}</p></div> <MyMap2 googleMapURL='https://maps.googleapis.com/maps/api/js? v=3.exp&libraries=geometry,drawing,places' loadingElement={<div style={{ height: '5%' }} />} containerElement={<div style={{ height: '5px' }} />} mapElement={<div style={{ height: '0%' }} />} lat={this.lat} lng={this.lng} /></div> <div className="noteContent">Title:{ this.noteContent }</div> <div className=" noteDesc">Description:{ this.noteDesc}</div> </div> ) } Θα εξηγήσουμε ένα ένα τα περιεχόμενα της render (). Πολλά ήδη γνώριμα από HTML, JavaScript. Το κουμπί διαγραφής ενός πλάνου. <span className="closebtn" onClick={() => this.handleRemoveNote(this.noteId)}> &times; </span> Η onClick() “ακούει” για γεγονός πατήματος του κουμπιού και όταν αυτό πατηθεί τότε καλείται η μέθοδος που αναγράφεται με όρισμα το noteId που υπάρχει από τον constructor. 39
  40. 40. <div className="UsrName"><p style={{color:'#448496'}}>Posted by:</p><p style={{paddingLeft:'105px'}}>{this.UsrName}</p></div> Εδώ έχουμε τη δήλωση ενός div με κλάση UsrName που είναι το όνομα κλάσης css. Το div περιέχει δυο απλά κείμενα το ένα στατικό το άλλο δυναμικό. Τα {…} είναι η βασική JSX σύνταξη. Μέσα εκεί μπορούν να γίνουν αναθέσεις, πράξεις με τελεστές και τελεστέους και άλλες εκφράσεις της JavaScript. Κάθε φορά θα εμφανίζεται διαφορετικό username ανάλογα με την τιμή του πεδίου this.UsrName. Σειρά του χάρτη να κάνει την εμφάνισή του. Πρώτα πρέπει να τον εισάγουμε στο αρχείο μας. import MyMap2 from '../map2'; Aς φτιάξουμε το αρχείο για να δηλωθεί ο χάρτης. Πρώτα τα απαραίτητα imports απ’ τη βιβλιοθήκη της Google-map-react import React, { Component } from 'react'; import GoogleMapReact from 'google-map-react'; import Marker from 'google-map-react'; Ακολουθεί η δήλωση του component με όνομα MyMap2 class MyMap2 extends React.Component { constructor(props){ super(props); this.lat=props.lat; this.lng=props.lng; } Κληρονομούνται τα lat και lng του από όποιον (component) θελήσει να χρησιμοποιήσει τον χάρτη state = { center: [60.938043, 30.337157], zoom: 9, draggable: false, lat: this.props.lat, lng: this.props.lng }; Δηλώνεται το object state με τα πεδία που είναι απαραίτητα για ένα χάρτη. Το center είναι το σημείο εστίασης του χάρτη. To zoom είναι το πόσο θα ζουμάρει/ξεζουμάρει ανάλογα με τον αριθμό στα δεξιά. Το draggable είναι boolean πεδίο που επιτρέπει ή όχι, αναλόγως την τιμή, αν 40
  41. 41. θα μπορεί ο χρήστης να σύρει τον χάρτη. Υπάρχουν και τα πεδία lat lng για παίρνουμε τις συντεταγμένες του marker από τον constructor. Ακολουθεί μια συνάρτηση onChange για να αλλάζουν τα states του κέντρου και του ζουμ όποτε χρειάζεται. _onChange = ({center, zoom}) => { this.setState({ center: center, zoom: zoom, }); } Παρατηρούμε την εντολή this.setState({…}). Πρόκειται περί ειδικής μεθόδου της React που χρησιμοποιείται κατά κόρον και είναι απ’ τις πιο σημαντικές για τη λειτουργία της. Αυτό που κάνει είναι να αλλάζει τις καταστάσεις των μεταβλητών που βρίσκονται στα αριστερά με τις τιμές που βρίσκονται στα δεξιά. Η αλλαγή αυτή δε γίνεται ακαριαία αλλά μόνο όταν γίνει η επόμενη φόρτωση (mount) στο DOM . Ώρα για τη συνάρτηση render () δηλαδή τι θα δείχνει το component που πριν λίγες στιγμές δηλώθηκε, όταν φορτώνεται το DOM. render(props) { return ( <GoogleMapReact draggable={this.state.draggable} onChange={this._onChange} center={[this.state.lat,this.state.lng]} zoom={this.state.zoom} > <div className="marker" lat={this.state.lat} lng={this.state.lng}> </div> </GoogleMapReact> ); } 41
  42. 42. Το ειδικό tag <GoogleMapReact …> μαζί με τα διαφορά props του είναι αυτό που σε τελική ανάλυση εμφανίζει τον χάρτη. Στο τέλος κάθε αρχείου δήλωσης component γράφουμε πάντα export default MyMap2; αν θέλουμε να μπορούμε να το κάνουμε import σε άλλα αρχεία κώδικα. Είδαμε πώς γίνεται η δήλωση του χάρτη. Το component MyMap2 θα χρησιμοποιηθεί τώρα ως δομικό συστατικό για το πλάνο μας. <MyMap2 googleMapURL='https://maps.googleapis.com/maps/api/js? v=3.exp&libraries=geometry,drawing,places' υποχρεωτικό attribute loadingElement={<div style={{ height: '5%' }} />} containerElement={<div style={{ height: '5px' }} />} mapElement={<div style={{ height: '0%' }} />} πρέπει να δηλωθούν διαστάσεις αλλιώς υπάρχει κίνδυνος ο χάρτης να μην εμφανιστεί καθόλου lat={this.lat} lng={this.lng} /> Τέλος έχουμε τα δυναμικά και ανάλογα κατάστασης πεδία τίτλου και περιγραφής: <div className="noteContent"> Title:{this.noteContent }</div> <div className=" noteDesc"> Description:{this.noteDesc}</div> Ας δημιουργήσουμε τώρα μια απλή φόρμα με δυο πεδία και ένα κουμπί ώστε να εισάγει ο χρήστης τα πλάνα του. To component που θα δημιουργήσουμε δε θα περιέχει τον χάρτη. Θα εξεταστεί μόνος του. Ξεκινάμε με τα απαραίτητα imports και τη δήλωση του constructor. import React, { Component } from 'react'; import './NoteForm.css'; 42
  43. 43. class NoteForm extends Component{ constructor(props){ super(props); this.state = { newNoteContent: '', newDescContent: '', }; } } Αρχικοποιούμε τις καταστάσεις των μεταβλητών newNoteContent και newDescContent με το κενό string. Έτσι όποτε δημιουργείται ένα component τύπου NoteForm θα έχει δύο κενά πεδία. Στη συνέχεια έρχεται η μέθοδος render(). render(){ return( <div className="formWrapper"> <input className="noteInput" placeholder="Plan title" value={this.state.newNoteContent} onChange={this.handleUserInput} /> <input className="noteInput" placeholder="Description" value={this.state.newDescContent} onChange={this.handleUserInput2} /> <button className="noteButton" onClick={this.writeNote}>Add Plan</button> </div> ) } Η παρακάτω δήλωση αποτελεί κλασσική σύνταξη JSX με το χαρακτηριστικό input tag, ακολουθούμενο από διάφορα attributes εν προκειμένω className για το css, placeholder για το τι θα φαίνεται μέσα το input box μέχρι ο χρήστης να πατήσει πάνω του, value για το τι τιμή θα πάρει και onChange για το τι θα συμβαίνει όταν το περιεχόμενο του αλλάξει. <input className="noteInput" placeholder="Plan title" value={this.state.newNoteContent} onChange={this.handleUserInput} /> 43
  44. 44. Το πεδίο value φαίνεται να έχει το this.state.newNoteContent δηλαδή από αυτό το component και από την ειδική μεταβλητή state και απ’ το κομμάτι της state που λέγεται newNoteContent. H onChange είναι ένα event listener που ψάχνει για αλλαγές. Όταν ο χρήστης πατήσει το επιθυμητό string τότε ενεργοποιείται η onChange που μέσα της έχει γραμμένο this.handleUserInput δηλαδή μια μέθοδο που θα κάνει κάτι με τα δεδομένα του χρήστη. <button className="noteButton" onClick={this.writeNote}>Add Plan</button> Κουμπί που περιμένει το γεγονός onClick. Όταν πατηθεί ενεργοποιεί τη writeNote. Προσθέτουμε τις απαραίτητες δηλώσεις στον constructor. class NoteForm extends Component{ constructor(props){ super(props); this.state = { newNoteContent: '', newDescContent: '', }; this.handleUserInput = this.handleUserInput.bind(this); this.handleUserInput2=this.handleUserInput2.bind(this); this.writeNote = this.writeNote.bind(this); } } Πάλι εδώ όπως και πριν πρέπει να συνδέσουμε με το bind τις μεθόδους, σε αυτό το component. Ας δούμε πώς λειτουργούν οι μέθοδοι. Έστω ότι ο χρήστης έγραψε κάτι στο ένα input box. Αμέσως καλείται η onChange εφόσον υπήρξε αλλαγή και έχει όρισμα το e. Αυτό σημαίνει ότι έχει τα δεδομένα του input box. Αυτό που εκτελεί η μέθοδος είναι μια αλλαγή κατάστασης από αυτό που υπήρχε πριν, στη νέα κατάσταση που είναι το νέο κείμενο του χρήστη. Την αλλαγή αυτή την επωμίζεται πάντα η setState. handleUserInput(e){ this.setState({ newNoteContent: e.target.value, }) } Τώρα, η writeNote είναι λίγο πιο σύνθετη καθώς χρησιμοποιεί props για να περάσει τα δεδομένα στο πιο πάνω επίπεδο. 44
  45. 45. writeNote(){ this.props.addNote(this.state.newNoteContent,this.state.newDescContent); this.setState({ newNoteContent: '', newDescContent: '', }) } Εδώ κάνουμε χρήση ενός property του component για πρώτη φορά. Συγκεκριμένα το prop είναι μια μέθοδος με δυο ορίσματα, τίτλος και περιγραφή. Εύλογα θα ρωτούσε κανείς, που δηλώνεται αυτό το prop. Η απάντηση είναι πως θα το δημιουργήσουμε όταν το component χρησιμοποιηθεί κάπου. Στη συνέχεια πρέπει να αρχικοποιηθούν οι τιμές ώστε να μην έχουμε τις παλιές. Αυτή τη δουλειά την εκτελεί η setState. Κομμάτι της φόρμας όμως αποτελεί και ένας χάρτης με ένα marker που ο χρήστης σύρει προς την επιθυμητή περιοχή. Μαζί με τα υπόλοιπα στοιχεία (τίτλο,περιγραφή) ολοκληρώνεται ένα πλάνο. Πρέπει να δηλώσουμε τον χάρτη μαζί με τις λειτουργίες για να παίρνουμε τις συντεταγμένες (lat,lng) από το σημείο που ο χρήστης άφησε τελευταία φορά το marker. Αρχικά imports import React, { Component } from 'react'; import GoogleMapReact from 'google-map-react'; import Marker from 'google-map-react'; Η δήλωση του component μαζί με τον constructor (θα αλλάξουν στη συνέχεια). class MyMap extends React.Component { state = { center: [60.938043, 30.337157], zoom: 9, draggable: true, lat: 60.955413, lng: 30.337844 }; ... } Και η render() που θα εμφανίζει τον χάρτη. 45
  46. 46. render() { return ( <GoogleMapReact bootstrapURLKeys={{ key:'AIzaSyB4khCGTRcksa9YKd4SoVtRcxcho7WV6d8'}} draggable={this.state.draggable} onChange={this._onChange} center={this.state.center} zoom={this.state.zoom} onChildMouseDown={this.onCircleInteraction} onChildMouseUp={this.onCircleInteraction3} onChildMouseMove={this.onCircleInteraction} > <div className="marker" lat={this.state.lat} lng={this.state.lng}> </div> </GoogleMapReact> ); } Η render δεν περιέχει τίποτα άλλο παρά ένα tag της βιβλιοθήκης google-map-react. Τα attributes : BootstrapURLKeys για το API key της Google (παραπάνω δείξαμε πώς το λαμβάνουμε) draggable={this.state.draggable} ή draggable={true} onChange={this._onChange} Όταν αλλάξει κάτι ενεργοποιείται η _onChange ( το underscore μπροστά από τα ονόματα μεθόδων είναι σύμβαση των developers ) center={this.state.center} zoom={this.state.zoom} Κέντρο και ζουμ αλλάζουν ανάλογα με το state. Θα δούμε αργότερα που τα τροποποιούμε. onChildMouseDown={this.onCircleInteraction} onChildMouseUp={this.onCircleInteraction3} onChildMouseMove={this.onCircleInteraction} Θέλουμε να ακούμε κάθε κίνηση του ποντικιού είτε αυτή είναι το σύρσιμο του marker είτε η κίνηση είτε η τοποθέτησή του σε κάποια περιοχή. Τότε καλούνται ειδικές μέθοδοι που χειρίζονται την κατάστση (onCircleInteraction). Θα τις δούμε σε λίγο. 46
  47. 47. Το τελευταίο div αφορά το marker <div className="marker" lat={this.state.lat} lng={this.state.lng}> </div> Οι συντεταγμένες του προέρχονται από το state. Στην αρχή είναι ό,τι δίνει ο κατασκευαστής όμως αυτό στη συνέχεια αλλάζει καθώς ο χρήστης το μετακινεί σε διάφορες τοποθεσίες. Ας κοιτάξουμε και τις μεθόδους. Η _onChange είναι γνώριμη _onChange = ({center, zoom}) => { this.setState({ center: center, zoom: zoom, }); } Αυτό που δεν είναι γνώριμο είναι η σύνταξη της που διαφέρει. H σύνταξη είναι ES6 (ECMAscript) και δίνει ένα διαφορετικό τρόπο να δηλώσουμε και να συντάξουμε μια μέθοδο. Συνεχίζουμε στην ίδια λογική με τη onCircleInteraction onCircleInteraction=(childKey, childProps, mouse)=> { this.setState({ draggable: false, lat: mouse.lat, lng: mouse.lng }); var first = this.props.first var second = this.props.second this.props.changeFirst(this.state.lat) this.props.changeSecond(this.state.lng) } Ας ξεκινήσουμε με τα ορίσματα. Αυτό που μας ενδιαφέρει είναι το mouse. Είναι το όρισμα εκείνο που έρχεται να μας δώσει τις συντεταγμένες. Με τη setState θέτουμε τις καινούριες συντεταγμένες του marker (lat,lng) με τις καινούριες που περιέχονται στο mouse.lat και 47
  48. 48. mouse.lng. Έτσι ενημερώνεται και ο χάρτης για το πού ο χρήστης άφησε το marker του. Δε φτάνει μόνο αυτό. Πρέπει οι τιμές να φύγουν από αυτό το επίπεδο για να τις χειριστούμε αλλού. Αυτό το κάνουμε με τις μεταβλητές first και second που αντιπροσωπεύουν τα lat και lng αντίστοιχα. Αρχικοποιείται το prop first και second var first = this.props.first var second = this.props.second και μετά οι τιμές στέλνονται εκτός του component μέσω μεθόδων που λειτουργούν ως props του component. this.props.changeFirst(this.state.lat) this.props.changeSecond(this.state.lng) Οι τιμές στα ορίσματα είναι αυτές που μόλις ήρθαν από το mouse.lat και mouse.lng Η επόμενη λειτουργία θα χρειαστεί τη βάση δεδομένων οπότε ας φτιάξουμε έναν κατάλογο Config στο / src και μέσα ένα αρχείο config.js. Ήρθε η στιγμή να πάρουμε το κομμάτι κώδικα από το firebase console που είχαμε πει ότι θα χρειαζόταν. Με κάποιες αλλαγές όμως. import firebase from 'firebase'; const DB_CONFIG = { apiKey: "AIzaSyCDkf0lY3Jj9r-VLGcBpkrUWpFAq1CaqlQ", authDomain: "react-notes-98f11.firebaseapp.com", databaseURL: "https://react-notes-98f11.firebaseio.com", projectId: "react-notes-98f11", storageBucket: "react-notes-98f11.appspot.com", messagingSenderId: "372840341372" }; Δήλωση για μία σταθερά που θα περιέχει τις ρυθμίσεις σύνδεσης στη βάση. const facebookProvider= new firebase.auth.FacebookAuthProvider(); Δημιουργούμε μια σταθερά για να έχουμε τις υπηρεσίες ταυτοποίησης μέσω Facebook. const app = firebase.initializeApp(DB_CONFIG); Εδώ γίνεται η αρχικοποίηση της υπηρεσίας Firebase με βάση τα στοιχεία που έχει το DB_CONFIG. export {app,facebookProvider} Export τις δυο σταθερές ώστε να μπορούμε να τις συμπεριλαμβάνουμε στα υπόλοιπα αρχεία όπου χρειάζεται. 48

×