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.
Activate automatically when:
Wheels associations return QUERIES, not arrays!
WRONG:
<cfset count = ArrayLen(post.comments())> ❌
<cfloop array="#comments#" index="comment"> ❌
CORRECT:
<cfset count = post.comments().recordCount> ✅
<cfloop query="comments"> ✅
WRONG:
<cfloop query="posts">
<p>#posts.comments().recordCount# comments</p> ❌ Fails!
</cfloop>
CORRECT:
<cfloop query="posts">
<cfset postComments = model("Post").findByKey(posts.id).comments()>
<p>#postComments.recordCount# comments</p> ✅ Works!
</cfloop>
Wheels doesn't have these helpers:
#emailField(...)# ❌ Doesn't exist
#passwordField(...)# ❌ Doesn't exist
#numberField(...)# ❌ Doesn't exist
Use textField() with type attribute:
#textField(objectName="user", property="email", type="email")# ✅
#textField(objectName="user", property="password", type="password")# ✅
#textField(objectName="user", property="age", type="number")# ✅
❌ WRONG - Associations Don't Work on Query Columns:
<cfloop query="tweets">
#tweets.user()# ❌ FAILS - tweets is a query, not an object
#tweets.likesCount()# ❌ FAILS - no such method on query
</cfloop>
✅ CORRECT - Load Object First:
<cfloop query="tweets">
<cfset tweetObj = model("Tweet").findByKey(tweets.id)>
<cfset tweetUser = tweetObj.user()> ✅ Works!
<p>By: #tweetUser.username#</p>
<p>Likes: #tweetObj.likesCount#</p>
</cfloop>
✅ BETTER - Preload with include (Prevents N+1):
<!--- In Controller --->
tweets = model("Tweet").findAll(include="user", order="createdAt DESC");
<!--- In View --->
<cfloop query="tweets">
<!--- User data already joined, no extra queries --->
<p>By: #tweets.username#</p>
<p>Tweet: #tweets.content#</p>
</cfloop>
<!--- ✅ CORRECT --->
<cfif tweets.recordCount>
<cfloop query="tweets">...</cfloop>
</cfif>
<!--- ❌ WRONG - queries are not arrays --->
<cfif ArrayLen(tweets)> ❌ Error!
Direct column access (if eager loaded):
tweets = model("Tweet").findAll(select="id,content,likesCount");
<cfloop query="tweets">
<p>#tweets.likesCount# likes</p> ✅ Direct access
</cfloop>
Association count (if not loaded):
<cfloop query="tweets">
<cfset tweet = model("Tweet").findByKey(tweets.id)>
<cfset likeCount = tweet.likes(returnAs="count")> ✅ Count association
<p>#likeCount# likes</p>
</cfloop>
<!--- ✅ CORRECT --->
<cfif user.active>
<cfif structKeyExists(user, "bio") && len(user.bio)>
<!--- ❌ WRONG --->
<cfif user.active == true> // Redundant
<cfif user.bio> // Fails if bio doesn't exist
<!--- ✅ CORRECT - Use CFML functions --->
#dateFormat(tweet.createdAt, "mmm dd, yyyy")#
#timeFormat(tweet.createdAt, "h:mm tt")#
<!--- Use custom model methods for consistency --->
#tweet.timeAgo()# // "5m", "2h", "3d"
<!--- ✅ CORRECT - Wheels returns queries --->
<cfloop query="tweets">
#tweets.content#
</cfloop>
<!--- ❌ WRONG - Not an array! --->
<cfloop array="#tweets#" index="tweet"> ❌ Error!
<cfparam name="resources">
<cfoutput>
#contentFor(pageTitle="Resources")#
<div class="container">
<div class="header">
<h1>Resources</h1>
<div class="actions">
#linkTo(text="New Resource", action="new", class="btn btn-primary")#
</div>
</div>
<cfif resources.recordCount>
<div class="resource-grid">
<cfloop query="resources">
<article class="resource-card">
<h2>
#linkTo(text=resources.title, action="show", key=resources.id)#
</h2>
<!--- Display excerpt or description --->
<p>#left(resources.description, 200)#...</p>
<!--- Display associated data (CORRECT pattern) --->
<cfset resourceAssoc = model("Resource").findByKey(resources.id).association()>
<div class="meta">
<span>#resourceAssoc.recordCount# items</span>
<span>#dateFormat(resources.createdAt, "mmm dd, yyyy")#</span>
</div>
<div class="actions">
#linkTo(text="View", action="show", key=resources.id, class="btn btn-sm")#
#linkTo(text="Edit", action="edit", key=resources.id, class="btn btn-sm")#
</div>
</article>
</cfloop>
</div>
<!--- Pagination if needed --->
#paginationLinks(prependToLink="page=")#
<cfelse>
<div class="empty-state">
<p>No resources found.</p>
#linkTo(text="Create First Resource", action="new", class="btn btn-primary")#
</div>
</cfif>
</div>
</cfoutput>
<cfparam name="resource">
<cfparam name="associations">
<cfoutput>
#contentFor(pageTitle=resource.title)#
<div class="container">
<div class="header">
<h1>#resource.title#</h1>
<div class="actions">
#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")#
</div>
</div>
<div class="resource-content">
<!--- Display full content --->
<div class="description">
#resource.description#
</div>
<!--- Display metadata --->
<div class="metadata">
<p>
<strong>Created:</strong>
#dateFormat(resource.createdAt, "mmmm dd, yyyy")#
at #timeFormat(resource.createdAt, "h:mm tt")#
</p>
<cfif structKeyExists(resource, "updatedAt")>
<p>
<strong>Last Updated:</strong>
#dateFormat(resource.updatedAt, "mmmm dd, yyyy")#
</p>
</cfif>
</div>
</div>
<!--- Display associated records --->
<div class="associations-section">
<h2>Associated Items (#associations.recordCount#)</h2>
<cfif associations.recordCount>
<ul class="associations-list">
<cfloop query="associations">
<li>
#associations.name#
<small>#dateFormat(associations.createdAt, "mmm dd")#</small>
</li>
</cfloop>
</ul>
<cfelse>
<p>No associated items.</p>
</cfif>
</div>
</div>
</cfoutput>
<cfparam name="resource">
<cfoutput>
#contentFor(pageTitle=resource.isNew() ? "New Resource" : "Edit Resource")#
<div class="container">
<h1>#resource.isNew() ? "Create" : "Edit"# Resource</h1>
<!--- Display form errors if any --->
<cfif resource.hasErrors()>
<div class="alert alert-error">
<p><strong>Please correct the following errors:</strong></p>
<ul>
<cfloop collection="#resource.allErrors()#" item="propertyName">
<cfloop array="#resource.allErrors(propertyName)#" index="errorMessage">
<li>#errorMessage#</li>
</cfloop>
</cfloop>
</ul>
</div>
</cfif>
#startFormTag(action=resource.isNew() ? "create" : "update", method=resource.isNew() ? "post" : "patch")#
<!--- Text input with error display --->
<div class="form-group #resource.hasErrors('title') ? 'has-error' : ''#">
<label for="resource-title">Title *</label>
#textField(objectName="resource", property="title", label=false, class="form-control")#
<cfif resource.hasErrors("title")>
<span class="error-message">#resource.allErrors("title")[1]#</span>
</cfif>
</div>
<!--- Textarea with error display --->
<div class="form-group #resource.hasErrors('description') ? 'has-error' : ''#">
<label for="resource-description">Description</label>
#textArea(objectName="resource", property="description", label=false, rows=6, class="form-control")#
<cfif resource.hasErrors("description")>
<span class="error-message">#resource.allErrors("description")[1]#</span>
</cfif>
</div>
<!--- Email field (use textField with type) --->
<div class="form-group #resource.hasErrors('email') ? 'has-error' : ''#">
<label for="resource-email">Email *</label>
#textField(objectName="resource", property="email", type="email", label=false, class="form-control")#
<cfif resource.hasErrors("email")>
<span class="error-message">#resource.allErrors("email")[1]#</span>
</cfif>
</div>
<!--- Number field --->
<div class="form-group">
<label for="resource-price">Price</label>
#textField(objectName="resource", property="price", type="number", step="0.01", label=false, class="form-control")#
</div>
<!--- Date field --->
<div class="form-group">
<label for="resource-publishedDate">Published Date</label>
#dateSelect(objectName="resource", property="publishedDate", label=false, class="form-control")#
</div>
<!--- Select dropdown --->
<div class="form-group">
<label for="resource-status">Status</label>
#select(
objectName="resource",
property="status",
options="draft,published,archived",
label=false,
class="form-control"
)#
</div>
<!--- Checkbox --->
<div class="form-group">
<label class="checkbox">
#checkBox(objectName="resource", property="active", label=false)#
<span>Active</span>
</label>
</div>
<!--- Association select (belongs to) --->
<div class="form-group">
<label for="resource-categoryId">Category</label>
#select(
objectName="resource",
property="categoryId",
options=model("Category").findAll(),
valueField="id",
textField="name",
includeBlank="-- Select Category --",
label=false,
class="form-control"
)#
</div>
<!--- Form actions --->
<div class="form-actions">
#submitTag(value=resource.isNew() ? "Create Resource" : "Update Resource", class="btn btn-primary")#
#linkTo(text="Cancel", action="index", class="btn")#
</div>
#endFormTag()#
</div>
</cfoutput>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>#contentFor("pageTitle")# - My App</title>
<!--- CSS --->
#styleSheetLinkTag("application")#
<!--- CSRF meta tags --->
#csrfMetaTags()#
</head>
<body>
<!--- Navigation --->
<nav class="navbar">
<div class="container">
#linkTo(text="Home", controller="home", action="index", class="logo")#
<ul class="nav-menu">
<li>#linkTo(text="Resources", controller="resources", action="index")#</li>
<cfif structKeyExists(session, "userId")>
<li>#linkTo(text="Profile", controller="users", action="show", key=session.userId)#</li>
<li>#linkTo(text="Logout", controller="sessions", action="delete", method="delete")#</li>
<cfelse>
<li>#linkTo(text="Login", controller="sessions", action="new")#</li>
<li>#linkTo(text="Sign Up", controller="users", action="new")#</li>
</cfif>
</ul>
</div>
</nav>
<!--- Flash messages --->
<cfif flashKeyExists("success")>
<div class="alert alert-success">
#flash("success")#
</div>
</cfif>
<cfif flashKeyExists("error")>
<div class="alert alert-error">
#flash("error")#
</div>
</cfif>
<cfif flashKeyExists("notice")>
<div class="alert alert-notice">
#flash("notice")#
</div>
</cfif>
<!--- Main content --->
<main>
#includeContent()#
</main>
<!--- Footer --->
<footer>
<div class="container">
<p>© #year(now())# My App. All rights reserved.</p>
</div>
</footer>
<!--- JavaScript --->
#javaScriptIncludeTag("application")#
</body>
</html>
<!--- File: views/resources/_resource.cfm --->
<cfparam name="resource">
<cfoutput>
<div class="resource-item">
<h3>#linkTo(text=resource.title, action="show", key=resource.id)#</h3>
<p>#resource.excerpt()#</p>
<div class="meta">
<span>#dateFormat(resource.createdAt, "mmm dd, yyyy")#</span>
</div>
</div>
</cfoutput>
<!--- Usage in parent view: --->
<!--- #includePartial(partial="resource", query=resources)# --->
<!--- Basic text field --->
#textField(objectName="user", property="name")#
<!--- With type attribute --->
#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")#
<!--- With attributes --->
#textField(
objectName="user",
property="name",
class="form-control",
placeholder="Enter your name",
maxlength=100,
required=true
)#
#textArea(objectName="post", property="content", rows=10, cols=50)#
<!--- Simple options --->
#select(objectName="user", property="role", options="user,admin,moderator")#
<!--- From query --->
#select(
objectName="post",
property="categoryId",
options=model("Category").findAll(),
valueField="id",
textField="name",
includeBlank="-- Select Category --"
)#
<!--- Single checkbox --->
#checkBox(objectName="user", property="active")#
<!--- Radio buttons --->
#radioButton(objectName="user", property="gender", tagValue="male")# Male
#radioButton(objectName="user", property="gender", tagValue="female")# Female
<!--- Date select --->
#dateSelect(objectName="event", property="eventDate")#
<!--- Time select --->
#timeSelect(objectName="event", property="eventTime")#
<!--- Date and time --->
#dateTimeSelect(objectName="event", property="eventDateTime")#
<!--- Link to action in same controller --->
#linkTo(text="View", action="show", key=resource.id)#
<!--- Link to different controller --->
#linkTo(text="Home", controller="home", action="index")#
<!--- Link with method (for RESTful routes) --->
#linkTo(text="Delete", action="delete", key=resource.id, method="delete", confirm="Are you sure?")#
<!--- External link --->
#linkTo(text="Wheels Docs", href="https://wheels.dev", target="_blank")#
<!--- Link with custom attributes --->
#linkTo(text="Edit", action="edit", key=resource.id, class="btn btn-primary", data-turbo="false")#
When generating a view:
<cfparam> to declare expected variables<cfoutput> blocks for dynamic content.recordCount for query counts (not ArrayLen)<cfloop query=""> for query iterationGenerated by: Wheels View Generator Skill v1.0 Framework: CFWheels 3.0+ Last Updated: 2025-10-20
npx skills add wheels-dev/Wheels View Generator下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer