mirror of
https://github.com/google/pebble.git
synced 2025-03-26 05:09:05 +00:00
248 lines
8.5 KiB
Markdown
248 lines
8.5 KiB
Markdown
---
|
||
# Copyright 2025 Google LLC
|
||
#
|
||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||
# you may not use this file except in compliance with the License.
|
||
# You may obtain a copy of the License at
|
||
#
|
||
# http://www.apache.org/licenses/LICENSE-2.0
|
||
#
|
||
# Unless required by applicable law or agreed to in writing, software
|
||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
# See the License for the specific language governing permissions and
|
||
# limitations under the License.
|
||
|
||
title: JavaScript Libraries for C Developers (pt. 1)
|
||
author: cat
|
||
tags:
|
||
- Freshly Baked
|
||
- Beautiful Code
|
||
|
||
---
|
||
|
||
One of the exciting changes introduced in [SDK 3.9] [sdk 3.9] was support for
|
||
including [Multiple JavaScript Files] [js blog] to your projects. While this
|
||
feature doesn’t allow you to run any JavaScript you previously couldn’t, it
|
||
makes organizing your PebbleKit JS code a heck of a lot easier.
|
||
|
||
In this blog post, we'll look at how to refactor some exisiting JavaScript
|
||
'library code' into an actual module for PebbleKit JS, making it simpler to use
|
||
and share!
|
||
|
||
|
||
|
||
## OWM-Weather
|
||
|
||
A few months ago we published a very simple 'library' for working with the
|
||
[Open Weather Map] [owm] API. The JavaScript side of the code takes care of
|
||
grabbing the user’s location with the built in [geolocation.getCurrentPosition]
|
||
[getcurrentposition] API, then fetches weather information for the returned
|
||
position.
|
||
|
||
The initial version of the code we’re working with can be found [here] [owm lib 0].
|
||
|
||
## Creating JavaScript Classes
|
||
|
||
> **Note:** While JavaScript (with ECMAS5) does not allow for true "Classes",
|
||
> JavaScript does a support a mechanism that allows you write Class-like code.
|
||
> For convience, we'll be refering to these objects as 'classes' throughout
|
||
> this post.
|
||
|
||
The first thing we want to do is wrap our library code in an Object constructor
|
||
function, which is what allows us to treat `OWMWeather` as a class, and rewrite
|
||
our functions so they're properties belonging to `this` (an instantiated
|
||
version of the object). If you're interested in diving a bit deeper into how
|
||
this works, take a look at Pivotal's great blog post about [JavaScript
|
||
constructors, prototypes, and the 'new' keyword] [pivotal blog].
|
||
|
||
```js
|
||
var OWMWeather = function() {
|
||
this.owmWeatherAPIKey = '';
|
||
|
||
this.owmWeatherXHR = function(url, type, callback) {
|
||
//...
|
||
};
|
||
//...
|
||
};
|
||
```
|
||
|
||
We’ll also need to change our JS application code to construct a new
|
||
`OWMWeather` object, and change our reference of `appMessageHandler()` in the
|
||
in the `Pebble.addEventListener('appmessage', ...)` callback to
|
||
`owmWeather.appMessageHandler()`.
|
||
|
||
```js
|
||
var owmWeather = new OWMWeather();
|
||
|
||
Pebble.addEventListener('ready', function(e) {
|
||
console.log('PebbleKit JS ready!');
|
||
});
|
||
|
||
Pebble.addEventListener('appmessage', function(e) {
|
||
console.log('appmessage: ' + JSON.stringify(e.payload));
|
||
owmWeather.appMessageHandler(e);
|
||
});
|
||
```
|
||
|
||
## Using the .bind() function
|
||
|
||
If we try to run our code at this point in time, we're going to run into a slew
|
||
of errors because we've changed the scope of the functions, but we haven't
|
||
updated the places where we call them. We need to change
|
||
`owmWeatherFunctionName()` to `this.owmWeatherFunctionName()` (same goes for
|
||
the API key). Here's what the new `owmWeatherLocationSuccess` method looks like
|
||
(changes are indicated with in-line comments):
|
||
|
||
```js
|
||
this.owmWeatherLocationSuccess = function(pos) {
|
||
// Change owmWeatherAPIKey to this.owmWeatherAPIKey
|
||
var url = 'http://api.openweathermap.org/data/2.5/weather?' +
|
||
'lat=' + pos.coords.latitude + '&lon=' + pos.coords.longitude +
|
||
'&appid=' + this.owmWeatherAPIKey;
|
||
console.log('owm-weather: Location success. Contacting OpenWeatherMap.org..');
|
||
|
||
this.owmWeatherXHR(url, 'GET', function(responseText) {
|
||
console.log('owm-weather: Got API response!');
|
||
if(responseText.length > 100) {
|
||
// Change owmWeatherSendToPebble(..) to this.owmWeatherSendToPebble(..)
|
||
this.owmWeatherSendToPebble(JSON.parse(responseText));
|
||
} else {
|
||
console.log('owm-weather: API response was bad. Wrong API key?');
|
||
Pebble.sendAppMessage({ 'OWMWeatherAppMessageKeyBadKey': 1 });
|
||
}
|
||
// Add .bind(this) to the closing brace of the callback
|
||
}.bind(this));
|
||
};
|
||
```
|
||
|
||
An observant developer might notice that along with changing `owmWeatherXHR` to
|
||
`this.owmWeatherXHR`, we've also added `.bind(this)` to the end of the callback
|
||
function.
|
||
|
||
We're not going to dive too deeply into how `this` and `bind` works (that's a
|
||
whole blog post on its own), but what I will say is that the `bind` method can
|
||
be thought of as modifying a function so that it will, when invoked, have its
|
||
`this` keyword set to the provided parameter.
|
||
|
||
> If you want to learn more about JavaScript Objects, scope, and `bind`, I will
|
||
> encourage you to read [You Don't Know JS: *this* & Object Prototypes] [js book].
|
||
|
||
We'll want use `bind(this)` (where `this` will be the instance of the
|
||
OWMWeather class) whenever we're using an OWMWeather method as a callback from
|
||
within the OWMWeather code.
|
||
|
||
```js
|
||
navigator.geolocation.getCurrentPosition(
|
||
this.owmWeatherLocationSuccess.bind(this),
|
||
this.owmWeatherLocationError.bind(this), {
|
||
timeout: 15000,
|
||
maximumAge: 60000
|
||
});
|
||
```
|
||
|
||
At this point, our code should look like [this] [owm lib 1].
|
||
|
||
## Using module.exports
|
||
|
||
The last thing we want to do to make this into a handy module is extract the
|
||
code into its own file (`/src/js/lib/owm_weather.js`), and use the
|
||
[module.exports] [module exports] API to export our OWMWeather class.
|
||
|
||
```js
|
||
var OWMWeather = new function() {
|
||
//...
|
||
};
|
||
|
||
module.exports = OWMWeather;
|
||
```
|
||
|
||
In order to use this in our PebbleKit JS application, we need to do a couple things..
|
||
|
||
If you're using CloudPebble:
|
||
|
||
- Change **JS Handling** to _CommonJS-style_ in the project settings
|
||
|
||
|
||
If you're using the SDK:
|
||
|
||
- Update our `appinfo.json` to include `'enableMultiJS': true` if it isn't
|
||
already there
|
||
|
||
- Rename `src/js/pebble-js-app.js` to `src/js/app.js`
|
||
|
||
Once we've made these changes to our files, we're ready to include OWMWeather
|
||
in our `app.js` file with the [require API] [require api].
|
||
|
||
```js
|
||
var OWMWeather = require('./lib/owm_weather.js');
|
||
var owmWeather = new OWMWeather();
|
||
|
||
Pebble.addEventListener('ready', function(e) {
|
||
console.log('PebbleKit JS ready!');
|
||
});
|
||
|
||
Pebble.addEventListener('appmessage', function(e) {
|
||
console.log('appmessage: ' + JSON.stringify(e.payload));
|
||
owmWeather.appMessageHandler(e);
|
||
});
|
||
```
|
||
|
||
At this point, our code should something look like [this][owm lib 2].
|
||
|
||
## Refactoring Variable and Function Names
|
||
|
||
Since we’ve moved all of the OWMWeather code into a class, we can safely remove
|
||
the `owmWeather` prefixes on all of our methods and properties. While we’re at
|
||
it, we're also going to rename functions and properties that are intended to be
|
||
private to begin with an `_`, which is fairly common practice:
|
||
|
||
```js
|
||
var OWMWeather = function() {
|
||
this._apiKey = '';
|
||
|
||
this._xhrWrapper = function(url, type, callback) {
|
||
//...
|
||
};
|
||
//...
|
||
};
|
||
```
|
||
|
||
## What's next..
|
||
|
||
And that's it - we've successfuly refactored our code into a module that should
|
||
be easier for developers to use and share (which is exactly what we want). You
|
||
can view the full source code for this blog post [here][owm lib final].
|
||
|
||
In the next blog post, we'll take this library a step further and look at how
|
||
you can abstract away the need for `appKeys` in your `appinfo.json` file to
|
||
make working with the library *even easier*...
|
||
|
||
Until then, happy hacking!
|
||
|
||
|
||
[sdk 3.9]: /sdk/changelogs/3.9/
|
||
[js blog]: /blog/2016/01/29/Multiple-JavaScript-Files/
|
||
|
||
|
||
[owm]: http://openweathermap.org/
|
||
|
||
|
||
[getcurrentposition]: https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition
|
||
|
||
[pivotal blog]: https://blog.pivotal.io/labs/labs/javascript-constructors-prototypes-and-the-new-keyword
|
||
|
||
[js book]: https://github.com/getify/You-Dont-Know-JS/blob/master/this%20&%20object%20prototypes/README.md#you-dont-know-js-this--object-prototypes
|
||
|
||
[module exports]: http://www.sitepoint.com/understanding-module-exports-exports-node-js/
|
||
|
||
[require api]: http://www.sitepoint.com/understanding-module-exports-exports-node-js/#importing-a-module
|
||
|
||
|
||
[owm lib 0]: https://github.com/pebble-hacks/owm-weather/tree/8c4f770e591fe5eff65209ebb6fe6ef23152d81a
|
||
|
||
[owm lib 1]: https://github.com/pebble-hacks/owm-weather/blob/8c9ab77a66d5c38719acbdfce939fbfac6d12235/owm_weather/owm_weather.js
|
||
|
||
[owm lib 2]: https://github.com/pebble-hacks/owm-weather/tree/597c717627c281b56ff303ca94f35789002a969e
|
||
|
||
[owm lib final]: https://github.com/pebble-hacks/owm-weather/tree/657da669c3d9309a956f655c65263b8dc06cec1f
|