How to manipulate DOM from template to add/remove attribute

In my code, I want to disable some inputs fields based on the user authentication level, I was able to do it in JavaScript, but this is unrecommended solution, and I want to make it from the server side.

Below is my go code;

package main

import (
	"context"
	"html/template"
	"net/http"
	"strings"
	"time"
)

type User struct {
	Flags string
	Title string
}

type UsersPageData struct {
	PageTitle string
	Users     []User
}

func requestTime(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		ctx = context.WithValue(ctx, "requestTime", time.Now().Format(time.RFC3339))
		r = r.WithContext(ctx)
		next.ServeHTTP(w, r)
	})
}

func helloHandler(name string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		var title string
		if requestTime := r.Context().Value("requestTime"); requestTime != nil {
			if str, ok := requestTime.(string); ok {
				title = "\ngenerated at: " + str
			}
		}

		master := strings.Join([]string{"admin", "user", "superuser", "read"}, " ")
		admin := strings.Join([]string{"admin"}, " ")
		user := strings.Join([]string{"user"}, " ")

		tmpl := template.Must(template.ParseFiles("index.html"))
		data := UsersPageData{
			PageTitle: "Users list: " + title,
			Users: []User{
				{Flags: master, Title: "Everything"},
				{Flags: admin, Title: "Administrator"},
				{Flags: user, Title: "Normal user"},
			},
		}
		tmpl.Execute(w, data)
	})
}

func main() {
	http.Handle("/john", requestTime(helloHandler("John")))
	http.ListenAndServe(":8080", nil)
}

And here is my template including the JS code;

<style>
.admin {
    color: green;
}

.user {
    color: red;
    --btn-disable: 0;
}


[data-authorized="no"] { 
  /* Attribute has this exact value */
        cursor: not-allowed;
        pointer-events: none;

        /*Button disabled - CSS color class*/
        color: #c0c0c0;
        background-color: rgb(229, 229, 229) !important;
}

</style>
<h1>{{.PageTitle}}</h1>
<ul>
    {{range .Users}}
            <input type="text" data-permissions={{.Flags}} data-authorized="no">{{.Title}}</s>
    {{end}}
</ul>

<script>
  var flags = ["admin", "super user"]
  var elements = document.querySelectorAll("input");
  elements.forEach((element, index, array) => { 
        if(element.hasAttribute("data-permissions")){
            console.log(element.dataset.permissions)
            var perm = element.dataset.permissions.split(" ");
                    var found = false;
                    for (var i = 0; i < perm.length; i++) {
                        if (flags.indexOf(perm[i]) > -1) {
                            element.dataset.authorized = "yes"
                            element.removeAttribute("data-permissions")
                            break;
                        }
                    } 
        }
  });
</script>

And here is the output:
enter image description here

Any thought?

You just need to send different HTML code from the app if you have an authorized user.

Did not get it, can you elaborate more with some code, thanks

One way is to hide / show elements in the go template

{{if and .User .User.Admin}}
  You are an admin user!
{{else}}
  Access denied!
{{end}}

@Sibert is right. @hyousef you can use logical differentiation in views or the same approach in Go code (just render another template).

This is the point, I’m looking for using the same template.

Thanks, but this means using different templates, actually I’ve 2 cases of authentication:

  1. Different view for each authority, and this can be controlled from GO with the code you supplied.
  2. Single view with some fields disabled for some authority level while opened for another authority level, and this is what I’m trying to do.

You can use ONE template with built in conditions.

<form>
{{if .SuperAdmin}}
  <input type="text" name="subject" placeholder="For Superadmin" required>
{{else if .Admin}}
  <input type="text" name="subject" placeholder="For Admin" required>
{{else}}
  <input type="text" name="subject" placeholder="For Normal Users" required>
{{end}}
//outside if statement:
<input type="text" name="subject" placeholder="For all users" required>
</form>

It worked with me if each user have single flag, like:

type Flag int

const (
	Admin Flag = iota + 1 // iota = 0
	Editer
	Superuser
	Viewer
)

type User struct {
	Flags Flag
	Title string
}

And template like:

<h1>{{.PageTitle}}</h1>
<ul>
    {{range .Users}}
            <span>{{.Title}}</span>
            {{if eq .Flags 1}}
                 <input type="text" name="subject" placeholder= {{.Flags}} required>
                 {{else if eq .Flags 4}}
                 <input type="text" name="subject" placeholder= {{.Flags}} disabled>
            {{end}}
    {{end}}
</ul>

But what if I’ve the Flags as array, where:

  1. Each user could have multiple flags
  2. If for the same user one flag having permission to the element, and another flag does not allow this permission, then how can I open the permission for the hire one.

An example is:

type User struct {
	Flags []Flag
	Title string
}

master := User{
 Flags: []string{Admin, Viewer},
Title: "Abnormal user",
}

In the above example, the user has 2 flags, Admin that granted the access of the element, and Viewer that blocked the access of the same element, how can I manage this?

UPDATE
After search, I tried the below, but still did not solve it, if I have multiple flags for the same user, and none of them having permissions, then it will repeat the same field multiple times;

		master := []flag{Admin, Editer, Superuser, Viewer}
		admin := []flag{Admin, Superuser}
		user := []flag{Viewer, Dummy}

// template
<h1>{{.PageTitle}}</h1>
<ul>
    {{range .Users}}
        <span>{{.Title}}</span>
        {{ $done := false }}
        {{range $i, $v := .Flags}}
            {{ if $done }}
            {{ else }}
                {{if or (eq $v 1) (eq $v 3)}} 
                    <input type="text" name="subject" placeholder= {{$v}} required>
                    {{ $done = true }}
                {{else}}
                     <input type="text" name="subject" placeholder= {{$v}} disabled>
                {{end}}
            {{end}}
        {{end}}
    {{end}}
</ul>

Thanks

This is not Go question as I see it. It is a logical issue. I is like poker. Admin wins the game having the best cards! :slight_smile:

But I think the operator “and” is another option, when you solved the logic. As I understad, it replaces a switch statement sort of.

Thanks, but the and working is not correct.
I was able to do it, with the help of template custom functions, below the full code:

package main

import (
	"html/template"
	"net/http"
)

type User struct {
	Flags []flag //string
	Title string
}

type UsersPageData struct {
	PageTitle string
	Users     []User
}

type flag int

const (
	Admin flag = iota + 1 // iota = 0
	Editer
	Superuser
	Viewer
	Dummy
)

func subtract(arg1, arg2 int) int {
	return arg1 - arg2
}

func helloHandler(name string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Map the `subtract` custom function in the template
		funcMap := map[string]interface{}{"subtract": subtract}

		master := []flag{Admin, Editer, Superuser, Viewer}
		admin := []flag{Admin, Superuser, Viewer}
		user := []flag{Viewer, Dummy}

		tmpl := template.New("").Funcs(template.FuncMap(funcMap))
		template.Must(tmpl.ParseFiles("index.html"))
		data := UsersPageData{
			PageTitle: "Users list: ",
			Users: []User{
				{Flags: master, Title: "Everything"},
				{Flags: admin, Title: "Administrator"},
				{Flags: user, Title: "Normal user"},
			},
		}
		tmpl.ExecuteTemplate(w, "index.html", data)
	})
}

func main() {
	fs := http.StripPrefix("/www/", http.FileServer(http.Dir("./www")))
	http.Handle("/www/", fs)

	http.Handle("/", helloHandler("John"))
	http.ListenAndServe(":8080", nil)
}

The index.html is:

<html>
{{/* This is a comment 
{{$flags := []flag{Admin, Editer, Superuser, Viewer};}}	
    Admin Flag = iota + 1 // iota = 0
	Editer
	Superuser
	Viewer
    }}
*/}}

<ul>
    {{range .Users}}
        <span>{{.Title}}</span>
        {{ $done := false}} {{$length := len .Flags}}
        {{range $i, $v := .Flags}}
            {{ if $done }}
            {{ else }}
                {{if or (eq $v 1) (eq $v 3)}} 
                    <input type="text" name="subject" placeholder= {{$v}} required>
                    {{ $done = true }}
                {{else}}
                    {{ if eq $i (subtract $length 1)}}
                        <input type="text" name="subject" placeholder= {{$v}} disabled>
                    {{ end }}
                {{end}}
            {{end}}
        {{end}}
    {{end}}
</ul>
</html>

enter image description here

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.