LOL. I do that so that I can easily recognize what type of component it is. But, I use underscore like so: lbl_Title
This is a great post with a lot of helpful tips. Any level of experience could learn something from this post. I wanted to share this thread with newer developers but wanted it a bit better organized. Thanks to the help of my make-believe assistant, I was able to do this relatively easy!
Wanted to share the output here too.
I take no credit for this knowledge.
1. Naming, Folders & Structure
-
Donāt be scared of making folders; navigation is keyābetter folders than a mess of files.
credit: @danielps1818 -
Give inputs names that match their purpose; container names should describe contents (Top/Bottom/Left/Right, Header/Footer) to ease view-tree navigation.
credit: @Ryan_Deardorff -
Fixing container/component names can break views that donāt use #13.
credit: @pturmel -
Aim for Jython-friendly naming: avoid spaces and be careful with non-alphanumeric characters.
credit: @msteele -
Build tag paths that are concise, human-readable, and meaningful.
credit: @msteele -
Folders can have custom properties (not visible in Designer) and can be written via
system.tag.writeBlocking()or tooling.
credit: @msteele -
Document your naming/folder conventions so others (and future you) can follow them; consistency matters more than perfection.
credit: @Brandon_Peterson -
āFlexContainer_0ā⦠if you follow that pattern, remember to increment.
credit: @msteele
2. Scripting Architecture & Reuse
-
Put event logic in the scripting library; event/tag/property handlers should be one-liners that call library functions.
credit: @bkarabinchak.psi -
Reasons: version-controllable/diff-able; easier to test via script console; decorators (e.g.,
@timeAndLog) add cross-cutting features without editing every event; reusable; avoids old scoping issues.
credit: @bkarabinchak.psi -
Prefer a base parent project for reusable code over copy-and-paste.
credit: @Ryan_Deardorff -
Reuse aggressively (imports/exports, reusable classes); think about the next projectābut note that āglobalā variables and heavy abstraction can introduce errors.
credit: @danielps1818 -
Beware over-abstraction: itās OK to duplicate views (e.g., create vs. edit) instead of forcing one to fit multiple roles; simpler and safer to change later.
credit: @pascal.fragnoud
Example decorator:
@timeAndLog def foo(*args, **kwargs): passcredit: @bkarabinchak.psi
3. UI & Perspective (Bindings, Patterns, Animations)
-
Donāt push from tag scripts to UI; bind tags to UI properties to pull data, and place change-monitoring actions in the UI where needed.
credit: @pturmel -
Scripts are resource-heavyāuse bindings instead (preferably without script transforms).
credit: @pturmel -
Simple property bindings are fastest.
credit: @pturmel -
Tag and named-query bindings are asynchronous; reference them with simple property bindings instead of curly-brace tag refs or scripted queries.
credit: @pturmel -
Expression bindings are faster than scripts; if you must script in a binding,
runScript()in an expression binding is faster than a script transform.
credit: @pturmel -
Avoid the
tag()expression function ālike the plague.ā
credit: @pturmel -
Donāt use scripts for Perspective animationsāuse CSS/style animations.
credit: @victordcq -
If something seems āimpossibleā in Perspective, ask on the forum; CSS/JS injection workarounds may exist.
credit: @victordcq
4. Scope (Gateway vs Client)
-
Schedule/house server-side tasks (e.g., 5 AM resets of DB tables/tags) in the gateway scopeānot clientāso they run even if clients are closed and to avoid NĆ duplication across many clients.
credit: @bkarabinchak.psi -
Inverse: donāt use gateway tags to track client UI state; use client tags/session props.
credit: @bkarabinchak.psi
5. Database & Queries
- Get DB design right: correct data types (donāt store dates as strings); use named or prepared queries; use transactions; ensure uniqueness via a proper PK or UNIQUE index.
credit: @bkarabinchak.psi
6. Tags, UDTs & Tooling
-
Spreadsheet Import Tool can create implied folders while writing tags and UDT instancesāhelps counter the āUDTs are inflexibleā critique.
credit: @msteele (plugging @pturmelās tool) -
Custom properties are powerful (e.g., store view paths/tag paths as strings).
credit: @msteele -
Explore var-map functions in the Integration Toolkit module for advanced patterns.
credit: @msteele
7. Process & Community
-
Document conventions so you can iterate and improve across projects.
credit: @Brandon_Peterson -
If people say itās impossible, tag experts for workarounds.
credit: @victordcq
I don't find a wall of mostly bold-face text to be "better organized". Not to mention that you omitted all of the reference links. LLM output is generally not welcome here on this forum. See the FAQ for details.
Do you have suggestions on how it could be organized for newer users? For instance if they wanted to improve their UI.
Maybe not the end all best way to bucket the tips but it was quick way to get a first iteration.
You mention a couple of mine, but not the one I feel strongest about (#13). It can save so much time and hassle when rearranging components. So much so that I feel like Ignition should never have allowed custom properties on anything but the view, and made each componentās props only visible to itself.
Yes, read the whole topic, follow the links. The topic itself is a bunch of summaries. Re-summarizing is lossy.
Newbies using LLMs with Ignition is a good way to waste time and get in a bind.
Agreed on having a newbie avoid LLMs all day. This came to mind after talking to someone about how data flows through a project, and wanted to give them community driven tips. Theyāre some in here but in between heaps of other good tips.
Using this thread as the dataset for the LLM to paraphrase from I felt a bit better about myself. I also donāt like how it has the reply links omitted not allowing one to easily see more context. Was interesting to have this as a use case and see the result.
Most of this has been covered already otherās posts but I thought Iād write up some mistakes that happened on the first project I inherited and had to fix up.
- Complex bindings - passing objects (key1) to embedded views rather than simply values. At first it seemed fine until the project grew and bandaid fixes would add more sub-tags to key1 and then perspective started to not always display the correct objects. A lot of rework was done to only pass simple values like tagpath into the object and then move any additional info into the UDT itself.
- SQL - We had some niche requirements that needed custom database tables made to store certain things we couldnāt save into tags. During early development it was never considered that clients on gatewayA wonāt have a network path to the database connected to gatewayB for cybersecurity reasons. Took a fair bit of re-work to break up how things were done, moving some into tags and some into remotely executing named queries with message handlers. In hindsight I wouldāve pushed back on the project requirements to simply just not agree to do certain things.
- Too many embedded views, not necessarily related to point 1 but rather just making every little widget itās own view but often times some of those widgets were only ever used in one place. This meant for maintenance you had to constantly jump around the project to find where you could make a change. Use embedded views when it makes sense or theyāre recycled but donāt needlessly use them and create clutter.
- Be mindful of choices when laying out tag / folder structures. Objects like tagbrowser or powerchart use those structures so itās important to make things clear and consistent from the very beginning as operations will see it down the road.
- Make better use of session.custom properties and not always rely solely on view.custom.
Donāt do what I did in one project and name your views too generically. For example:
Specific Thing > Views > Main View
Other Specific Thing > Views > Main View
When you have a bunch of tabs open and several are named āmainā or ātemplateā or something else really generic, itās a huge pain for your workflow.
I donāt know if this was mentioned or not, but be very careful with Script transforms on bindings, especially in template views. If that template is on a flex repeater and iterated a bunch of times, every iteration is going to run that script transform, and then multiply that by every session of that project thatās open, and you could have literally hundreds of scripts per second running before you know it. Iāve seen it take down gateways before.
Ideally, scripts should be run at very slow intervals, or on-demand only. If there is any way you can possibly avoid a script transform on a binding, do it. If you canāt avoid it, try to put the script in your project library and call it with the runScript() expression instead. You should only ever have a script transform if there was absolutely no other way to accomplish what you wanted to accomplish. And if you think thatās the case, it is well worth the time to research and make sure it is. Not every nail needs a python-shaped hammer. There is usually a better way to do it.
āNewbies using LLMs with Ignition is a good way to waste time and get in a bind.ā
I should tell you the tale of the interns who used ChatGPT to write an Ignition project and ended up doing nearly ALL of the scripting in Named Queries as a result. Iām talking For loops. Iām talking DO-Whiles. Iām talking If-Then-Else. Things I had no idea SQL could (or should) even do. I sure learned a lot about itās capabilities and I sure hated it.
Weāre scrapping everything they did and starting over. Thankfully this was just a low-stakes internal project. It would be an utter nightmare if we trusted them with billable work.
They should have used Claude ![]()
SQL is an awesome language if you really dive deep. And it's fast.
But yeah, certain things just shouldn't be done in SQL.. even if they can be.
If the data source is large (I.E. A large site with a lot of tags, alarmed and historized from a lot of PLCās), is it not reasonable to offload fuctions to the database? Iām referring to pgFunctions in PostgreSQL in this case. I found by doing this, it massively improved the response time in my Perspective views. How do you suggest handling this situation?
If it got to the point where running a series of SQL statements in ignition was going considerably slower running it inside a function all in the databse, then it makes sense to switch.
I certainly would not start there and assume that though. I would program all my SQL statements in a single transaction in ignition and only if I run into a bottleneck seek to offload it to the database entirely in a function/stored procedure and see if it improves or to see if caching somewhere could help.
What were your queries like before that were taking so long? Since you mention its for a view I have to imagine its mostly select queries. I do find it odd that running them inside a function would provide a substantial boost over running through ignition. I am curious what your specific queries are that cause such a situation and what perfomance was in ignition vs a pgFunction.
Worth noting too if these queries you are running involve UPDATE/INSERT/DELETE you 100% should be wrapping them in a transaction in Ignition for both atomocity but also it improves performance for basically all flavors of db. If thatās the case I wonder if in ignition you were not wrapping them in a transaction but in your pgFunction you are. Quick search does tell me pgfunctions do automatically execute inside of one transaction so this may be something.