--- name: Wheels View Generator description: Generate Wheels view templates with proper query handling, form helpers, and association display. Use when creating or modifying views, forms, layouts, or partials. Prevents common view errors like query/array confusion and incorrect form helper usage. Handles index views, show views, form views, and layouts with proper CFML syntax. --- # Wheels View Generator ## When to Use This Skill Activate automatically when: - User requests to create a view (e.g., "create an index view for posts") - User wants to create forms - User needs to display associated data - User is creating layouts or partials - User mentions: view, template, form, layout, partial, display, list, show ## Critical Anti-Patterns to Prevent ### ❌ ANTI-PATTERN 1: Query/Array Confusion **Wheels associations return QUERIES, not arrays!** **WRONG:** ```cfm ❌ ❌ ``` **CORRECT:** ```cfm ✅ ✅ ``` ### ❌ ANTI-PATTERN 2: Association Access Inside Query Loops **WRONG:** ```cfm

#posts.comments().recordCount# comments

❌ Fails!
``` **CORRECT:** ```cfm

#postComments.recordCount# comments

✅ Works!
``` ### ❌ ANTI-PATTERN 3: Non-Existent Form Helpers **Wheels doesn't have these helpers:** ```cfm #emailField(...)# ❌ Doesn't exist #passwordField(...)# ❌ Doesn't exist #numberField(...)# ❌ Doesn't exist ``` **Use textField() with type attribute:** ```cfm #textField(objectName="user", property="email", type="email")# ✅ #textField(objectName="user", property="password", type="password")# ✅ #textField(objectName="user", property="age", type="number")# ✅ ``` ## 🚨 Production-Tested Best Practices ### 1. Association Access in Query Loops (CRITICAL) **❌ WRONG - Associations Don't Work on Query Columns:** ```cfm #tweets.user()# ❌ FAILS - tweets is a query, not an object #tweets.likesCount()# ❌ FAILS - no such method on query ``` **✅ CORRECT - Load Object First:** ```cfm ✅ Works!

By: #tweetUser.username#

Likes: #tweetObj.likesCount#

``` **✅ BETTER - Preload with include (Prevents N+1):** ```cfm tweets = model("Tweet").findAll(include="user", order="createdAt DESC");

By: #tweets.username#

Tweet: #tweets.content#

``` ### 2. Checking if Query Has Records ```cfm ... ❌ Error! ``` ### 3. Counter Access Patterns **Direct column access (if eager loaded):** ```cfm tweets = model("Tweet").findAll(select="id,content,likesCount");

#tweets.likesCount# likes

✅ Direct access
``` **Association count (if not loaded):** ```cfm ✅ Count association

#likeCount# likes

``` ### 4. Boolean Checks in Views ```cfm // Redundant // Fails if bio doesn't exist ``` ### 5. Date Formatting ```cfm #dateFormat(tweet.createdAt, "mmm dd, yyyy")# #timeFormat(tweet.createdAt, "h:mm tt")# #tweet.timeAgo()# // "5m", "2h", "3d" ``` ### 6. Loop Query vs Loop Array ```cfm #tweets.content# ❌ Error! ``` ## Index View Template (List View) ```cfm #contentFor(pageTitle="Resources")#

Resources

#linkTo(text="New Resource", action="new", class="btn btn-primary")#

#linkTo(text=resources.title, action="show", key=resources.id)#

#left(resources.description, 200)#...

#resourceAssoc.recordCount# items #dateFormat(resources.createdAt, "mmm dd, yyyy")#
#linkTo(text="View", action="show", key=resources.id, class="btn btn-sm")# #linkTo(text="Edit", action="edit", key=resources.id, class="btn btn-sm")#
#paginationLinks(prependToLink="page=")#

No resources found.

#linkTo(text="Create First Resource", action="new", class="btn btn-primary")#
``` ## Show View Template (Detail View) ```cfm #contentFor(pageTitle=resource.title)#

#resource.title#

#linkTo(text="Edit", action="edit", key=resource.id, class="btn")# #linkTo( text="Delete", action="delete", key=resource.id, method="delete", confirm="Are you sure?", class="btn btn-danger" )# #linkTo(text="Back to List", action="index", class="btn")#
#resource.description#

Associated Items (#associations.recordCount#)

  • #associations.name# #dateFormat(associations.createdAt, "mmm dd")#

No associated items.

``` ## Form View Template (New/Edit) ```cfm #contentFor(pageTitle=resource.isNew() ? "New Resource" : "Edit Resource")#

#resource.isNew() ? "Create" : "Edit"# Resource

Please correct the following errors:

  • #errorMessage#
#startFormTag(action=resource.isNew() ? "create" : "update", method=resource.isNew() ? "post" : "patch")#
#textField(objectName="resource", property="title", label=false, class="form-control")# #resource.allErrors("title")[1]#
#textArea(objectName="resource", property="description", label=false, rows=6, class="form-control")# #resource.allErrors("description")[1]#
#textField(objectName="resource", property="email", type="email", label=false, class="form-control")# #resource.allErrors("email")[1]#
#textField(objectName="resource", property="price", type="number", step="0.01", label=false, class="form-control")#
#dateSelect(objectName="resource", property="publishedDate", label=false, class="form-control")#
#select( objectName="resource", property="status", options="draft,published,archived", label=false, class="form-control" )#
#select( objectName="resource", property="categoryId", options=model("Category").findAll(), valueField="id", textField="name", includeBlank="-- Select Category --", label=false, class="form-control" )#
#submitTag(value=resource.isNew() ? "Create Resource" : "Update Resource", class="btn btn-primary")# #linkTo(text="Cancel", action="index", class="btn")#
#endFormTag()#
``` ## Layout Template ```cfm #contentFor("pageTitle")# - My App #styleSheetLinkTag("application")# #csrfMetaTags()#
#flash("success")#
#flash("error")#
#flash("notice")#
#includeContent()#

© #year(now())# My App. All rights reserved.

#javaScriptIncludeTag("application")# ``` ## Partial Template ```cfm

#linkTo(text=resource.title, action="show", key=resource.id)#

#resource.excerpt()#

#dateFormat(resource.createdAt, "mmm dd, yyyy")#
``` ## Form Helper Reference ### Text Inputs ```cfm #textField(objectName="user", property="name")# #textField(objectName="user", property="email", type="email")# #textField(objectName="user", property="password", type="password")# #textField(objectName="user", property="age", type="number")# #textField(objectName="user", property="website", type="url")# #textField( objectName="user", property="name", class="form-control", placeholder="Enter your name", maxlength=100, required=true )# ``` ### Textarea ```cfm #textArea(objectName="post", property="content", rows=10, cols=50)# ``` ### Select Dropdown ```cfm #select(objectName="user", property="role", options="user,admin,moderator")# #select( objectName="post", property="categoryId", options=model("Category").findAll(), valueField="id", textField="name", includeBlank="-- Select Category --" )# ``` ### Checkboxes and Radio Buttons ```cfm #checkBox(objectName="user", property="active")# #radioButton(objectName="user", property="gender", tagValue="male")# Male #radioButton(objectName="user", property="gender", tagValue="female")# Female ``` ### Date/Time Selects ```cfm #dateSelect(objectName="event", property="eventDate")# #timeSelect(objectName="event", property="eventTime")# #dateTimeSelect(objectName="event", property="eventDateTime")# ``` ## Link Helper Reference ```cfm #linkTo(text="View", action="show", key=resource.id)# #linkTo(text="Home", controller="home", action="index")# #linkTo(text="Delete", action="delete", key=resource.id, method="delete", confirm="Are you sure?")# #linkTo(text="Wheels Docs", href="https://wheels.dev", target="_blank")# #linkTo(text="Edit", action="edit", key=resource.id, class="btn btn-primary", data-turbo="false")# ``` ## Implementation Checklist When generating a view: - [ ] Use `` to declare expected variables - [ ] Use `` blocks for dynamic content - [ ] Use `.recordCount` for query counts (not ArrayLen) - [ ] Use `` for query iteration - [ ] Handle association access correctly in loops - [ ] Use textField() with type attribute (not emailField, etc.) - [ ] Display validation errors for each field - [ ] Include CSRF protection in forms (automatic with startFormTag) - [ ] Add flash message displays - [ ] Use contentFor() to set page titles - [ ] Provide empty state messages when no records ## Related Skills - **wheels-anti-pattern-detector**: Validates view code - **wheels-controller-generator**: Creates controllers that supply view data - **wheels-model-generator**: Creates models displayed in views --- **Generated by:** Wheels View Generator Skill v1.0 **Framework:** CFWheels 3.0+ **Last Updated:** 2025-10-20