A Location Based TODO App
I want to use a TODO application that takes into account where I need to be to complete a task. I don’t “buy milk” while at home nor do I “cut the grass” when I’m at work. I want my TODO application to be smart and only show me tasks that make sense for where I am or where I’m going.
TodoMVC
If you are unfamiliar with TodoMVC, it was started by Addy Osmani and Sindre Sorhus sometime around 2012 with the help of many contributors over the years as “a project which offers the same Todo application implemented in most of the popular JavaScript MV* frameworks of today — think of it as speed dating for frameworks.” (1)
With the TodoMVC React implementation as a starting point we’re going to start adding location features.
- [ ] Add latitude and longitude with a TODO React component
- [ ] Display TODOs with a marker on a Map component
Additionally, there is a little bit of housekeeping to update the sample with the latest ES6 and React functionality.
// TODO: Run “Hello World”
There are at least two paths for getting a “Hello World” started quickly.
If you are more of a deconstructionist that wants to start with a working app and take it apart to understand how it works:
git clone https://github.com/tastejs/todomvc.git
cd todomvc
python -m SimpleHTTPServer
You should be able to view http://localhost:8000 and navigate to the React example found in examples/react. If you are more of a constructionist and want to start with a blank slate and build up by adding piece by piece:
npx create-react-app my-todo-app
cd my-todo-app
npm start
I prefer this approach, so when create-react-app finishes you should be able to view http://localhost:3000 with live reloading and a basic Hello World React App.
// TODO: Add A Component
A React component is a structure for creating independent, reusable pieces of a user interface. The component accepts properties as input and returns a React element that can be rendered.
TodoMVC originated with a goal of implementing an architecture by which the **M**odel, **V**iew, and **C**ontroller were independent. React aims to not artificially separate technologies such as JavaScript from HTML/CSS but rather separate concerns with loosely coupled components that contain both. The Reactjs.org [Main Concepts](https://reactjs.org/docs/components-and-props.html) goes into more detail on this design approach.
Review the following component source. It defines a simple text input box that defaults to the current location. There are inline comments to help describe some of the logic.
import React, { Component } from 'react';// This class definition is a React.Component so that we
// can use it in multiple places for the app.class Location extends Component {// The constructor takes properties defined as element attributes
// defined in JSX along with an initial default value for state.constructor(props) {
super(props);
this.state = {
value: '0,0', // Null Island
error: null,
}
}// When the component is rendered to the DOM for the first time
// such as at page load we call the Geolocation API to determine
// a latitude and longitude for the browsercomponentDidMount() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(position) => {
this.setState({
value: position.coords.latitude + ',' + position.coords.longitude,
error: null,
});
},
(error) => this.setState(
{error: error.message}
)
);
}
}// Respond to user input with event callback
changeLocation(evt) {
this.setState({
value: evt.target.value,
}
)
}// The JSX definition for how to render this component on the page.
// In this case, it's a simple input field for new todo items.render() {
return (
<input
className="new-todo"
value={ this.state.value }
onChange={ evt => this.changeLocation(evt) }
/>
);
}
}
// TODO: Add a Map
For the map, we’re going to use the Map Image API which provides a quick and easy way to fetch a static image. The parameters take some getting used to but let me unpack the basics here:
w=600
specifies the width of the image tile requested andh=300
the heightz=10
specifies the zoom levelt=5
specifies the rendering scheme so you can choose from various styles of map tiles including satellite, transit, etc.poitxs=1
,poitxc=black
, andpoitfc=yellow
specifies the point of interest size, background color, and foreground colorapp_idd=...
andapp_code=...
are found in the here developer projects section and needed for working with any HERE APIs.
Each of these parameters could be stored as either props or state on the component to provide rich customization options to users. The last parameter we want to send to the Map Image API is the poi=
for a comma separated list of latitude and longitude for any markers we want to place.
In the case of my TODO app, I’ve added two tasks with locations:
- “Return Library Book” at 37.86836, -122.26859
- “Pickup Badge for TechCrunch” at 37.784117,-122.401386
The poi query we’d want to make for these todo items would look like poi=37.86836,-122.26859,37.784117,-122.401386
.
Here is an example of the Map component that will re-render with each setState()
call when adding points of interest:
class Map extends Component {// For conciseness simply included all parameters in the querystring directlyconstructor(props) {
super(props);
this.state = {
url: 'https://image.maps.api.here.com/mia/1.6/mapview?w=600&h=300&z=10&t=5&poitxs=16&poitxc=black&poifc=yellow',
points: [],
}
}// Helper function to format list of pointsgetPOIList() {
if (this.state.points.length > 0) {
let param = '&poi=';
for (var poi in this.state.points) {
param += poi.latitude + ',' + poi.longitude;
}
return param;
}return '';
}// Render method builds the URL dynamically to fetch the image from the
// HERE Map Image APIrender() {
return (
<img
src={ this.state.url
+ '&app_id=' + this.props.app_id
+ '&app_code=' + this.props.app_code
+ this.getPOIList()
}
alt="Todo Map"/>
);
}
}
Once defined, you can reuse this component in any other component such as the root app component.
<Map app_id=”PUT_APP_ID_HERE” app_code=”PUT_APP_CODE_HERE” />
// TODO: Wrap It Up
This first post introduced TodoMVC with a couple of very basic React components that leverage the HERE Map Image API. In upcoming React posts I’ll dive deeper into this example to respond to state changes with event handling for geocoding lookups, map clustering for TODOs at the nearby locations with conditional rendering, Matrix Routing to get between TODO locations efficiently, Place Search to find related TODO items, and a few other project integrations to polish up the final todo app.