Global constant, environment variable, config?

Nowadays, what is the best/recommended way to set a global constant to be used across the entire app, like an API url string?
I’ve read about creating a service to store the constant and injecting it in your component, whenever it’s needed.
But there is also this: http://ionicframework.com/docs/api/config/Config/
… and this option too: https://stackoverflow.com/questions/41808521/ionic2-what-is-the-best-method-to-store-static-constants-like-api-urls
And a few other options…

I put constants related to models in the model file.
export const WIDGET_HELP_MESSAGE = 'Widgets are cool';
goes in the same file as the definition of the Widget interface.

But for your specific situation, a provider is ,most correct, because API calls should happen in a provider, not a page, as a general rule.

I’ve used that string enum technique to guarantee that types across different files all have unique names. But I think it’s a little too cute for the use case you describe. I’d be worried about producing something that works but is unreadable because it’s too fancy.

1 Like

I’m sure @AaronSterling is justifiably tired of hearing me whine about how braindead JavaScript is in general, and const in particular, but IMHO it is less than worthless.

Yeah, the way I produce database locations is with internal constants and exported functions, but this sounded like a lot more than you needed. But if you have 50 URLs or something:

const USER_LOCATION = 'Filepath/To/Users';
export function userLocation(userID: string): string {
  return USER_LOCATION + '/' + userID;
}

The function userLocation is guaranteed to return an immutable string with the correct path.

1 Like

Could you not do the same thing by assigning that string to an exported interface?

Such as

export interface User_Location {
 location: string,
  userId: string,
    combinedString: string,

      createPath (x: string) {
          this.location = 'Filepath/to/Users'; 
             this.userId = x;
               this.combinedString = this.location +  '/' + this.userId
}

Then dynamically set with something like

Let x = data.inputField;
this.User_Location.createPath(x);

That would radically change my conception of interfaces. Have you actually tried this?

On the regular. It’s how I’m coding all my apps at the moment

Yeah, I’m not really believing this. My IDE hates it, my sandbox project won’t build with it, and it makes absolutely no sense. Interfaces do not have concrete methods, and that is largely the entire point of their existence.

Perhaps I oversimplified. Here’s a quick scrapped together example.

export interface test {
  userId: string,
   path: string,
   combined: string;
   getValue(value: string);
}
 export class testClass {etc...}

Then on import

import { testClass, test } from '../../providers/testClass';
 export class tester {
 tester: test;
  constructor(etc...)

this.tester = {
      path: 'Path/to/users/',
      userId: "",
       combined: "",

       getValue(x: string){
           this.userId = x;
             this.combined = this.path + this.userId;
       }
    }

ionViewDidLoad() {
let x = "rapropos";
this.tester.getValue(x);
 console.log(this.tester);
}

I suppose one could also just create this.tester on the provider class and call

this.providerName.test.getValue(x)

having an interface that extends the test interface to push objects into an array has been pretty handy as well, and seems to be pretty useful when manipulating and moving around really complex data.

Then again, I have no idea what concrete methods are, so my code might be common fare. But, it does seem to do away with your least favorite of terms, const.

Maybe I don’t understand your technique yet, but right now it seems to me that it breaks modularity and DI. The goal is for class A not to know or care about how to find information in the database. Instead class A asks black box B to produce the information. So the only data that passes from A to B is the request for a user’s profile, for example. Your sample code requires class A to know something about how database URLs are created. I don’t see how that scales, for example if you want to swap databases because you decided one is cheaper, you can’t just change your database provider. You’d have to change every class that asks a question of the database.

Understood, kindof. I’m newer to the concept of interfaces, at least in practice. Rapropos actually introduced me to them a couple months back, but I have been unable to do any programming until recently, so I just started using them. I have no formal training whatsoever so alot of the concepts and/or intended use of certain concepts is over my head or just not understood well. I do have one interface on a provider that returns data to objects on request from other pages without sharing the rest of itself. Is this closer to what an interface is expected to do?

The interface

export interface Keyword {
  value: string,
   deleted: boolean
}

export interface SingleImage  {
  value?: string,
  deleted?: boolean,
  keywords?: Keyword[],
  notes?: string
}

export interface Gallery extends SingleImage {
  images?: Array<{SingleImage}>,
   title?: string,
   setTitle(title: string),
  }

export interface ImageCollection extends Gallery {
  date: Gallery[],
   getSingleGallery(x: Gallery, title: string)
    transferSelf(x)
 
}


this.imageCollection = {
  date: [],
setTitle(title: string){
  this.title = title;
},

 getSingleGallery(x: Gallery, title: string){

   for (let i = 0; i < this.date.length; i++) {
       if (this.date[i].title === title) {
       return;
             }
            }
        this.date.push(x)
           },
             transferSelf(x){
               for (let i = 0; i < this.date.length; i++){
                x.push(this.date[i])
             }
      }
}  

The transferSelf(x) method is how i’m moving data from provider to pages, pushing date[ i ] which contains image galleries.

Or is this a complete bastdardization of what an interface is supposed to do / be?

I use interfaces to represent PODs, which have no methods or logic. They only carry data around. Galleries are groups of images, they are not images themselves, so it makes no sense to have Gallery extend SingleImage. I don’t see why ImageCollection is not a collection of SingleImage, but rather of Gallery. So, Keyword and SingleImage are fine, the rest I’m not such a big fan of.

1 Like

Each gallery is a representation of images taken on a specific date, so my objective is to have an array of Gallery’s that get called by said date. Also, I’m experimenting, so the constructive criticism is most welcome.

But, again, my logic is to create Gallery’s by date, and access that gallery by selecting that date. The date itself is represented by the title property on each gallery. The overall objective is to create an app that acts as a legal aid for attorneys requesting images from clients, sorted by date, searchable by date and keywords.

I do, however, see your point in that I may want to think of interfaces as vessels or mediums as opposed to actionable “things”.

My advice, for what it’s worth, is to put all logic (i.e. methods) into service provider classes that are managed by DI. Do not put direct data in them, at least not exposed publicly. The only way that actual data should be exposed is through Observables or Promises or functions. If you follow that rule you will avoid the temptation to have “gatekeeper” properties like dataReady that are tough bugs just waiting to happen.

Actual data containers should have no intelligence or functions. They should only have properties. Inheritance is vastly overrated, and should be avoided as much as possible in favor of composition.

Ok, this makes a lot of sense to me. It clarifies the purpose and expected behavior of providers, interfaces, and how they should interact.

There’s a tension because the “dumb data containers” approach requires extra structure to make things stupid on purpose. If you only have a couple pages in your app, it might be more trouble than it’s worth – you’re adding extra structure to make it harder to communicate. The benefit comes when you have several pages and/or several providers. Then it becomes important for everything to stay in its own lane. It’s like the human body., The cells in the lungs know nothing about the big toe; they just send oxygen in a toey direction. And that’s basically what you want – each area of your code knows exactly what it is needs to, and nothing else.

Perhaps, but it also allows eliminating some. For example, when business objects are coming from an external source (like an HTTP backend), you need (generally) zero code to turn them into a usable state. With “smart data containers” you normally need either a bunch of boilerplate or gnarly reflection gimmickry just to get them constructed out of JSON.

This was something that bothered me constantly when I was using moment.js to handle dates. I would constantly forget to reanimate the moment objects as needed, and I couldn’t cleanly type date fields in business objects because the app was supposed to consider them moments, but inside the service provider we had to know that they were really ISO8601 strings when coming in. date-fns eliminates all this tediousness, and I consider the problem symptomatic of trying to put any sort of intelligence into the POD itself. Even if I didn’t really want to, the library was forcing me to.

I’m thinking, at least in this specific app, that the ‘dumb data’ approach might be best. I have multiple pages, am passing data to Modals, passing data with viewCtrl.onDidDismiss(data), etc., so I like the idea of simplifying whatever I can. Also, considering that the data i’ll be passing around will be shared among attorney / client, security and privacy are important factors.

On the other hand, I have an app that tracks altitude, notices when sharp drops in altitude occur, emits a beacon and phone’s geolocation, and (in theory), will alert 911 to the phone’s location. Essentially, an app that lets emergency services know if a car goes off a cliff and where the car is at. In this case, would you say that having “smart containers” is actually a benefit since the data being processed is intentionally public?

I think there’s almost never an upside to coding in a way that doesn’t scale. What if your emergency services app goes viral and you want to add a lot of new features to it? Unless a project is literally a homework problem, it’s probably worth building as though it will someday be bigger than it currently is.