- Overview
- Some useful Terminology
- The stack
- Folder and File set-up
- Naming conventions
- Visual Studio Code
- Documenting
- HTML5 standards
- JS and ES6 standards
- Node
- Client Overview
- Vue.js
- Sources of truth
- Babel
- Bundler - Webpack
- Debugging
- Testing
- OpenLayers
- Server Overview
- MongoDB
- Environment Variables
- Into Production
- Other Notes
Table of contents generated with markdown-toc
This is an eclectic, pragmatic working document to help me remember how to do things, and evolving as I get more experience (or change technologies!). It has lots of links for useful reference. Be aware that although the intention is there, it is likely that at any time the code will not meet the standards by a long way. There is also lots of stuff here that I do not understand yet!
Whilst the codebase represented here is usually a working system, there is currently quite a bit of the code that is non-functional or not yet working, because it is:
- redundant
- in process of being adopted from v1 of M4OPS
- experimental, or
- under development
These files are excluded from linting by virtue of
- being in a folder called "unused" (per src/.eslintignore)
- being in a folder with the word OLD in the name (per src/.eslintignore, or
- starting with the line /* eslint-disable */
Although fairly complex, this is a simplified approach to aid understanding, development and maintenance, and has been adapted in the light of experience. See other possibilities in M4OPS2 Other Technical Possibilities, although as we go on any that are actually used will be moved from there into this document.
- To see diagrams you need mermaid installed
- Do not use , ( ) / in headings else links from TOC will not function without manual change
-
Semantic versioning (semver) - Change major number when it may break existing usages
-
a software stack is a set of software subsystems or components needed to create a complete platform such that no additional software is needed to support applications
-
polyfill - a snippet of code that patches a piece of functionality that's missing in some browsers.
- ponyfill provides that functionality as a standalone module you can use – see this discussion
-
Function currying is the process of successive partial applications, until the last argument is given at which point the result of the function is returned
-
functional programming is a style of building computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data. In contrast, imperative programming changes state with commands in the source code, the simplest example being assignment. (Ignore as too complex?)
-
A closure is the combination of a function and the scope object in which it was created. Closures let you save state — as such, they can often be used in place of objects. (Not used here)
-
CRUD - create, read, update, and delete
-
REST stands for Representational State Transfer -The fundamental concept within REST is that of a resource - the object we are operating on. For each resource we define what methods can operate on that resource. Methods such as GET, POST, UPDATE, DELETE.
-
HOC - higher-order component - a function that takes a component and returns a new component (used in React), compare in Vue.js ..
-
In a mixin you can put any component’s methods and they will be merged with the ones of the component that uses it.
-
a variadic function is one of indefinite arity, i.e., one which accepts a variable number of arguments
-
Parentheses, Braces, or Brackets?
- Round: Parentheses () for function calls, conditional statements, or enforcing Order of Operations.
- Curly: Braces {} for the declaration of Object Literals, or to enclose blocks of code
- Square: Brackets []for accessing the properties of an Object (or the elements of an Array)
-
ORM - Object-relational mapping for converting data between object-oriented and relational forms
-
Duck Typing is better than using instanceof
-
memoization (memoisation) is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.
-
a Promise is an object that is used as a placeholder for the eventual results of a deferred (and possibly asynchronous) computation.
-
PWA (Progressive Web Apps)
- a hybrid of regular web pages (or websites) and a mobile application
- see 4 important points to know about Progressive Web Apps (PWA)
- the Service Worker is a script that the browser runs in the background separate from web page is the backbone of every PWA
- also Accelerated Mobile Pages (AMP) provides reliably fast web components for first load
-
BEM - "Block, Element, Modifier” naming convention - see Introducing BEM: The popular CSS naming convention
-
VNode - a “virtual node” containing information (returned by createElement) describing to Vue what kind of node it should render on the page, including descriptions of any child nodes. The entire tree of VNodes is called the “Virtual DOM”.
The main stack is
-
the client: OpenLayers, Vue.js (and Vuex), Webpack
-
<-via REST API->
-
the server: Express, Node
-
database: MongoDB
**client** Folder Structure
├── dist - result of the build process
├── public
├── src
└── tests (TODO)
**public** Folder Structure
├── index.html (main entry point, includes \<div id="app"></div> into which the whole app is injected by main.js)
├── ...
├── OPS (one folder for each OPS)
│ ├── HcN - all the images, html files etc used for HcN in M4OPS
│ ├── HNB
│ └── ...
└── img (used by the system)
└── ...
**src** Folder Structure: based on [How to Structure a Vue.js Project](https://itnext.io/how-to-structure-a-vue-js-project-29e4ddc1aeeb)
├── main.js - the first script loaded
├── App.vue - the first Vue component - contains all the others router.js
├── router.js - 'routes' URLs to views
├── assets - any assets that are imported into components
│ └── ...
├── components - All the components that are not routed (ie not views)
│ └── ...
├── global
│ ├── components - a few that are generic, and potentially used anywhere
│ │ └── ...
│ ├── constants ... system-wide
│ ├── utils ... functions used in various places
│ ├── plugins - TODO
│ │ └── ...
│ └── styles - any not included in SFCs
│ └── ...
├── initialising (used on first load)
│ └── ...
├── modules - for routines by application area (will change) - each potentially has index.js, components, submodules, utilities
│ ├── demo - to do
│ ├── framework - header, sidebar
│ ├── geography - ??
│ ├── mapping
│ └── params - parameters to the URL
├── store
│ ├── index.js - where we assemble modules and export the store
│ ├── mutation-types.js - Vuex constants
│ └── modules - each potentially has index.js, components, submodules, utilities
│ ├── ...
│ └── vuexApi - getters etc but no index.js
├── translations - Locales files (for eg using Vue-i18n) TODO
│ └── index.js
├── views - the components that are routed in router.js eg xxx/dashboard
│ └── ...
└── other folders ... such as filters,or constants, API.
**server** src Folder Structure
├── controllers - the various 'find' functions to get data from the database
├── middleware TODO
├── models - defining the Mongoose/MongoDB schemas
└── routes - the routes from the URL to the controllers
Notes: Each folder
- can have a README.md file describing it
- can have .gitignore for files to be excluded from git
- can have .eslintignore for files to be excluded from eslint
- can have .eslintrc.json to direct eslint and give it parameters
Our OPS images etc go in the public folder - see this
- storing them in the MongoDB database would use up space too much???
- consider Fractal Project Structure
See under Standards and styles
- Our main development environment is VSCode - intelligent code editing
- Documentation and Chrome debugging in VSCode
- a workspace (defined by an editable workspace file) is defined to be one or more folders and VSCode settings - we will not use them, unless it becomes necessary
- Note that almost all VSCode settings (including cSpell words) are done at the folder level (eg in C:\projects\m4ops.vscode\settings.json), rather than at the User (C:\Users\Peter2\AppData\Roaming\Code\User) or workspace level
- Similarly for ESLint
- as the VSCode shell defaults to PowerShell on Windows 10 we set the terminal.integrated.shell.windows in user settings to "C:\Windows\System32\cmd.exe"
- Use Ctrl+Shift+P for the list of commands (Ctrl+S to save - but we have set it to save automatically), and use Ctrl+space for context-sensitive snippets (or start typing)
- Ctrl+/ toggles lines as comments
- For documentation and markdown see separate Documenting section below
- Useful about Node.js and Express in VSCode
- VS Code can do that?!
- (See them in C:\Users\USER.vscode\extensions)
- Using Code Spell Checker
- can Enable / Disable checking sections of code with // cSpell:disable
- Includes Emmet - snippets etc (not used much)
- Read Only indicator
- Vetur Vue tooling for VS Code
- markdownlint
- ESlint - see below
- GitLens for examining the git history of files
- Bracket Pair Colorizer
- [Markdown Preview Github Styling]
- [Markdown Preview Mermaid Support]
- [vscode-babel-coloring]
- [dotenv]
- [debugger-for-chrome]
- [JSON to JS converter]
- [vue-snippets]
- [vue-peek]
-
GitHub P6 XXXX is the git server where my projects are
-
Projects on GitHub; GitHub Marketplace; Help; free to use for public and open source projects
-
With gists, you can share single files, parts of files, and full applications - powered by CodeMirror.
-
On forking see How to fork a dependency and use it locally in a project, npm link and Package linking
-
we have the very helpful Pro Git PDF book, written by Scott Chacon and Ben Straub - then follow up with Git support in VSCode
-
Excellent Tutorial/Reference on git and GutHub - commands, cheat sheet
-
GIT locally on GIT for Windows
- as GitHub Desktop is not for use from outside
- Chose option to checkout Windows-style
-
Issues are suggested improvements -each contains its own discussion forum, Glossary
-
Pull requests (PRs) let you tell others about changes you've pushed to a branch (ie you are Requesting that they Pull from your repository)
-
To clone a repository from GitHub to local (don't create folder first)
-
Useful guide to git protocol and Learn Version Control with Git
-
A Git blob (binary large object) is the object type used to store the contents of each file in a git repository.
-
Notes
- Initialise repository for this project, or "clone" (=download / copy) one to your local computer
- A helpful compilation of ignore rules
- Files are "untracked" (not under version control, yet), or "tracked" - Git watches for changes
- Commit Only Related Changes: Working copy > Staged (ready for commit) > Local Repository
- Write Good Commit Messages
- Use Branches Extensively - for new features, bug fixes, experiments, ideas: it is quick and easy
- Never Commit Half-Done Work
- Think of Stashes as a clipboard on steroids: it takes all the changes in your working copy and saves them for you, leaving a clean working copy. Later, you can restore the changes from that clipboard in your working copy current HEAD branch - and continue working where you left off.
- You can merge (one branch into another - HEAD) as often as you like
- One Long-Running Branch Only - your production code- all new topic branches are based off (and merged into) this "master" branch
- A "Submodule" is just a standard repository (pointing to a specific commit) nested inside a parent repository. To include a code library, you can simply add it as a Submodule in your main project.
-
Our simple workflow is (using Ctrl-Shift-P each time)
- Checkout to...
- +Create new branch - give it a name (XXX)
- Do work until system is working OK
- Checkout to Master branch
- Merge branch XXX (into Master branch)
- Delete branch XXX
-
Every now and then publish changes (to origin)
-
to remove them from tracking: put filename in .gitignore AND remove history using git rm filename --cached
-
More on fork and pull
-
Set up guide includes style –uses eslint-config-airbnb-base (-base for non-react)
- npm i -D eslint eslint-plugin-import (not eslint-config-airbnb-base ?)
-
Tried using Prettier rather than a style guide, but complications
-
Note that we don't use setting up global eslint in VSCode (because create-react requires it local)
-
Note that any plugins or shareable configs that you use must also be installed locally to work with a locally-installed ESLint
-
Vue and EsLint
-
See eslint-prettier-vue-workflow - seems good but not actually tried
-
Also installed Vue 2 Snippets, Vue Peek
-
To run on all files in eg client/src folder use "../node_modules/.bin/eslint" . (but see .eslintignore below)
- Note that "npm run lint" runs "vue-cli-service lint" - which probably has different eslint options set
- See guide to configuring
- List of rules
- We do use some Configuration (in-file) comments)
- /* eslint-disable */ or // eslint-disable-next-line or // eslint-disable-line
- for rules eg no-console, max-len
- can just right click in VSCode to implement this
- /* eslint-disable */ or // eslint-disable-next-line or // eslint-disable-line
- We do not use the eslintConfig field, but do use .eslintrc.json files
- /m4ops/.eslintrc.json has the basic configuration that applies to both client and server code
- do not include any rules (even common ones) as the whole rules object is overwritten by that in either the client or server file
- /m4ops/client/.eslintrc.json has any specific configuration needed for the client (Vue) system, including rules
- in particular it has the kebab-case names of imported components, for which the PascalCase rule is ignored
- /m4ops/server/.eslintrc.json is similar for the server (Express) system, including rules
- /m4ops/.eslintrc.json has the basic configuration that applies to both client and server code
- We also use an /m4ops/.eslintignore file in any folder we run eslint in (note that only one such file is used)
- for patterns see gitignore
-
(eg this) use GitHub flavoured markdown (gfm) - Mastering GitHub flavoured markdown
-
Markdown quick reference (not GitHub), see also Writing on GitHub
-
see also markdown cheat sheet
-
lint using markdownlint - see also configuration
-
could use markdown-style-guide but not easily linted in VSCode so stick with markdownlint RULES
-
use mermaid in markdown for charts - Flowchart, Sequence diagram, Gantt diagram - but GitHub does not use mermaid
-
use pandoc to convert from Word to markdown– see User Guide, but as we have permissions problems so as cmd Admin user:
- cd C:\Users\Peter Admin\AppData\Roaming\pandoc, and do everything there
- pandoc -f docx -t gfm “C:\Users\Peter_2\Documents\Mapping\Software\M4OPS2\Documentation\filename.docx” -o filename.md
-
for relative links use [a relative link](other_file.md)
-
Percolate - A command-line tool to turn web pages into beautifully formatted PDFs
- See html5doctor's tag/element-index and their flow chart for use of <section> etc
- Learn to Code HTML & CSS - the fundamentals
- Learn to Code Advanced HTML & CSS
- HTML Living Standard
- HTML etc Cleanup
- Excellent documentation/tutorial
- Roadmap for learning the JavaScript language ES3++ - see MDN links for basic syntax, functions, object literals, arrays, regular expressions, prototypal inheritance
- Airbnb JavaScript Style Guide
- useful book - Exploring ES6 covers every level
- useful list of es6 features
- newline between dependency imports and local imports
- in Node.js continue to use module.exports (or just exports.) and require rather than export and import
- (NOT YET) could use object literals instead of switch statements as per this suggestion
- Add comments only to explain complex thoughts
- Boolean variables, or functions that return a boolean value, should start with 'is', 'has' or 'should'
- use has() instead of hasOwnProperty as it is better
- (someone says we don't need to call
super(props)
, unless you need to usethis.props
inside the constructor) - Could use Flow for typing
(Documentation) for static typing see eg here
- add // @flow to any files you want to type check (for example, to src/App.js (TO DO)
- npm run flow to check the files for type errors
- for (ES6) maps and collections
- new ES6 standard maps, and
- what-you-should-know-about-es6-maps
- excellent article about maps
- array-vs-set-vs-map-vs-object
- Note that we often use [k, v] meaning key, value
- The collections.js package provides JavaScript implementations of common collections, with many useful features but they are less relevant now we have maps
- for arrow functions
- Glossary of Modern JS Concepts
- Object.freeze() shallow freezes an object (so nothing about it can be changed) whereas Object.seal() seals an object (just preventing new properties from being added to it)
- Enums in Javascript
-
For URLs etc use url-parse, which includes querystringify - which has just parse and stringify methods
- Note that anything that you put after a hash (#) the browser ignores so that's a special trick that Vue Router uses to just get the frontend to take care of the URLs and not relay this information back to a server, and not reload the page
-
use uuid/v1 for IDs (or cuid?)
-
lodash has many useful functions
- just import _ from 'lodash' (see benchmark)- eventually use babel-plugin-lodash
- for iteratees see this explanation
-
consider vanilla ES6 js or ramda
-
for async/await see Javascript.Info: The Modern Javascript Tutorial
-
for promises see Javascript.Info
-
to pass 'this' context to a function use js bind
-
IIFE (Immediately Invoked Function Expression) is a JavaScript function that runs as soon as it is defined - an anonymous function with lexical scope enclosed within the Grouping Operator ()
- regexr is an online tool to learn, build, & test Regular Expressions
- regular expressions.info is useful
- eloquent
- w3schools & objects
- regexp methods Excellent on replace
- exec vs match
- Quick Reference
with a global regular expression, .exec is meant to be used in a loop, as it will retrieve all matched subexpressions. String.match does this for you and discards the captured groups. Without the /g both exec and match return the first match ([0]) and any captured groups ([1] etc)
- Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.
- npm - the package manager for javascript (documentation)
- npm globals are in C:\Users\Peter2\AppData\Roaming\npm
- claudia, jest, nodemon, sass, vue
- How to create and publish your npm package Node module in just 10 minutes - simple
- Installing and Building an NPM Package from Github - some insights
- How to make a beautiful, tiny npm package and publish it - jokey
- Official documentation - Contributing packages to the registry, and About packages and modules
- building your first node module
- (using "building your first node module" from above)
- choose the name (vfg-display-fields)
- npm view vfg-display-fields => confirms it does not exist yet
- create repository in github, and clone to c:\projects
- npm init => initialises package
- edit, commit, merge into master, synchronise with repo
- see guide to test package
- npm login (p6 lower case, )
- npm publish
- github repo is the 'source of truth', so commit and publish
- Updating your published package version number - only when significant changes have been made
client/public/index.html is the file a browser will open - this provides a title and a root div (app), and runs client/src/main.js which takes the output of App.vue and puts it into the root div. (registerServiceWorker.js is used just in production)
The client is the system that the user interacts with. The server is the system that the client system can use, eg if it wants to store or retrieve data). We keep these systems separate, and develop them separately.
When the client needs to communicate with the server it sends an asynchronous request (with a promise attached) and when the server is done it notifies the client the promise has been completed. The client can then do any more processing dependent on the promise being completed. [More on this later]
(Almost) all of the state of the client is held in the Vuex store, and access to it is very structured [see later].
graph LR;
M[Vuex store] -->|getters| C[Client]
C --> A[Actions]
A --> |mutations| M
C -->|POST or GET| S[Server]
S -->|Promise| C
- Main guide to Vue.js
- Vue.js API
- Vue.js cookbook
- Vue Development In 2019: What You Need To Know
- to open the visual interface for managing project > vue ui
- Vue.js User Forum
- GitHub for Vue.js
- Awesome Vue.js is a HUGE curated list of things related to Vue.js, including components
- eg styles
- Projects etc Made with Vue.js
- SSR Server-Side Rendering & Nuxt are not for us)
- See boilerplate-template-scaffold for useful scaffolds
- consider later this template which includes user sign-in
- useful examples and libraries for Vue.js
- Master Vue.js by mastering common problems
- Consider Local Storage for persistence and speed (better use plugin?)
- Six random issues and their solutions in VueJS
- Understanding Vue.js Lifecycle Hooks
- Useful on Vue's communication channels
- vue component to use common functions
- Global plugins
- clear description
- Writing a very simple plugin in Vue.js
- (Good but too complex) Production-Ready Example from SnipCart
- Working with Environment Variables in Vue.js
- Vue.js Instance Properties etc - available to all components as this.$...
- Managing cookies
- Authentication-system-with-node-js-backend
- any components starting with _base in /global/components/ are globally registered
- The correct way to force Vue to re-render a component - update the key
- A template element is used to declare fragments of HTML that can be cloned and inserted in the document by script.
- The Vue.js Cheat Sheet
- Methods, Computed, and Watchers in Vue.js
- Vue Filters can be used to apply common text formatting in two places (mustache interpolations and v-bind expressions)
- Vue Enter/Leave & List Transitions - automatically apply classes for CSS transitions and animations etc - <transition> goes around an element, <transition-group> round a v-for
- v-cloak - hide until Vue compiled
- special abstract component keep-alive tells Vue not to destroy and recreate its child, instead keep it in memory
- Access And Change Parent Variables From A Child Component With Vue.js
- Simplify Your Vue Components with Computed Setters - get/set for a computed property
- 26 Time Saving Tips for Vue - useful to review
- permission-control as an example of an abstract component
- Constructor Pattern
- Scoped Slots can be in any element
- see the excellent Understanding scoped slots in Vue.js
- and (video) The Trick to Understanding Scoped Slots in Vue.js, explaining that if slots are like props, then scoped slots are like function props
- slot-scope is the name of a temporary variable that holds the props object passed from the child - even simpler if it is destructured
- How I finally got my head around Scoped Slots in Vue
- Vuescript.com aims to offer latest free Vue.js components for web application developers. - eg Gallery components
- For gallery we use v-viewer
- Render Functions - use instead of templates to generate components
- Refers a lot to Vue Instance Properties API
- Can even use JSX
- Another similar explanation of render functions
- Use Any Javascript Library With Vue.js - Object.defineProperty(Vue.prototype, '$moment', { value: moment }); or ... { get() { return this.$root.moment}}
- Vue.JS Components for building Search UIs - Reactive Search for Vue, using Elasticsearch
- Vue Router, and its API ref
- we have access to the router (as this.$router) and the current route (as this.$route) inside any component
- although better to decouple components from the router by using props:
- a dynamic segment is denoted by a colon : - the value of the dynamic segments is in this.$route.params
- eg for /user/:username/post/:post_id, /user/evan/post/123 yields params = { username: 'evan', post_id: '123' }
- uses path-to-regexp for path matching - example
- supports eg optional dynamic segments, zero or more / one or more requirements
- includes compile ("Reverse" Path-To-RegExp)
- can have nested router-views (use the children option in routes), and named router-views
- <router-link :to="..."> is the equivalent of router.push(...)
- there are also router.replace, router.go
- advanced uses
- Use path parameters, with their specific order, where there is an implied hierarchy, otherwise use query parameters
- See Synchronize with Vue Router for passing route parameters as props, and keep the URL in sync with changes
- We use https://github.com/vuejs/vuex-router-sync to sync $route into vuex store's state
- Missing manual
- compares state-router vs url-router
- can pass complex data from one-route to another during transition without making the data part of URL
- Prefer tree configuration over a flat configuration
- create a store injector function
- for a route configuration with an asynchronous component may need to watch/unwatch $route in the parent
- Yes, this is how vue-router guards work & when to use them
- Global guards (on the instance) - each time the URL changes
- Route guards (on route definitions) - when the associated ROUTE is matched
- Route Component guards - when a ROUTE COMPONENT is used/unused
- the to and from in the guards are route objects, which include params etc
- Data Fetching and Validating:
- Fetching After Navigation (in created and watch $route) - but problem with validation?
- Fetching Before Navigation (beforeRouteEnter and beforeRouteUpdate guards) - only call next when the fetch is complete
- see also Fetching/ using store in router
- using Vue Router Navigation Guards
(specific order separated by /) - see client\src\router.js "path: '/maps/"
- :ops: code for the OPS (optional - default HcN)
- :layers: zero or more layer titles (starting at layer 0) separated by / (if none then default is Bing%20Aerial/OSM)
- layer titles can be alphanumeric, including -_ and spaces
- :opacities: zero or more opacity numbers (% starting at layer 1, as the base is always 100% opaque) separated by / (if none then default is 50 for rasters, 100 for feature layers)
- :ZoomOrFitTo: zoom level preceded by Z eg 18, or FitTo preceded by F eg 1 (default is the HomeView)
- (FitTo was Extent and is the number of the layer to initially fit to)
- :Lon/Lat: Longitude/Latitude decimal degrees, eg -0.0318640/52.3304020 for central Needingworth
- (This is in 'EPSG: 4326' or Spherical Mercator, and is irrelevant if a FitTo is specified)
(for now the final / is important) Basic Pubs Protected Layer 2 Spinney Way HNB is Protected
-
(key/value pairs, or just the key - meaning true)
-
NoCHNG means you cannot change the OPS
-
Showlevel= (default 9999) for demonstrations starts at 0 then goes up at cutoffpoints
-
Splash= html text for splash screen when M4OPS first opens, can include abbreviations (#..#)
- one word abbreviations will, if necessary, have the word Splash appended, and be surrounded by #..#
- (thus Splash=Spyglass becomes #SpyglassSplash#)
- (and Splash=25inch becomes #25inchSplash#)
-
Tab= the initial advanced option tab to show:
- Actions - PNG, Demo, icons (the default)
- MFL - Modifiable Feature Layers
- Upload - Upload, compile
- Time - Time sliders
-
Displaystyle= (initial view)
- onemapOpacity - One map with Opacity slider, or
- sidebyside - Side by Side maps, or
- onemapSpy - One Map with Spyglass
-
Colours= initial colour scheme for features
-
Click= the initial drop-down value - one of:
- no - No lat/lon click
- M4OPScsv - M4OPS lon;lat csv
- M4OPSparam - M4OPS parameters
- csv - lat,lon csv
- geojson - {lon,lat} GeoJSON
- EPSG3857 - EPSG:3857 (x/y)
- HDMS - DegMinSec N/E
- GeoHack - GeoHack links
- Featureid - Feature id
-
Green if you want the background in development to be the normal Green
-
NoShift if you want the layers NOT shifted east or north
-
LoadwA if you want tiles loaded during animations (may improve the user experience, but can also make things stutter on devices with slow memory)
-
LoadwI if you want tiles loaded while interacting with the map (ditto)
-
(Mouse if you want the next feature to be Georeferenced to appear by the mouse pointer)
- from forum
- File= filename of json to use, default M4OPS (.json) NA
- In Vue.js events can only go from child to parent, but a more general way is to use an Event Bus (and page 106 of the book) - add to Vue.prototype (lecture 98)
- we usually use Vuex (state) rather than events
- Debouncing and Throttling Explained Through Examples - see also this Vue guide for example and lodash function
- Vuex - State management
- the store is injected into all child components of the root and will be available on them as this.$store
- other routines can access the store and its getters etc by simply importing it
- "getters" in the store are computed properties that can be referenced in components via store.getters.xxx (can be functions)
- a mutation is a function eg increment-mutation (state) { state.count++ } - always synchronous
- mutation types are UPPERCASE and defined in mutation-types.js
- an action commits a mutation eg increment-action ({ commit }) { commit('increment-mutation') }
- can be asynchronous, usually has a payload object, can handle Promises
- we dispatch actions in components with this.$store.dispatch('xxx')
- in components use the helper functions
- mapState to generate computed getter functions
- mapGetters to map store getters to local computed properties
- mapActions maps component methods to store.dispatch
- inside module actions and getters, the root state will be exposed as as well as the module's state
- We do not Namespace vuex Modules (except alert)
- Various Vuex Utilities
- Not using vuex-pathify although it simplifies the Vuex wiring
- Vuex getters are great but use mapState for simple ones
- Vuex Instance Properties etc - available to all components as this.$store.xxx
- Documentation
- We use the Logger Plugin - createLogger
- or use vue-devtools time-travel (p143 of the Fullstack Vue book) - but see this issue with crashing
- For ORM Vuex ORM - a plugin for Vuex to enable Object-Relational Mapping access
- 5 Vuex Plugins For Your Next VueJS Project
- Persisting state
- Syncing tabs/windows
- Language localization
- Managing multiple loading states
- Caching actions
- A Vuex Undo/Redo Plugin
- Using Typescript with vue - we don't
- Should You Learn TypeScript? (Benefits & Resources) (answer - not yet!)
- Type Vue without TypeScript
- Use vuex-api
- for GETS use json-api
- Considered later using plain Axios, but too hard to change
- perform HTTP requests using Axios - simple examples
- Rather than plain fetch, see fetch-vs-axios
- see Using Axios to Consume APIs for a simple approach
- See vue.config.js is an optional config file, and needed for eg devserver-proxy
- Not using vuex-rest-api (makes part of the store), nor vue-api-request (which has lots of options)
- or p 178ff in Fullstack Vue book (action = axios call then commit mutation)
- (need to commit a different mutation when axios call fails)
- We use Jason Watmore's approach, and have absorbed his routines into ours (users modules), adapted to our standards
- The source of truth is in the server's (MongoDB) database
-
The key constraints are on:
- being able to see an OPS at all
- being able to see a layer
- being able to upload a file
- being able to compile
- being able to set up, alter, delete users
-
OPS are one of:
- Unprotected - anyone can see it (default)
- Protected - users need a specific right for that OPS to see it (and then can see all layers)
-
Each Layer (settled and modifiable/MFL) is one of [see protectionStatusEnum]:
- Unprotected - anyone can see it (default), if they can see the OPS
- Protected - logged-in users can see it (eg with Census images), if they can see the OPS
- Personal - only the one user (and admin) can see it
- Test - only an admin (globalAdmin, or opsAdmin for that OPS) can see it, and then only if the Test switch is On
-
Everyone (including [not logged-in] Guests) can see all Unprotected layers in all Unprotected OPS
- and login as a User (thereby acquiring any specific rights they have)
- and register as a User (but without any specific rights)
- but not Upload, compile, create or change an MFL
-
Logged-in Users (without needing any specific rights) can see all Protected (and Unprotected) layers in all Unprotected OPS
- and their own Personal layers
- and create an MFL, or change an MFL they can see
-
In addition, Users can have none, one or more of the following specific rights [see userRightsEnum]:
- opsViewer - as well as everything a Logged-in User can do
- can see all Protected layers in the specific Protected OPS (not necessary if the OPS is Unprotected)
- but not Upload, compile, create or change an MFL
- opsTeamMember - can do everything the opsViewer can do
- and Upload, compile, create or change an MFL in the specific OPS
- opsAdmin - can do anything in the specific OPS
- including everything an opsTeamMember can do
- and see all Personal layers in that OPS (from everyone)
- and Register/edit Users as opsTeamMembers and opsViewers
- globalAdmin - can do anything
- opsViewer - as well as everything a Logged-in User can do
See client\src\global\constants.js for protectionStatusEnum {UN,PD,PL,TT}, userRightsEnum = {9_NO,6_OV,4_OT,2_OA,0_GA}
- See Jason Watmore's approach - client
- clone of Jason Watmore's system is in C:projects/vue-vuex-registration-login-example
- routines in \client\src
- \store\modules\users - defines the user aspects of the Vuex store (state, actions, mutations)
- account.module.js - the current user and their loggedin status
- alert.module.js - alert state: type and message
- users.module.js - all users
- \views
- LoginPage.vue - includes logout
- RegisterPage.vue
- ManagePage - (originally HomePage) sb only accessible by administrator
- \modules\users
- _helpers
- auth-header.js - generates a header
- fake-backend.ts - intercepts certain api requests and mimics the behaviour of a real api
- user.service.js - all backend api calls, and logging in/out (handleResponse handles if the JWT token is no longer valid for any reason)
- \store\modules\users - defines the user aspects of the Vuex store (state, actions, mutations)
- the URLs that are public are in router.js (beforeEach)
- Could use Route Meta Fields to determine navigation limits
- JSON Web Tokens are an open, industry standard method for representing claims securely between two parties - and the library jwt.io allows you to decode, verify and generate JWT
- We use jwt-express to facilitate this on the server
- it uses express-unless to conditionally prevent itself from running
- As well as in REST APIs, JWTs can also be used in everyday web applications to store session data in a cookie - the JWT contains all the information we need
- We ignore the issue of CSRF/Cross-site request forgery
- JWTs can be stale (different than expired) after a period of inactivity, so sometimes need to ensure their JWT is fresh, as well as valid
- See Jason Watmore's approach - server/API
- clone of Jason Watmore's system is in C:projects/node-mongo-registration-login-api (server)
- routines in \server\src
- \models\User.js for the Schema/model
- \modules\users for routines:
- config.json - has just the secret
- error-handler.js - centralised handling of api errors (TO DO include other modules' errors?)
- jwt.js - calls expressJwt to revoke the user token unless the route is specified as not requiring authentication
- user.service.js - contains the core business logic, encapsulates all interaction with the mongoose user model, and exposes a simple set of methods
- app.js includes a new major route: '/users', usersRoute
- \routes\user.js defines the actions for each sub-route (including our normal Controller aspects)
- the API calls that are public are in jwt.js, and to begin with we allow all calls
- A list of users is given at menu option 'Manage', and they can be deleted from there
-
very simple with mock data
-
more complete: Structuring a Vue project — Authentication
-
Could use Firebase - Google's realtime service, see Introduction
- VueSchool - not free, with code
- Useful: Build a Vue App with Firebase Authentication and Database
- uses vuefire
- also useful - OK on firebase and vuex, but too Vuetify!
-
We tried to use Auth0 Identity-as-a-Service (IDaaS), Managing
- We use Universal Login, as seen the Vue QWuickstart
- Use vue-auth0-handler and vue-cookie
- We have v9 of auth0-js see reference documentation
- Auth0 Community
- For other guidance see half way down Developing Production-Ready Apps
- and also the quick start
-
- Might have used Vue Auth but no
- For demo could use Vue Tour - a quick and easy way to guide your users through your application.
- We use the Buefy icon with fontawesome 5
- Free icons
- More details and a full-guide-to-using-font-awesome-icons-in-vue-js-apps
- Make sure you register the component and have added icons to your library before you bootstrap your Vue application (see main.js)
- then use eg <font-awesome-icon icon="arrow-up" />
- but be aware that in some cases self-closing tags are not allowed
- advanced svg icons
- Did use Element Icons
- ICONSVG is a tool to simplify the process of finding and generating common icons
- CSS Basics: selector { property: value; property: value }
- CSS Properties, and defaults
- CSS Selectors, and Tester
- class selector begins with a (.) period, id selector begins with a (#) number sign
- comma between selectors means for both
- space between selectors means for the right one within the left
-
between selectors means for the right one where its parent is the left one
- [attribute=value] means all elements with the given attribute having the given value
- etc
- Pseudo classes/elements precede by (:) colon eg a:visited and p:first-line
- class selector begins with a (.) period, id selector begins with a (#) number sign
- Use Bulma via Buefy for a CSS framework (like Bootstrap) - as used by vuelayers
- Bulma uses HSL colours
- High quality designs and components for Bulma.io - eg calendar, Lightbox
- Bulma-extensions (not Vuejs-specific) - divider, sexy checkbox and radio,** Icon Picker**, slider, switch, tags, badge, page-loader, Accordion, Calendar, Carousel, Pricing table, Quickview, Steps, Timeline
- SCSS/SASS Reference
- SAAS Tutorial - includes debugging
- Use scss within a .vue file - installed node-sass & sass-loader
- Globally Load SASS into your Vue.js Applications - for when loading
- Integrating and Using CSS Frameworks (eg Bulma) with Vue.js
- We used to use plain CSS ??with variables - see Everything you need to know about CSS Variables
- Learn CSS Layout - a short course
- Styling Vue.Js Components Using CSS - lots of places to put css and classes
- See also vue.config.js css-modules
- CSS Specificity: Things You Should Know
- How to Organize Your CSS with a Modular Architecture (OOCSS, BEM, SMACSS)
- CSS utility classes
- Buefy Components include all we need except (see below):
- Cascader (asked for)
- Slider
- source monitor ? for infinite scroll
(We are replacing Element by Buefy when possible)
- Guide, Quick Start and Components
- Awesome Element - A curated list of Element projects etc
- Element uses BEM-styled CSS so that you can override styles easily but also create a new theme
- VSCode-Element-Helper
- Language handling needed as Element standard is Chinese
- Vee Element enables the Vee Validate validation library to be used in Element UI
- Gitter conversations
- Element for VueJS: 5 things to love and 5 things to hate
- Form Input Bindings
- Further descriptions
- and this
- Use form element
- interestipng - use i18n translation strings even if only one language?
- Working with Dynamic Components in Vue.js - useful vanilla Vue using :is
- Vue.js — Forms, components and considerations - some dos and don'ts
- Building Custom Multi-Option Form Components with Vue.js
- On github - on npm
- Documentation, on github, my fork ( see Creating a pull request from a fork)
- another example and on Vue.js Feed
- css aspects and this fiddle
- How to dynamically load (part of) a form schema asynchronously
- Populate vue-form-generator's select field with dynamic values
- Attributes compatibility for input types
- VFG generator: Issues #503 and my #569
- Vue-multiselect - used in vue-form-generator
- other (custom) field types - including Public Custom Fields:
- vfg-field-array - a vfg field to handle arrays
- vfg-field-sourcecode - a source code field for vfg
- vfg-field-object - a vfg field to handle objects, with or without schemas
- vue-tel-input - for international telephone numbers
- vfg-field-matrix - a vfg field to handle matrices
- https://gitlab.com/m_pchelnikov/vue-form-generator-graphql#README - realize query building for GraphQL based on schema
- NO - vfg-field-checkboxlist
- multiple, and multi: true fields, is about fields that can have more than one value (cf see this)
- featured means bold
- validateDebounceTime (milliseconds) is used eg on text fields with validation
- fields can have complex definitions, and even incorporate buttons
- core field types are checkbox, checklist, input, label, radios, select, submit (button), textArea
- other field types we might use include cleave, image, pikaday, switch, vueMultiSelect (trying to avoid jQuery)
- fields can be grouped
- Built in Validators are number, integer, double, string, array, date, regexp, email, url, creditCard, alpha, alphaNumeric - Custom Validators are possible
- for style see client\vfg.css.txt
- Could (but don't) use Vuelidate validation - see introduction - for forms and other validations
- or Vuetify - Material Design Component Framework, see also extending Vuetify’s input field validation NO as it is too all-embracing - using Element UI et al
- see VeeValidate 2.1: Validation Providers
- See also this list of form components
- includes Rich Text Editing etc
- Interesting: How to Handle Multi-row Forms with Vue, Vuex and vuex-map-fields (automates computed get/set)
- For the (optional) color type VFG uses Spectrum Colorpicker, but this needs JQuery and there is no recommended non-JQuery equivalent
- We use TinyColor
- Modals
- We use portal-vue (a set of two components that allow you to render a component's template (or a part of it) anywhere in the document - even outside the part controlled by your Vue App!)
- can also see lessons from Advanced Vue Component Design course (not free)
- Did try to use buefy (b-modal), but now only use buefy css classes (see Buefy styles for ref.css)
- Essentially
- the modals are all defined at the top level (M4OPSView) but hidden until 'wanted'
- a modal is 'wanted' when showPortal sets title (in store/forms)
- showPortal also always sets portalName (eg ModalForForms), and (usually) actionTextsArray
- the Modal actually appears in portal-target in App.vue, a standard component from portal-vue
- this has a name bound to store/forms/portalName in Vuex
- one main Modal form component is ModalForForms (for general M4OPS forms such as LogIn), which handles
- linking to the portal-target (with name "ModalForForms")
- whether it shows (only if store/forms/title !== NOPORTAL - and the portal-target has the right name) or not
- the background and closing (hidePortal sets forms/title = NOPORTAL)
- the modal-card classes for head, body, foot etc
- the VueFormGenerator form, whose spec is defined by the 'thisFormSpec' getter, which
- assumes the value of forms/formId has been set (by showPortal)
- collects the relevant mode, schema, options from the Forms database
- the text on button(s) (from store/forms/actionTextsArray)
- the Submit action(s) (handleSubmit is hard-coded for now, with a switch on formId)
- This main Modal component is switched on from elsewhere by the actions showPortal({}), eg in
- ActionsPane and UserStatus
- it is switched off internally by hidePortal()
- it needs showPortal to have also set: formId
- Note that ModalForForms uses the VuexApi data as spec, thus its '(vfg_)model' is changed by any form entries
- for passwords, and any other sensitive data, the action to clear it should be included
- eg dispatch('clearFormField', {formId: 'LogIn', fieldName: 'password'});
- this data is imported from client\src\modules\forms\vfgData\forms.json
- Another main Modal component is ModalForMessages, similar to ModalForForms, except
- it needs showPortal to have set: messagesArray
- it just displays the text from the messagesArray, without using vfg
- Another main Modal component is ModalForOPSForms (for OPS-specific forms):
- it needs showPortal to have set: ldid (of a vector layer)
- it expects the specified layer to have a 'formId' property (eg 1B) corresponding to an OPSForm in the OPS' FormsArray
- it uses the getOPSFormByLdid getter to get the schema and formOptions
- it has a local copy of the model so nothing in the form data is overwritten
- but the initial values and updated values therefore need to be handled separately
- vue-stepper-component
- scrolling etc
- pagination
- Create documentation
- Tables/grids
- Charts
- Markdown
- Trees
- search
- GraphicsJS - a lightweight free graphics library, based on SVG/VML technology
-
See the official style guide 1 > 2 > 3
-
Naming conventions
- Component names are always multi-word, except for the root “App” component.
- Each component is in its own file.
- Filenames of single-file components (.vue) are in PascalCase.
- Components that are only used once per page begin with the prefix “The”, to denote that there can be only one (eg TheNavbar.vue)
- Child components include their parent name as a prefix (eg a “Photo” component used in “UserCard” is named UserCardPhoto).
- Always use full name instead of abbreviations in the name of components, eg UserDashboardSettings.
- Folder names are in lower case.
- Non component filenames are in camelCase according to the job that they perform, eg userDropdown.js.
- For components it's generally best to use PascalCase in the JavaScript, and kebab-case in the template - but Vue sees them as the same.
- Beware the kebab/Pascal issue (OPSDetails would have become o-p-s-details, so we use opsdetails)
-
Component data must be a function, as in export default {data () {return {foo: 'bar'}}}
-
Prop definitions should be as detailed as possible, including where possble a validator function
-
Always use key with v-for
-
Never use v-if on the same element as v-for
-
Styles in components should be scoped (except in the top-level App component and in ?? view components styles may be global)
- and use class selectors (eg .btn-close) rather than element selectors (eg button)
-
Always use the $_ prefix for custom private properties in a plugin, mixin, etc
-
Base components (pure components) that apply app-specific styling and conventions should begin with the prefix Base
-
Components with no content should be self-closing in single-file components, string templates, and JSX - but canot be in DOM templates
-
Where possible we use Single File Components (.vue) - needs Webpack, and has all in the one file:
- <template> - HTML code template
- <script> - JavaScript logic
- <style> - CSS styling
-
Elements with multiple attributes should span multiple lines, with one attribute per line (similar to JS object properties)
-
Don’t use arrow functions on an options property or callback, since arrow functions are bound to the parent context, 'this' will not be the Vue instance as you’d expect
-
Don’t use arrow functions in methods and computed properties as they almost always reference this to access the component data
-
Do use arrow functions for filters (see also the vue2-filters package)
-
“Mustache” syntax (double curly braces): {{}} for interpolation (of component data into a component template)
-
Component templates should only include simple expressions, with more complex expressions refactored into computed properties or methods.
-
Complex computed properties should be split into as many simpler properties as needed.
-
Non-empty HTML attribute values should always be inside quotes (even though without spaces they don't need to be)
-
Directive shorthands (: for v-bind: and @ for v-on:) should be used always or never (we choose always)
-
Component/instance options should be ordered consistently - see this list
-
Attributes of elements (including components) should be ordered consistently - see this list
-
In .vue files we do not indent what is in the <script> or <style> blocks (even though Vetur could do it)
-
For a comparison of Methods vs Watchers vs Computed Properties see page 90 of Vue Handbook.pdf
-
Can do dependency injection (provide and inject - sort of 'long-range props'), see also, and vue-inject - but beware
-
Vue CLI Provides Standard Tooling for Vue.js Development
-
To run a project > npm run serve
-
See Deployment
-
For amazon S3
-
(Remember that npm install -E (or --save-exact) ensures that the current version is not updated)
- state.mapping.currentOptionArray[3]
- default initialOpsCode
- getter: use mapState
- setter via updateCurrentOptionArray
- Note that store.getters.place.OPSCode holds the OPSCode for the OPS currently loaded into place
- store.state.users.account.user
- has .username and .status.loggedIn
- default none (username === 'Guest')
- localStorage.getItem('user') is used to keep user logged in between page refreshes - includes jwt token
- so is kept updated by routines in user.service.js, and used in authHeader
- but should not be used for any other reasons
- store.state.mapping.mainmap.chosenLayers (array)
- each has .ldid, .opacity (0-1), .displaytype (A or B)
- getter chosenLayersMainmap
- set by setLayer and setOpacity
- there is also the simpler store.state.mapping.chosenRhLayer which has just ldid
- initialised to initialStateChosenLayersByOpsCode(opsCode) - none yet
- default initialStateChosenLayers.default [Bing Aerial, OSM]
- configuration
- the-core-concepts
- Introduction
- See vue.config.js configurewebpack and here
- need DefinePlugin in webpack for environment variables eg process.env.NODE_ENV
- We were thinking of using Parcel as 'no configuration'.
- (nice but Vue CLI uses Webpack) - see Parcel and Vue
- See also A quick look at Parcel, and Getting Started With Parcel
- OL has this example, and their Building an OpenLayers Application tutorial
-
See also Lighting Up Your JavaScript With the Debugger - links to
-
Note that a DOM node with a property = a Function is reported as "[object Function]"
- a property = an Object is reported as "[object Object]" (similarly for an Array etc)
-
Chrome devtools has network tab - can turn on throttling to emulate a slow network (Online, Fast 3G, Slow 3G, Offline)
- Unit Testing Vue Components
- Test runner Karma
- Vue Test Utils is the official unit testing utility library for Vue.js.
- Can use vue-jest preprocessor
- Possibles
- Mastering testing with Vue.js by testing a real application and setting up CI/CD
- JavaScript Testing Fundamentals - then continues into a full paid-for course
- Could use ebook Testing Vue.js components with Jest - $14 +VAT
Use version number eg v5.3.0 (which is the one we use) or latest (the original M4OPS used v4.6.5)
- Documentation
- Tutorials
- (Current) Workshop)
- API
- Examples
- (Latest) code
- ol on npm
- Releases on GitHub
- Useful on Adding a progress indicator (ie loading layers)
- note that we use a v-if and spinner rather than buefy loading
-
Use vuelayers - Vue components with the power of OpenLayers
-
GitHub - scroll down for demo, quick start etc
-
Demo and demo code, App.vue code copied into vuelayers-demo
- also includes AlexRiceGist/ re storing map data in Vuex
-
- Demo uses vue-loader, vuejs-templates, and the webpack template
- but these are not needed now because Vue CLI 3 does it all
-
and use of $
-
see Complex components for generating eg layer components
-
Other Vue OL implementations - fairly simple, could copy
- vue-openlayers: humble wrapper
- vuejs-openlayers
- see also integrating OL was quite easy - but some gotchas
-
FAQ: What projection is OpenLayers using? - default is EPSG:3857
-
See the note on projections
-
this note on Coordinates in M4OPS includes something on projections
-
In M4OPS v1 we had: a Note on projections, From this discussion
- The data in Open Street Map database is stored in EPSG: 4326 (Spherical Mercator: decimal degrees)
- The Open Street Map tiles and the WMS webservice, are in EPSG 3857
- So it is usual to have to transform the basic maps from 'EPSG:4326' to 'EPSG:3857'
- When you configure a source with features, you have to provide them in the view projection (ie in EPSG:3857) as no transformation is possible.
-
Correction Features are always geojson, and always 'EPSG:4326'. Not sure what the statement above means
-
The values for HcN in the Testbed are
- lon0 = -3970;
- lat0 = 6860390;
- d = 300;
-
Other than that we do everything with GeoJSON in Lon/Lat ('EPSG:4326'), and the View is 'EPSG:3857' (metres) See these insights
-
in M4OPS v1 we convert from the featureProjection: 'EPSG:3857' to the dataProjection: 'EPSG:4326', decimals: 8}) when we writeFeatures
- EPSG:4326 is WGS 84 -- WGS84 - World Geodetic System 1984, used in GPS, or Spherical Mercator: decimal degrees
- and HcN is [-0.0322294, 52.3305610] (lon, lat)
- EPSG:3857 is WGS 84 / Pseudo-Mercator -- Spherical Mercator: metres
- used by Google Maps (EPSG:900913), OpenStreetMap, Bing, ArcGIS, ESRI
- and HcN is [-3591, 6860098] (x,y) - (metres East, metres North)
- We do all georeferencing work based on the EPSG:3857
- EPSG:27700 is OSGB 1936 / British National Grid -- United Kingdom Ordnance Survey
- and HcN is [534174, 272097] or TL3417472097
- Northings and Eastings
- and HcN is 52° 19′ 50″ N 0° 01′ 56″ W
- For the internal view projection we use EPSG:3857 (the default) - this is the projection with which OpenLayers components will work (ol.View, ol.Feature etc)
- For the data-projection property on the vl-map component we use EPSG:4326 (as the demo does)
- We assume that all vector layers are geojson, and always read (and written) as 'EPSG:4326'
- Thus for plain coordinates, and GeoJSON encoded features or geometries there is a thin projection transform layer between Vue (EPSG:4326) and OpenLayers (EPSG:3857)
- to add a projection use createProj, addProj from ol-ext Projection transform helpers
- this has lots of other useful transform functions
- TODO
- shiftingProjections.js - we have not adapted yet (see ProjectionsArray in Place)
- extents in layerAndSourceTile.js
- projection in layerAndSourceWms.js
- projection in mousePosition
- Use the LayerDefs (string) projection member of their sourcedef
When the client needs to communicate with the server it sends an asynchronous GET or POST request via eg fetch(/continents
). The corresponding route is defined in the server's routes folder (and referenced in its app.js). When the server is done it notifies the client, which can then do any more processing dependent on the promise being completed.
graph LR;
C[Client] -->|POST or GET| S[Server]
S -->|Promise| C
The main program is app.js which sets up connections to the database, (express) routes, and listens on a port.
Each route (including with parameters eg :id) is
- in a file in the server/routes folder, which is
- referred to in app.js
- can be called from the client by eg fetch(
/continents
)
- All on localhost:5000 (and formatted by JSONView)
- /places - list of studies in M4OPS
- /places/xxx - OPSDetails for XXX study, with all its bits and arrays
- /continents - continent/country/location/study (each has M4OPS:true where it is/contains an included study)
- /m4opsdata - the M4OPS.json file, with all its bits and arrays
- /featurelayers/OPS_Xxxx - the given feature layer from the given OPS in FeatureLayers
- /places - list of studies in M4OPS
The MongoDB URL (including database) is set in the environment variables (qv)
For each type of data held in the database, (via Mongoose) we have
- a data structures (or schema) defined in a file in the server/models folder
- a model defined in the same file
- a controller in the server/controllers folder that queries, deletes and/or updates the data using MongoDB
- call(s) from routes to the controller to do its work
graph LR;
C[Client] -->|POST or GET| S[Server]
S -->|Promise| C
S -->|model & controller| M[Mongoose/MongoDB]
M -->|Promise| S
- middleware
- server needed this then npm i before running with nodemon
-
provides users with a NoSQL document database system (Open Source) Documentation
-
we are on v4.0
-
Install MongoDB Community Edition on Windows
- Start windows service: Command line as Administrator “net start MongoDB” (not Powershell)
-
MongoDB is designed to be run in trusted environments (ie Localhost) - but in production we use authentication
-
URL (if we needed it) is mongodb://localhost:27017/m4opsdb
-
MongoDB Compass provides a GUI, queries, CRUD – use localhost:27017 documentation, Getting started
- Example query on Places {OPSCode:"HcN"}
- If problem, use Resource Monitor: cmd as Admin, resmon.exe -> CPU tab -> in handles type Compass, and Kill one (will kill all) associated processes - and wait for it to happen.
-
for the MongoDB interactive shell see the manual, or this tutorial - example use:
- mongo
- use m4opsdb
- show collections
- db.M4OPSData.drop()
- MongoDB Compass (but did not work)
- mongoimport can import JSON and csv into MongoDB
- Remember that it is one (JSON) document per line, although a single JSON document can span more than one line
- can use jsonformatter for checking one of the json, not using –jsonArray option
- from terminal, for a whole collection or a single document, use
- mongoimport --db m4opsdb --collection M4OPSData --drop --file C:\Users\Peter2\Documents\Mapping\Software\M4OPS2\M4OPS.json
- mongoimport --db m4opsdb --collection Places --drop --file C:\Users\Peter2\Documents\Mapping\Software\M4OPS2\Places.json
- mongoimport --db m4opsdb --collection Continents --drop --file C:\Users\Peter2\Documents\Mapping\Software\M4OPS2\Continents.json
- mongoimport --db m4opsdb --collection Forms --drop --file C:\projects\m4ops\client\src\modules\forms\vfgData\Forms.json
- (Note that for now we do not import Users - we set them up manually via /regiter)
- For Feature Layers
- can use: ...m4ops\utils\importAllFeatureLayers.bat
- Remember the url of the source in Places (eg Testing VFG.geojson) when prefixed by OPS_ (eg HcN_) and all spaces replaced by underscores (_) must equal the _id in the geojson file of the Feature Layer
- mongoimport --db m4opsdb --collection FeatureLayers --mode upsert --file "C:\Users\Peter2\Documents\Mapping\Software\M4OPS\OPS\ENG England\HcN Holywell-cum-Needingworth\FromDev\ForMongo\Pubs.geojson"
- and then:
- Buildings.geojson"
- Censuses.geojson"
- HcN land ownership.geojson"
- OSM20180209.geojson"
- test03.geojson"
- (adding --drop ensures the target instance drops the collection before importing the data from the input)
- (adding --mode upsert to replace documents whose _id matches the document(s) in the import file)
- (Note that the Studies collection is not used now)
- Note that layerAndSourceVector.js prepends the OPS_ (eg HcN_) to the layer id before using it in the URL to get it from the database via the server
- and the FromDev\ForMongo versions of the FeatureLayers have the extra _id field to correspond eg "_id":"HcN_Pubs"
- maximum BSON (Mongodb) document size is 16 megabytes, or use GridFS API.
- using M4OPS-manage and with OPS.csv as chosen file compile all the studies
- for each study upload the OPS.json file into the study's local folder
- use concatOPS.bat to create output.txt concatenated from all of the individual OPS.json files
- rename output.txt as Places.json
-
Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment - see documentation
- see also gotchas
- always use the third parameter in mongoose.model(singular, schema, collection), because of default upper/lower case and pluralisation issues
-
CRUD operations in a Node.js, ExpressJS, MongoDB using mongoose
-
creating a RESTful API using Node, Express 4 and Mongoose to interact with MongoDB
- see documentation
- for now we use mongodump
- data is stored at dbPath: "C:\Program Files\MongoDB\Server\4.0\data" in WiredTiger files
- mongodump --db m4opsdb --out "C:\My Backups\MongoDB\Dump00"
- see bsondump for reading bson files
- How to handle environment-specific settings in your JavaScript apps
- Working with Environment Variables in Node.js
- Implement NodeJS environment variables in a modern Webpack app
- the environment variables we need are defined in .env.example ( as used by dotenv-safe)
- PORT (only needed in development)
- MONGO_DB_URL see doc
- they are read before, or very early in, app.js starts
- console.log(process.env) shows them
- we use dotenv-safe to read from .env (untracked), and this is called if MONGO_DB_URL is not already set
- they include (but we do not use) all the windows environment variables such as PATH
- We use Claudia/AWS Lambda
- .aws/credentials is stored in %UserProfile% (ie Peter)
- environment variables are all in prod.json
- these are read by claudia via --set-env-from-json
- In the client we use Vue CLI 3 standards - see VueCLI 3: Environment Variables and Modes
- .env # loaded in all cases
- .env.local # loaded in all cases, ignored by git
- .env.[mode] # only loaded in specified mode
- .env.[mode].local # only loaded in specified mode, ignored by git
- (where [mode] is development, production or test)
- Only variables that start with VUE_APP_ will be available (via process.env. ), plus BASE_URL & NODE_ENV
- Computed env vars can be in vue.config.js (still prefixed with VUE_APP_)
- Note that the older Vue CLI 2 had a config directory with dev.env.js and prod.env.js
- The front end (Vue.js) is built within VS Code then uploaded to an AWS S3 bucket
- The (Node/Express) back end is constructed using claudia and uploaded into AWS Lamda
- The (MongoDB) database behind that is implemented in Mongo Atlas
Where possible we use AWS REGION N. Virginia (us-east-1)
-
Vue.js and AWS Lambda: Developing Production-Ready Apps (Part 1)
-
Vue.js and AWS Lambda: Developing Production-Ready Apps (Part 2)
-
Created (m4ops@one-place-studies.org) accounts with:
- AWS ( see also ...\Guiding documents\AWS S3 for storing maps.doc)
- Auth0 (for later)
- MongoDB Atlas (mLab is merging with them)
-
Now we use nodemon src/appd to start development server (Lambda server is at src/app)
- Vue Production Deployment
- Not used:
- GitHub Pages - we use AWS S3
- vue-loader is a loader for webpack that handles Single-File Components (SFCs):
- CSS Extraction
- Kubernetes vs. Docker: What Does It Really Mean?
- See Dockerize Vue.js App
From Terminal: C:\projects\m4ops\client>npm run build
- Compiled with 2 warnings
- asset(s) exceed the recommended size limit (244 KiB).
- js/chunk-vendors.2cee4a6c.js (1.2 MiB)
- entrypoint(s) combined asset size exceeds the recommended limit (244 KiB)
- app (1.51 MiB)
- css/chunk-vendors.253ba11b.css
- js/chunk-vendors.2cee4a6c.js
- css/app.655d80b5.css
- js/app.a9c51c81.js
- asset(s) exceed the recommended size limit (244 KiB).
File Size Gzipped
dist\js\chunk-vendors.2cee4a6c.js 1229.84 kb 347.15 kb
dist\js\app.a9c51c81.js 47.83 kb 14.06 kb
dist\js\about.30fd5a98.js 1.64 kb 0.68 kb
dist\precache-manifest.7a2d93dc6fc2147 1.01 kb 0.40 kb
a8173f6a6a2fd6cfb.js
dist\service-worker.js 0.94 kb 0.53 kb
dist\css\app.655d80b5.css 241.59 kb 31.64 kb
dist\css\chunk-vendors.253ba11b.css 31.60 kb 5.53 kb
dist\css\about.0e433876.css 0.00 kb 0.02 kb
See Vue CLI 3 deployment instructions - essentially drag and drop contents of dist folder into AWS bucket.
-
AWS Lambda needs AWS API Gateway to define how external services (or, in this case, a Vue.js client) can communicate with your serverless backend app - makes AWS Lambda not straightforward.
-
We use an open-source tool called Claudia.js.
-
To see what is happening in Lambda, and console messages, use AWS CloudWatch in the region we used (us-east-1) - prices
-
but see the serverless_computing_study
Claudia and Express (now we use node v8.11.3) Use the claudia CLI tool to prepare a serverless proxy around Express API: claudia generate-serverless-express-proxy --express-module src/app
Then using claudia create:
claudia create --handler lambda.handler --deploy-proxy-api --region us-east-1 --set-env-from-json prod.json
us-east-1 is Virginia
To re-create use claudia update --set-env-from-json prod.json
See our lambda check costs !!!
- Useful
- Popular Express middleware to define backend endpoints
- bodyParser (have) - Express middleware that parses request bodies so you can access JSON objects sent by clients
- cors (have) - Express middleware to make your endpoint accept cross-origin requests
- helmet - Express middleware that helps to secure your apps with various HTTP headers
- morgan - HTTP request logger middleware for Node.js web apps
- mongodb (we use mongoose??) - the MongoDB native driver for Node.js;
-
Deploying a production Node/Express Mongo App to AWS — soft lessons
-
refers to How to deploy a Node.js app to the AWS Elastic Beanstalk
-
See Google
-
Why serverless newbies should use a deployment framework recommends Serverless Framework
-
Now is an alternative to claudia?
- Documentation and pricing
- Free tier has maximum 512 MB storage, no automatic backups - see limitations
- login (manual) via RoboForm
- users m4ops_admin, m4ops_r, m4ops_rw - passwords in db
- AWS REGION N. Virginia (us-east-1)
- how to connect (command line - v2?!)
- mongoimport --host Cluster0-shard-0/cluster0-shard-00-00-bfjgs.mongodb.net:27017,cluster0-shard-00-01-bfjgs.mongodb.net:27017,cluster0-shard-00-02-bfjgs.mongodb.net:27017 --ssl --username m4ops_admin --password opl0oUiDw3w9FAH7 --authenticationDatabase admin --db m4opsdb --collection M4OPSData --drop --file C:\Users\Peter2\Documents\Mapping\Software\M4OPS2\M4OPS.json
- also need --type csv if not json
- --authenticationDatabase admin just means the user's details are in the admin db
- Compass URI Connection String: mongodb+srv://m4ops_admin:opl0oUiDw3w9FAH7@cluster0-bfjgs.mongodb.net/admin
- See also re Lambda
- whitelist etc
- Need VPC Peering and this for production, but
- (free) M0 clusters don’t support VPC Peering so we need
- whitelist 0.0.0.0/0 (access from anywhere)
- older URI mongodb://m4ops_r:iS2NPR3HBmsSTheK@cluster0-shard-00-00-bfjgs.mongodb.net:27017,cluster0-shard-00-01-bfjgs.mongodb.net:27017,cluster0-shard-00-02-bfjgs.mongodb.net:27017/m4opsdb?ssl=true&replicaSet=Cluster0-shard-0&authSource=admin&retryWrites=true
- Check this
- Also
- The Power of Web Components
- anatomy-of-a-url
- Convert/prettify
- In VSCode use JSON to JS converter - Select a JSON string in the editor and in press Ctrl+Shift+J
- Useful conversions (and prettifier) - eg
- JS Object to JSON
- JSON to Mongoose Schema
- Markdown to HTML
- CSS Formatter
- FreeFormatter
- to generate code in VSCode could use quicktype - copy json into clipboard and "paste as code"
- Any locally installed command (eg xx) will be available at ./node_modules/.bin/xx in your project
- node_modules/.bin directory will be added to system $PATH when your're running npm scripts, so you can directly use the local xx command there
- Popular Systems in 2017 - especially Vue.js
- We have a codeSandbox for trying things out
- unpkg is a fast, global content delivery network for everything on npm - it makes every npm package available in the browser
- Beware Memory Leaks
- to install a specific version use eg npm install vuelayers@^0.11.0 --save
- to reload not from cache use location.reload(true) - the parameter forces it to reload from the server
- more slots, less props