mirror of
https://github.com/google/pebble.git
synced 2025-03-26 05:09:05 +00:00
249 lines
8.5 KiB
Markdown
249 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
|