Scope, and using 'this' in the context of ionic components. Is it the same as using 'this' in a standard javascript environment?


#1

Until recently, I was under the impression that using the ‘this’ keyword using ionic’s product and its’ Component/Page structure was not “bad”. As an example, I have a page called TicketsPage. Im especially curious in two regards.
@Component ({
selector: ‘page-tickets’,
templateUrl: ‘tickets-html’,
}).

export class TicketsPage {
venues: any;
users: any;

constructor (public mySharedOb: MySharedOb) {

this.venues = [];
this.user = {};
this.user[‘tickets’] = [];
}

getTickets(){
for (var i = 0; i < this.user[‘tickets’].length; i++) {
this.venues.push( this.user[‘tickets’][i] )
}

}

My questions are:

  1. Simply put, is this a bad practice? Is using ‘this’ and assigning it properties the same as using ‘this’ in a not-ionic environment?

  2. Is my use of ‘this’ creating properties on and/or exposing the global environment?

  3. Or am I creating properties on TicketsPage, my ionic page?

  4. Being that TicketsPage is a ‘class’, is using ‘this’ in reference to it just fine?

  5. Is creating this.venues as an array in TicketsPage’s constructor any better/worst/different than creating this.user as an object in TicketsPage’s constructor and assigning this.user new properties?

Sorry to post a pretty basic Javascript question here, but from what I understood, using classes and using them in Ionic’s context had different implications. Am I wrong?


#2

Yes, but “this” in JavaScript is inherently unreliable. You can protect yourself from this greatly by following these two rules:

  • never type the word “function” inside of one; use arrow functions always
  • never pass methods as callbacks, always use lambdas

No and yes.

Can’t really answer this one yes or no. Inside the constructor, this is an object that purports to be of type TicketsPage. Inside other methods, it probably also is, if you followed the rules in the answer to Q1.

This one is also hard to answer, because you don’t declare a user property. You declare a users property. Compounding the problem is that it’s any. Don’t use any in your app code unless absolutely positively necessary for some outside reason. Always give proper types to all your properties. This makes your code less error-prone and largely self-documenting.

In general, one of my strongest design rules is “avoid putting the same thing in two places at once”, so the fact that you seem to be effectively cloning user.tickets into venues worries me. I would also recommend always using let instead of var.

This is just a stylistic preference, but I like using initializers when possible instead of doing initialization in the constructor, because I don’t have to look in two places to see both something’s type and its initial value. This also tends to save me from intending to initialize something in the constructor and forgetting. If I do it at the declaration point, I can’t forget.


#3

Thank you very much. Really good information. In regard to moving this.user[‘tickets’][i] into this.venues, I left a lot out for the sake of brevity. this.venues has categories in and of itself, and tickets are directed to specific arrays based on identifying properties on specific tickets. As they are shuffled into venues, each is spliced from its respective array. Based on that fact, do you still feel it’s a bad practice and something I should reconsider?

Using any as types being a bad idea is priceless to hear at this point. I’ve used that practice ALOT in a very big, complex app. Thank you for letting me know before gettiing rid of those ‘anys’ became a 3 week process.


#4

I like composition a lot, so if I were modeling your domain, I would do something like this:

export interface Venue {
  name: string;
  location: string;
  capacity?: number;
  petFriendly?: boolean;
  servesFood?: boolean;
}

export interface Ticket {
  id: string;
  venue: Venue;
  date: string;
  performer: string;
}

export interface User {
  name: string;
  email: string;
  tickets: Ticket[];
}

If Venue is sufficiently unwieldy that you don’t want to be sending the whole thing over the wire for every ticket, you could put just a venue ID in the JSON coming from the server and reanimate it to a Venue in the service model, but that complicates the situation a bit. In any case, I think a model sort of like this would be easiest to deal with in component code, which is the way I think. I push all the gory details of unmarshalling into service providers, instead of making components deal with what is easy for the server to be sending.

At this point, if you are iterating only across venues and don’t care about other stuff in the Ticket (like showing the date and performer name or printing a QR code of the id), you can make a case for populating this.venues, but instead of a loop, I would do this:

this.venues = this.user.tickets.map(tix => tix.venue);

If you want all the juicy stuff in each Ticket (which I would expect you would), I would just iterate across user.tickets in the template instead of bothering to set up a separate venues array.


#5

I’ll look in to the use of interfaces. I haven’t done so yet, but it looks like that may be the way to reach a level of comparmentalization I haven’t been able to reach at this point. I’m bringing all the data in from 3 different databases with http and injectable, then filtering tickets into most of my components based on if the user owns the ticket or has taken an action on another user’s ticket. Though I do have a main storage object I created to hold every user’s ticket in one of my components, which has the potential to have an obscene amount of data involved.

I hadn’t considered adding functionality to the SharedObject/@injectable page itself. If I understand correctly, that opens up some big possiblities for me. I wrote my first line of code 6 months ago so if it seems like i’m misunderstanding something, it’s because I am.

As of now, I’m using MySharedObject to move data from component to component, but have done nothing to the page aside from exporting it. If at some point you have the time to make any suggestions as to what I might be able to do on that page, by all means let me know.

As far as using let instead of var. I have quite a few functions that look like this.

for (var i = 0; i < this.user[ ‘tickets’ ].length; i++)
for (var h = 0; h < this.user[ ‘tickets’ ][i][9].length; h++) {
if ( this.user[ ‘tickets’ ][i][9][h][2] === this.venues[ i ][2] )
this.venues[ i ][2].push(this.user[ ‘tickets’ ][i][9][h];
}
Some of my functions go even deeper into iterating arrays within arrays. All I’m wondering is will those functions be effected by using let instead of var? Based on scope/block/anything?

Venues doesn’t worry me too much, but Tickets do. And yes, I’m working with alot of juicy details, so there’s a level of complexity that can’t be avoided. Your suggestions are already really exciting for me though. I’m struggling with efficiency in my approach so far, and think your tips will go a long way toward improving it. Many Thanks!


#6

If I could choose one point to try to get across in this discussion, it’s the bit I mentioned earlier about writing templates first and working backwards from there to the data model, the component controllers, and the service providers.

Write the templates so they are as simple and do as little work as possible. Just assume that the data will be in the exact format that makes their life as easy as possible. Then create interfaces for every business domain object that they deal with, and put properties into their controller that are objects and arrays exactly like the templates wanted, containing no extraneous data and requiring no complex logic in the page constructor.

Then and only then, write your service provider so that it has various functions that give the page controllers exactly the data they want in exactly the format they want. You can experiment with various ways of storing, repackaging, and caching inside your provider, without ever touching a single line of code in any other class.

If I’m doing the whole system, I then do the application server last, creating endpoints that give the client-side provider what it wants in as close of a format as I can. If you don’t control the backend, then you sort of have to compensate for some of this part in the provider, but don’t let any of that complex data munging leak outside of the provider.

It has taken me several decades of pain and complete rewrites to get to this point, but I am convinced that this method results in better, more maintainable designs. It is tempting to work the other way around - doing the provider first, and having it just pass whatever raw product is coming from the server or databases through to the page controllers, and having all sorts of complex filtering and massaging done in those controllers, or even worse, in the templates using a Rube Goldberg collection of pipes. I have gotten consistently worse results with that method.


#7

It’s been 6 months of pain mixed with too many multi-layered for-loops, so I’ll heed your warning avoid 19.5 more years of it. Right on, and appreciated.


#8

I’d like to chime in that I strongly agree with these design suggestions, and I use most of the recommended techniques every day. One difference in my work flow is that I write the ts file first and the template second, but with the attitude that if my template isn’t totally trivial, then I did something wrong. (Actually the template might be complex, because it is built up from multiple components, but then the template of each component is totally trivial.)

The idea is to make maximum use of AOT compiling, and to demand as little work as possible from the DOM, because reaching into the DOM tends to require an expensive step.


#9

And yeah. Don’t do this. If you make absolutely everything have a type, your rate of runtime errors will go down dramatically.


#10

Is it fair to say that you try to break things up into small enough chunks (components) so that in effect you have already written the portion of the template in your mind that determines what it is going to want from the controller and in what format before you start on the TS code?


#11

Sort of. It might be a distinction without a difference. Though, hard as I’ve found learning RXJS to be, the hardest part of this process for me has been wireframing user interfaces so (1) pages are performant, (2) the correct thing to do on a page is obvious, and (3) the gesture-distance between a user’s location and a desired functionality is very low. So the reality is that I’ve completely rewritten templates several times, but for UX reasons more than for programming reasons.


#12

Fair enough. I guess instead of when you write what, what I really want to emphasize is that template > controller > provider > backend is an ongoing customer/shop relationship proceeding from left to right, so the controller is a customer of the provider and a seller to the template. And the design should always favor the convenience of the customer at each interface point.


#13

Now, when the two of you refer to template, you’re referring to the .html correct? Again, i’m newer to ionic. And the html is referenced by
@component({
templateUrl: ‘tickets-page.html’
})

I’m sure there’s value in both approaches from a conceptual/mental-framework standpoint as well as a performance one. The worst approach? Probably mine. Figuring both out as you go. I’m not a graphics / app-ergonomics guy, so it has not been my main focus. I have highly functional grids with lists embedded in each column. That’s the reach of my designing ability.
In the future, I think it’s a good idea to put more energy into html/template creation. If anything, to solidify that mental picture / mental-framework before structuring the data that will go into it.


#14

Right.

“Template” = .html
"Controller" / “Component” = .ts containing class with @Component decorator
"Data model" = .ts containing a bunch of exported interfaces representing business domain objects
"Provider" / “Service” = .ts containing a class decorated with @Injectable that controllers inject in their constructors and rely on to get data from somewhere
"Backend" / “Application server” = program that lives on server, responds to HTTP requests, returns data from somewhere generally in JSON format representing objects of types declared in the data model

The scope of var is one of JavaScript’s many boneheaded language design decisions. let works how a sane person would expect var to, so you shouldn’t run into any trouble switching it. Instead of me trying to write up some examples demonstrating the difference, I think I’ll just let MDN describe it.


#15

Thanks. Simply put, effective response that clarified what the facets needed are. I believe my ‘Application server’ would be my website that directs data to and from databases via php scripts. I have not done any work on a data model, and will focus on that piece of advice more than anything at this point.


#16

You’re assuming I’m sane. I replaced var with let in quite a few functions already and is working just fine.


#17

I think this will help you a lot. Once you have a data model that is easy to consume, it will probably guide you in directions where you can push some of the work that you are doing client-side into the server-side PHP. I expect the current state of affairs is likely just a thin veneer over more or less direct dumps from databases. Doing as much work as possible on the server side is always a big win, because devices are relatively slow and there is only one of them. Servers are much faster and you can scale them up as much as needed.


#18

I would say that your thin veneer comment is probably accurate. Though, learning 5 computer languages including mysql to manipulate databases within 6-9 months has me on the very weak end of database knowledge. The extent of my php activity is
while($row = mysql_fetch_array($retVal, MYSQL_ASSOC)) { etc…}

and

$sql = “DELETE FROM sellers WHERE id=’$_request[0]’”;
$sql =“INSERT INTO buyers WHERE id=’$_request[8]’”;
$sql = "INSERT INTO users…etc’.

By no means am I asking for or expecting literal suggestions or for anyone to do my homework for me, just a yay or nay, is that quite thin? If so, I know where to turn my attention to in the upcoming weeks.


#19

I’m going to go with “probably”. I would focus on trying to define what you wish you wanted in your controllers, and then what you wish the JSON looked like coming from the app server from the client-side service provider’s POV. That should naturally define what work could potentially be migrated onto the server.


#20

Thanks Aaron, you both seem pretty emphatic about it so i’ll clean that up.