In this tutorial I’m going to show you how to build a user-driven photo gallery, powered by Angular, hosted on the App Server. Cosmic JS TL;DR View Demo View the full source code on GitHub Install the App and deploy to the Cosmic App Server Prerequisites You’ll need the node JS, npm and Angular cli pre-installed. Make sure you already have them before you start. Please refer to on how to do this. Angular docs Getting Started First of all we’ll need to create the Angular project. We’ll use ng cli to do it. So once you’ll have all prerequisites installed, you’ll need to setup the new Angular project: ng cosmic-angular new After you’ll setup this project you’ll be able to run cosmic-angular ng serve cd --open And play with your app in browser Doing everything using the existing git repo First of all, you have to be sure you have node > 6.x installed, than run the following commands: npm install -g @angular/cli git clone https://github.com/cosmicjs/angular-image-feed cd cosmic-angular npm install ng serve --open The most recent ng cli version at the article creation moment was 1.1.3. Browser window will open automatically once you’ll run the last command Setting up Cosmic JS library First of all, install the for Angular/JavaScript applications: Cosmic JS npm module npm cosmicjs install --save Now you should be able to import Cosmic object and perform Cosmic JS API calls like following: Cosmic ; bucket = { : }; Cosmic.getObjects({ bucket }, (err, res) => { .log(res.objects); }); import from 'cosmicjs' const slug 'your-bucket-slug' console Setting up things with Cosmic JS Create the bucket and remember the bucket name ( in our case): ‘cosmic-angular’ Than create a new object type named Photo and please remember the object type slug (photos’). We also need a way to store the picture itself. Please enter the “Metafields Template” tab and add “Image/File” type Metafield with key . This Metafield will store the image. We don’t need anything more, so just set the name and save Object Type. After save you’ll be redirected to ‘New Photo’ page. Create some photos using this page and save them - we'll use them as test data. 'photo' You’ll also need to create the Bucket write key. It’s necessary to allow users upload pictures and create photo Objects. Open Settings page and click ‘Generate new key’ on API Write Access Key than copy generated key and save the changes. Angular environments Edit the to match the following: src/environments/environment.ts environment = { : , : , : , : }; export const production false write_key 'YOURWRITEKEY' bucket_name 'YOURBUCKETNAME' photos_type 'photos' Configuration service for Angular We’re planning to use Cosmic JS Objects in more than one Angular component. In such case makes sense to create a dedicated configuration service and store all Cosmic JS related things such as bucket name, write key, etc in a single place. Let’s create with the following contents: src/services/cosmic_config.ts {Injectable} ; @Injectable() { private write_key; private bucket_name; private photos_type; () { .photos_type = environment.photos_type; .write_key = environment.write_key; .bucket_name = environment.bucket_name } public getReadCfg(): any { { : { : .bucket_name } }; } public getWriteCfg(): any { { : { : .bucket_name, : .write_key } }; } public buildPhotoUploadObj(title, file): any { { : .write_key, : .photos_type, : title, : [{ : , : , : file }] }; } getPhotoSlug() { .photos_type; } } import from '@angular/core' export class CosmicConfigService constructor this this this return bucket slug this return bucket slug this write_key this return write_key this type_slug this title metafields key 'picture' type 'file' value return this This service has a few methods: - returns config object for reading data getReadCfg - return config object for writing data (with write_key specified) getWriteCfg - builds object to create photo using file name and title buildPhotoUploadObj - returns object type slug for photos getPhotoSlug We’ll call these methods from our Angular components. View the gallery — Angular part Create file with the following content: src/components/picture/picture.ts { Component, Input } ; @Component({ : , : }) { @Input() picture: any; () { } } import from '@angular/core' selector 'picture' templateUrl './picture.html' export class Picture constructor Than create a template for Picture component: <div = > <img class="ui fluid image" [src]="picture.metafield.picture.url" alt="{{ picture.title }}"> </div> <div class="content"> <div class="description">{{ picture.title }}</div> </div> class "ui card picture-item" < = > div class "image" </ > div We’ll use this component to display a single gallery picture item. Now create file with the following content: src/components/picture_upload/picture_upload.ts { Component, Input, Output, EventEmitter } ; Cosmic ; { CosmicConfigService } ; @Component({ : , : }) { private fl; private title; public uploading; @Output() onUpload = EventEmitter<any>(); ( private cosmicConfig: CosmicConfigService ) { .uploading = ; .fl = ; .title = ; } onFileChange(ev) { (ev.target.files && ev.target.files.length) { .fl = ev.target.files[ ]; } } upload() { .uploading = ; Cosmic.addMedia( .cosmicConfig.getWriteCfg(), { : .fl, : .fl.name }, (error, response) => { Cosmic.addObject( .cosmicConfig.getWriteCfg(), .cosmicConfig.buildPhotoUploadObj( .title, response.body.media.name), (error, response) => { .title = ; .fl = ; .uploading = ; .onUpload.emit({}); }); }); } } import from '@angular/core' import from 'cosmicjs' import from '../../services/cosmic_config' selector 'picture-upload' templateUrl './picture_upload.html' export class PictureUpload new constructor this false this null this "" if this 0 this true this media this folder this this this this this '' this null this false this Than add the following template: Upload photo Upload is in progress < = > div class "picture-upload" < = [ ]= > div class "ui form" ngClass "{ 'active dimmer': uploading }" < = * = > div class "ui grid" ngIf "!uploading" < = > div class "five wide column" < = > div class "field" < = = ( )= [ ]= /> input type "text" placeholder "Title..." input "title = $event.target.value" value "title" </ > div </ > div < = > div class "six wide column" < = > div class "field" < = ( )= /> input type "file" change "onFileChange($event)" </ > div </ > div < = > div class "five wide column" < = ( )= > button class "ui primary button fluid" click "upload()" </ > button </ > div </ > div < * = = > div ngIf "uploading" class "ui text loader" </ > div </ > div </ > div Add and components to as it’s done with other components like . This will allow us to use it in our app. Picture PictureUpload app.module.ts AppComponent What happens here? Our component doesn't perform anything interesting - it just displays object properties. However component doing much more interesting thing. It creates a record on Cosmic JS servers, but this record has an image attached, so this makes the whole process more complicated: Picture PuctureUpload upload the image to Cosmic JS servers (using addMedia method) obtain uploaded image name create the new Cosmic JS object (using addObject method) passing it obtained image name fire an event to notify parent component about finished upload Concatenating everything together Now we need to modify our AppComponent to use these newly created components. Modify to look like the following: src/app/app.component.ts { Component } ; Cosmic ; {CosmicConfigService} ; @Component({ : , : , : [ ] }) { public items = []; page = ; page_size = ; scrollEnabled = ; ( public cosmicCfg: CosmicConfigService ) { .reload(); } reload() { .items = []; .page = ; .scrollEnabled = ; params = { : .cosmicCfg.getPhotoSlug(), : .page_size, : }; Cosmic.getObjectType( .cosmicCfg.getReadCfg(), params, (err, res) => { .items = res.objects.all; }); } onUpload() { .reload(); } onScroll() { (! .scrollEnabled) { ; } .page++; params = { : .cosmicCfg.getPhotoSlug(), : .page_size, : .page * .page_size }; Cosmic.getObjectType( .cosmicCfg.getReadCfg(), params, (err, res) => { (res.objects && res.objects.all) { res.objects.all.forEach( { .items.push(itm); }); } { .scrollEnabled = ; } }); } } import from '@angular/core' import from 'cosmicjs' import from '../services/cosmic_config' selector 'app-root' templateUrl './app.component.html' styleUrls './app.component.css' export class AppComponent 0 2 true constructor this this this 0 this true let type_slug this limit this skip 0 this this this if this return this let type_slug this limit this skip this this this if ( ) => itm this else this false And make its template like following: < [ ]= [ ]= ( )= > div infiniteScroll infiniteScrollDistance "1" infiniteScrollThrottle "300" scrolled "onScroll()" < ( )= > picture-upload onUpload "onUpload()" </ > picture-upload < * = [ ]= > picture ngFor "let item of items" picture "item" </ > picture </ > div We’re planning to have the infinite-scrollable gallery (and you can already see it’s directives in code). This means we have to install the right library: npm install -- ngx-infinite- save scroll What’s happening here? until we got empty response, we’re assuming there are more photos on server we’re fetching photos in bulks of 2 ( property) page_size we’re fetching only ‘photo’ type objects (this is useful in case if we have more object types) once we got empty response, we’re setting the flag and stopping try to fetch more photos once we’re getting an event from component, we're resetting the whole list. PhotoUpload Deploy to Cosmic JS servers Cosmic JS has some requirements for deploying apps: it must be in public git repo, or you need to for private repo deployment connect your GitHub account depending on your platform must be met Specific requirements In our case we actually have HTML5 app, so we’ll need some additional software. Prepare config Create a file in your project directory: prepare.js fs = ( ); str = ; fs.writeFile( , str, { (err) { .log(err); } .log( ); }); var require 'fs' var ` export const environment = { production: true, write_key: ' ', bucket_name: ' ', photos_type: 'photos' }; ` ${process.env.COSMIC_WRITE_KEY} ${process.env.COSMIC_BUCKET} "./src/environments/environment.prod.ts" ( ) function err if return console console "The file was saved!" This script will rewrite default Angular production settings file to use your Cosmic JS bucket write key and bucket name. Modify package.json Angular cli adds some packaged on as . We have to move them in to make our scripts work: package.json devDependencies dependencies ... : { : , : , ... }, ... "dependencies" "@angular/cli" "^1.1.3" "@angular/compiler-cli" "^4.0.0" Prepare software We’ll also need something to serve our Angular app. We’ll use Express framework: npm install -- save express Add the following to your package.json: { ... : { ... : }, ... : { : , : } ... } "scripts" "start" "node app.js" "engines" "node" "6.9.4" "npm" "4.2.0" The main point is to have command defined in the section (you can safely replace default angular command). This is the command which will be run to start our app. So now we have the only thing left - create the file: start scripts start app.js express = ( ) app = express() app.use(express.static( )); app.listen(process.env.PORT, { }); const require 'express' const './dist' ( ) function This is a simple Express app which serves dist dir as dir of static files. Please take note - app listens on port specified via environment variable, it's important to run apps on Cosmic JS App Server. PORT Build Angular app for production We’ll use to do this (dokku section): app.json predeploy { : { : { : } } } "scripts" "dokku" "predeploy" "node prepare.js && ng build --aot --prod" This script will be executed before we’ll launch our express app to build the Angular app for production. Run it! Now you can enter ‘Deploy Web App’ page in your Cosmic JS Dashboard. Simply enter your repo URL and click ‘Deploy to Web’ — deploy process will be started and app become ready in a couple of minutes. Conclusion Using the Cosmic JS App Server allows us to quickly deploy the application to hosting using a git repo and you don’t worry about server configuration and software installation — everything will be done by Cosmic JS servers. This article originally appeared on the . Cosmic JS Blog