اعمال و توابع قالب
در این بخش، ما قصد داریم به اعمال و توابع قالب که Go ارائه میدهد نگاه کنیم.
ما قبلاً در مورد برخی از اعمال صحبت کردهایم — {{define}}، {{template}} و {{block}} — اما سه مورد دیگر وجود دارد که میتوانید برای کنترل نمایش دادههای پویا استفاده کنید — {{if}}، {{with}} و {{range}}.
| Action | Description |
|---|---|
{{if .Foo}} C1 {{else}} C2 {{end}} |
اگر .Foo خالی نباشد، محتوای C1 را رندر کنید، در غیر این صورت محتوای C2 را رندر کنید. |
{{with .Foo}} C1 {{else}} C2 {{end}} |
اگر .Foo خالی نباشد، سپس dot را به مقدار .Foo تنظیم کنید و محتوای C1 را رندر کنید، در غیر این صورت محتوای C2 را رندر کنید. |
{{range .Foo}} C1 {{else}} C2 {{end}} |
اگر طول .Foo بزرگتر از صفر باشد، سپس روی هر عنصر حلقه بزنید، dot را به مقدار هر عنصر تنظیم کنید و محتوای C1 را رندر کنید. اگر طول .Foo صفر باشد، محتوای C2 را رندر کنید. نوع زیرین .Foo باید یک آرایه، slice، map یا channel باشد. |
چند نکته در مورد این اعمال برای اشاره وجود دارد:
برای هر سه عمل، بند
{{else}}اختیاری است. به عنوان مثال، میتوانید{{if .Foo}} C1 {{end}}بنویسید اگر محتوایC2وجود ندارد که میخواهید رندر کنید.مقادیر خالی false، 0، هر اشارهگر nil یا مقدار رابط، و هر آرایه، slice، map یا رشته با طول صفر هستند.
مهم است که درک کنید که اعمال
withوrangeمقدار dot را تغییر میدهند. وقتی شروع به استفاده از آنها میکنید، آنچه dot نشان میدهد میتواند بسته به اینکه در کجای قالب هستید و چه کاری انجام میدهید متفاوت باشد.
پکیج html/template همچنین برخی توابع قالب ارائه میدهد که میتوانید از آنها برای افزودن منطق اضافی به قالبهای خود و کنترل آنچه در زمان اجرا (runtime) رندر میشود استفاده کنید. میتوانید یک فهرست کامل از توابع را اینجا پیدا کنید، اما مهمترین آنها عبارتند از:
| Function | Description |
|---|---|
{{eq .Foo .Bar}} |
true را yield میکند اگر .Foo برابر با .Bar باشد |
{{ne .Foo .Bar}} |
true را yield میکند اگر .Foo برابر با .Bar نباشد |
{{not .Foo}} |
نفی بولی (boolean negation) .Foo را yield میکند |
{{or .Foo .Bar}} |
.Foo را yield میکند اگر .Foo خالی نباشد؛ در غیر این صورت .Bar را yield میکند |
{{index .Foo i}} |
مقدار .Foo در ایندکس i را yield میکند. نوع زیرین .Foo باید یک map، slice یا آرایه باشد، و i باید یک مقدار عدد صحیح باشد. |
{{printf "%s-%s" .Foo .Bar}} |
یک رشته فرمت شده حاوی مقادیر .Foo و .Bar را yield میکند. به همان روش fmt.Sprintf() کار میکند. |
{{len .Foo}} |
طول .Foo را به عنوان یک عدد صحیح yield میکند. |
{{$bar := len .Foo}} |
طول .Foo را به متغیر قالب $bar اختصاص میدهد |
ردیف آخر مثالی از اعلان یک متغیر قالب است. متغیرهای قالب به خصوص مفید هستند اگر میخواهید نتیجه از یک تابع را ذخیره کنید و در چندین مکان در قالب خود استفاده کنید. نامهای متغیر باید با علامت دلار پیشوند شوند و فقط میتوانند شامل کاراکترهای الفبایی-عددی باشند.
استفاده از عمل with
یک فرصت خوب برای استفاده از عمل (action) {{with}} فایل view.tmpl است که در فصل قبل ایجاد کردیم. ادامه دهید و آن را مانند این بهروزرسانی کنید:
{{define "title"}}Snippet #{{.Snippet.ID}}{{end}}
{{define "main"}}
{{with .Snippet}}
<div class='snippet'>
<div class='metadata'>
<strong>{{.Title}}</strong>
<span>#{{.ID}}</span>
</div>
<pre><code>{{.Content}}</code></pre>
<div class='metadata'>
<time>Created: {{.Created}}</time>
<time>Expires: {{.Expires}}</time>
</div>
</div>
{{end}}
{{end}}
پس اکنون، بین {{with .Snippet}} و تگ مربوطه {{end}}، مقدار dot به .Snippet تنظیم میشود. Dot اساساً به ساختار models.Snippet به جای ساختار والد templateData تبدیل میشود.
استفاده از اعمال if و range
بیایید همچنین از اعمال {{if}} و {{range}} در یک مثال ملموس استفاده کنیم و صفحه اصلی خود را برای نمایش یک جدول از آخرین snippetها بهروزرسانی کنیم، کمی شبیه این:

ابتدا ساختار templateData را بهروزرسانی کنید تا یک فیلد Snippets برای نگهداری یک slice از snippetها داشته باشد، مانند این:
package main import "snippetbox.alexedwards.net/internal/models" // Include a Snippets field in the templateData struct. type templateData struct { Snippet models.Snippet Snippets []models.Snippet }
سپس تابع handler home را بهروزرسانی کنید تا آخرین snippetها را از مدل پایگاه داده ما واکشی کند و آنها را به قالب home.tmpl ارسال کند:
package main ... func (app *application) home(w http.ResponseWriter, r *http.Request) { w.Header().Add("Server", "Go") snippets, err := app.snippets.Latest() if err != nil { app.serverError(w, r, err) return } files := []string{ "./ui/html/base.tmpl", "./ui/html/partials/nav.tmpl", "./ui/html/pages/home.tmpl", } ts, err := template.ParseFiles(files...) if err != nil { app.serverError(w, r, err) return } // Create an instance of a templateData struct holding the slice of // snippets. data := templateData{ Snippets: snippets, } // Pass in the templateData struct when executing the template. err = ts.ExecuteTemplate(w, "base", data) if err != nil { app.serverError(w, r, err) } } ...
حالا بیایید به فایل ui/html/pages/home.tmpl برویم و آن را برای نمایش این snippetها در یک جدول با استفاده از اعمال {{if}} و {{range}} بهروزرسانی کنیم. به طور خاص:
میخواهیم از عمل
{{if}}برای بررسی اینکه آیا slice از snippetها خالی است یا نه استفاده کنیم. اگر خالی است، میخواهیم یک پیام"There's nothing to see here yet!نمایش دهیم. در غیر این صورت، میخواهیم یک جدول حاوی اطلاعات snippet را رندر کنیم.میخواهیم از عمل
{{range}}برای تکرار روی همه snippetها در slice استفاده کنیم، محتوای هر snippet را در یک ردیف جدول رندر کنیم.
این نشانهگذاری است:
{{define "title"}}Home{{end}}
{{define "main"}}
<h2>Latest Snippets</h2>
{{if .Snippets}}
<table>
<tr>
<th>Title</th>
<th>Created</th>
<th>ID</th>
</tr>
{{range .Snippets}}
<tr>
<td><a href='/snippet/view/{{.ID}}'>{{.Title}}</a></td>
<td>{{.Created}}</td>
<td>#{{.ID}}</td>
</tr>
{{end}}
</table>
{{else}}
<p>There's nothing to see here... yet!</p>
{{end}}
{{end}}
مطمئن شوید که همه فایلهای شما ذخیره شدهاند، برنامه را مجدداً راهاندازی کنید و به http://localhost:4000 در یک مرورگر وب بروید. اگر همه چیز طبق برنامه پیش رفته باشد، باید صفحهای ببینید که کمی شبیه این است:
اطلاعات اضافی
ترکیب توابع
ترکیب چندین تابع در تگهای قالب شما امکانپذیر است، با استفاده از پرانتز () برای احاطه کردن توابع و آرگومانهای آنها در صورت نیاز.
به عنوان مثال، تگ زیر محتوای C1 را رندر میکند اگر طول Foo بزرگتر از 99 باشد:
{{if (gt (len .Foo) 99)}} C1 {{end}}
یا به عنوان مثال دیگر، تگ زیر محتوای C1 را رندر میکند اگر .Foo برابر با 1 باشد و .Bar کمتر یا مساوی 20 باشد:
{{if (and (eq .Foo 1) (le .Bar 20))}} C1 {{end}}
کنترل رفتار حلقه
درون یک عمل {{range}} میتوانید از دستور {{break}} برای پایان زودهنگام حلقه، و {{continue}} برای شروع فوری تکرار بعدی حلقه استفاده کنید.
{{range .Foo}} // Skip this iteration if the .ID value equals 99. {{if eq .ID 99}} {{continue}} {{end}} // ... {{end}}
{{range .Foo}} // End the loop if the .ID value equals 99. {{if eq .ID 99}} {{break}} {{end}} // ... {{end}}