• 13-Javascript Magic
    • 服务器端支持
    • 客户端Ajax
    • Links

    13-Javascript Magic

    在本章,我们将添加一个功能,当你将鼠标悬停在用户的昵称上时,会弹出一个漂亮的窗口。

    社交网站的常见用户交互模式是,当你将鼠标悬停在用户的名称上时,可以在弹出窗口中显示用户的主要信息。 如果你从未注意到这一点,请访问Twitter,Facebook,LinkedIn或任何其他主要社交网站,当你看到用户名时,只需将鼠标指针放在上面几秒钟即可看到弹出窗口。

    本章的GitHub链接为: Source, Diff, Zip

    服务器端支持

    在深入研究客户端之前,让我们先了解一下支持这些用户弹窗所需的服务器端的工作。 用户弹窗的内容将由新路由返回,它是现有个人主页路由的简化版本。

    viewmodel 我们偷下懒,由于Popup的 vm 和 Profile 的相似,我们直接在 vm/profile.go中加入 GetPopupVM 来获得 Popup 的 vm

    vm/profile.go

    1. ...
    2. // GetPopupVM func
    3. func (ProfileViewModelOp) GetPopupVM(sUser, pUser string) (ProfileViewModel, error) {
    4. v := ProfileViewModel{}
    5. v.SetTitle("Profile")
    6. u, err := model.GetUserByUsername(pUser)
    7. if err != nil {
    8. return v, err
    9. }
    10. v.ProfileUser = *u
    11. v.Editable = (sUser == pUser)
    12. if !v.Editable {
    13. v.IsFollow = u.IsFollowedByUser(sUser)
    14. }
    15. v.FollowersCount = u.FollowersCount()
    16. v.FollowingCount = u.FollowingCount()
    17. v.SetCurrentUser(sUser)
    18. return v, nil
    19. }
    20. ...

    controller/home.go

    1. ...
    2. r.HandleFunc("/user/{username}/popup", popupHandler)
    3. ...
    4. func popupHandler(w http.ResponseWriter, r *http.Request) {
    5. tpName := "popup.html"
    6. vars := mux.Vars(r)
    7. pUser := vars["username"]
    8. sUser, _ := getSessionUser(r)
    9. vop := vm.ProfileViewModelOp{}
    10. v, err := vop.GetPopupVM(sUser, pUser)
    11. if err != nil {
    12. msg := fmt.Sprintf("user ( %s ) does not exist", pUser)
    13. w.Write([]byte(msg))
    14. return
    15. }
    16. templates[tpName].Execute(w, &v)
    17. }
    18. ...

    templates/content/popup.html

    1. <table>
    2. <tr valign="top">
    3. <td width="64" style="border: 0px;"><img src="{{.ProfileUser.Avatar}}&s=64"></td>
    4. <td style="border: 0px;">
    5. <small>
    6. <p><a href="/user/{{.ProfileUser.Username}}">{{.ProfileUser.Username}}</a></p>
    7. {{if .ProfileUser.AboutMe}}
    8. <p>{{ .ProfileUser.AboutMe }}</p>
    9. {{end}}
    10. {{if .ProfileUser.LastSeen}}
    11. <p>Last seen on: {{ .ProfileUser.FormattedLastSeen }}</p>
    12. {{end}}
    13. <p>{{ .FollowersCount }} followers, {{ .FollowingCount }} following.</p>
    14. {{if .Editable}}
    15. <p><a href="/profile_edit">Edit your profile</a></p>
    16. {{else}}
    17. {{if .IsFollow}}
    18. <p><a class="btn btn-outline-primary" href="/unfollow/{{.ProfileUser.Username}}">Unfollow</a></p>
    19. {{else}}
    20. <p><a class="btn btn-outline-primary" href="/follow/{{.ProfileUser.Username}}">Follow</a></p>
    21. {{end}}
    22. {{end}}
    23. </small>
    24. </td>
    25. </tr>
    26. </table>

    当用户将鼠标指针悬停在用户名上时,随后小节中编写的JavaScript代码将会调用此路由。客户端将服务器端返回的响应中的html内容显示在弹出窗口中。 当用户移开鼠标时,弹出窗口将被删除。 听起来很简单,对吧?

    如果你想了解弹窗像什么样,现在可以运行应用,跳转到任何用户的个人主页,然后在地址栏的URL中追加/popup以查看全屏版本的弹出窗口内容。

    本小节 Diff

    客户端Ajax

    我们在 _base.html 中加入 popup 的 Ajax,这样所有继承它的页面也同样继承了 popup的功能

    templates/_base.html

    1. ...
    2. <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
    3. <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
    4. <script>
    5. $(function () {
    6. var timer = null;
    7. var xhr = null;
    8. $('.user_popup').hover(
    9. function(event) {
    10. // mouse in event handler
    11. var elem = $(event.currentTarget);
    12. timer = setTimeout(function() {
    13. timer = null;
    14. xhr = $.ajax(
    15. '/user/' + elem.first().text().trim() + '/popup').done(
    16. function(data) {
    17. xhr = null;
    18. elem.popover({
    19. trigger: 'manual',
    20. html: true,
    21. animation: false,
    22. container: elem,
    23. content: data
    24. }).popover('show');
    25. }
    26. );
    27. }, 1000);
    28. },
    29. function(event) {
    30. // mouse out event handler
    31. var elem = $(event.currentTarget);
    32. if (timer) {
    33. clearTimeout(timer);
    34. timer = null;
    35. }
    36. else if (xhr) {
    37. xhr.abort();
    38. xhr = null;
    39. }
    40. else {
    41. elem.popover('hide');
    42. }
    43. }
    44. );
    45. });
    46. </script>
    47. ...

    然后我们在需要有 Popup 功能的地方,就是所有的用户Post的头像地方加入 class='user_popup'

    templates/content/index.html & explore.html & profile.html

    1. ...
    2. <td><span class="user_popup"><a href="/user/{{.User.Username}}">{{ .User.Username }}</a></span> said {{.FormattedTimeAgo}}:<br>{{ .Body }}</td>
    3. ...

    13-01

    本小节 Diff

    • 目录
    • 上一节: 12-Dates-And-Times
    • 下一节: 14-Deployment-On-Heroku