简体   繁体   English

Polymer SPA的登录页面

[英]Login page for a SPA in Polymer

I'm fairly new at Polymer and have used the Polymer 2.0 Starter Kit template for all of my apps. 我是Polymer的新手,已经为我的所有应用程序使用了Polymer 2.0 Starter Kit模板。 The majority of my apps are very similar - a login page that redirects you to different portals based on a user role (usually admin or user). 我的大多数应用程序都非常相似-登录页面可根据用户角色(通常是admin或user)将您重定向到其他门户。 The portals have an app-drawer with different views setup via iron-pages . 门户网站具有一个app-drawer ,可通过iron-pages设置不同的视图。

My current flow duplicates the my-app template within each 'portal'. 我当前的流程在每个“门户”中复制了my-app模板。 I'm just uncertain if this is the correct way of doing this. 我只是不确定这是否是正确的方法。 I'll explain further. 我会进一步解释。

Using my-app as is from the starter kit with iron-pages to load the login-view , admin-portal , and user-portal, defaulting to login-view . 使用入门工具包中的my-app并带有铁页面来加载login-viewadmin-portal和user-portal,默认为login-view

After a user has logged in, the application will load admin-portal or user-portal depending on which user has logged in. 用户登录后,应用程序将根据登录的user-portal来加载admin-portaluser-portal

Both admin-portal and user-portal have a structure very similar to my-app, with an app-drawer and then iron-pages loading views specific to admin capabilities or user capabilities. admin-portaluser-portal的结构都非常类似于my-app,具有app-drawer ,然后iron-pages加载特定于管理功能或用户功能的视图。 I was mainly wondering if this is best practice - as you always have a nested iron-pages . 我主要是想知道这是否是最佳做法-因为您总是有嵌套的iron-pages Is there away to abstract the login-view ? 是否有抽象login-view

Code: 码:

my-app - out of the box template my-app-开箱即用的模板

<app-location
    route="{{route}}"
    url-space-regex="^[[rootPath]]">
</app-location>

<app-route
    route="{{route}}"
    pattern="[[rootPath]]:page"
    data="{{routeData}}"
    tail="{{subroute}}">
</app-route>

<iron-pages
    selected="[[page]]"
    attr-for-selected="name"
    fallback-selection="view404"
    role="main">
  <login-view name="login" current-user="{{currentUser}}"></login-view>
  <superuser-portal name="superuser"></superuser-portal>
  <admin-portal name="admin" current-user="{{currentUser}}"></admin-portal>
  <doctor-portal name="doctor" current-user="{{currentUser}}"></doctor-portal>
  <my-view404 name="view404"></my-view404>
</iron-pages>

  _routePageChanged(page) {
    //console.log(this.user.email);
    // If no page was found in the route data, page will be an empty string.
    // Default to 'view1' in that case.
    this.page = page || 'login';
  }

  _pageChanged(page) {
    console.log('_pageChanged: ' + page);

    let resolvedPageUrl = this.resolveUrl('my-' + page + '.html');

    if(page === 'login') {
      resolvedPageUrl = this.resolveUrl('login-view.html');
    } else if(page === 'superuser') {
      resolvedPageUrl = this.resolveUrl('superuser-portal.html');
    } else if(page === 'admin') {
      resolvedPageUrl = this.resolveUrl('admin-portal.html');
    } else if(page === 'doctor') {
      resolvedPageUrl = this.resolveUrl('doctor-portal.html');
    }

    console.log('resolvedPageUrl: ' + resolvedPageUrl);

    Polymer.importHref(
        resolvedPageUrl,
        null,
        this._showPage404.bind(this),
        true);
  }

admin-portal - again, basically same as out of box template admin-portal-再次,与开箱即用的模板基本相同

    <app-route route="{{route}}" pattern="/admin/:page" data="{{routeData}}" tail="{{subroute}}">
    </app-route>

    <!-- <firebase-auth id="auth" user="{{user}}" on-error="handleError"></firebase-auth> -->

    <app-drawer-layout fullbleed narrow="{{narrow}}">
      <app-drawer id="drawer" slot="drawer" swipe-open="[[narrow]]">
        <div class="drawerHeader">
          <div class="drawerTitle">GetonHealth</div>
          <div class="drawerSubtitle">[[displayName]]</div>
        </div>
        <div style="margin-top: 24px; color: white;">
          <vaadin-list-box>
            <iron-selector attr-for-selected="name" selected="{{routeData.page}}">
              <vaadin-item name="calendar">
                <iron-icon icon="vaadin:calendar"></iron-icon>
                Calendar
              </vaadin-item>
              <vaadin-item name="doctors">
                <iron-icon icon="vaadin:specialist"></iron-icon>
                Manage Doctors
              </vaadin-item>
              <vaadin-item name="view3">
                <iron-icon icon="vaadin:cog-o"></iron-icon>
                Settings
              </vaadin-item>
              <vaadin-item name="view4">
                <iron-icon icon="vaadin:info-circle-o"></iron-icon>
                Help
              </vaadin-item>
              <vaadin-item name="logout">
                <iron-icon icon="vaadin:exit-o"></iron-icon>
                Sign Out
              </vaadin-item>
            </iron-selector>
          </vaadin-list-box>
        </div>
      </app-drawer>
      <app-header-layout>
        <app-header>
          <app-toolbar effects="waterfall">
            <paper-icon-button icon="my-icons:menu" drawer-toggle=""></paper-icon-button>
            <div class="main-title" main-title>[[pageTitle]]</div>
          </app-toolbar>
        </app-header>

        <iron-pages selected="[[page]]" attr-for-selected="name" fallback-selection="view404" role="main">
          <admin-dashboard name="dashboard"></admin-dashboard>
          <admin-manage-doctors name="doctors"></admin-manage-doctors>
          <manage-doctor-detail name="doctor" page-title="{{pageTitle}}"></manage-doctor-detail>
          <admin-calendar-view name="calendar" narrow="[[narrow]]"></admin-calendar-view>
          <admin-new-event name="newevent"></admin-new-event>
          <admin-event-detail name="event" page-title="{{pageTitle}}"></admin-event-detail>
          <admin-reschedule-view name="reschedule" page-title="{{pageTitle}}"></admin-reschedule-view>
          <admin-new-appointment name="newappointment" current-user="{{currentUser}}"></admin-new-appointment>
          <my-view404 name="view404"></my-view404>
        </iron-pages>

      </app-header-layout>
    </app-drawer-layout>

  </template>

  <script>
    class AdminPortal extends Polymer.Element {
      static get is() { return 'admin-portal'; }

      static get properties() {
        return {
          pageTitle: String,
          displayName: String,
          currentUser: {
            type: Object,
            value: {}
          },
          page: {
            type: String,
            reflectToAttribute: true,
            observer: '_pageChanged',
          },
          routeData: Object,
          subroute: Object,
        };
      }

      static get observers() {
        return [
          '_routePageChanged(routeData.page)',
        ];
      }

      _routePageChanged(page) {
        // If no page was found in the route data, page will be an empty string.
        // Default to 'view1' in that case.
        this.page = page || 'calendar';
      }

      _pageChanged(page) {
        console.log('_pageChanged: ' + page);

        let resolvedPageUrl = this.resolveUrl('my-' + page + '.html');

        if (page === 'dashboard') {
          this.set('pageTitle', 'Dashboard');
          resolvedPageUrl = this.resolveUrl('admin/views/admin-dashboard.html');
        } else if (page === 'doctors') {
          this.set('pageTitle', 'Manage doctors');
          resolvedPageUrl = this.resolveUrl('admin/views/admin-manage-doctors.html');
        } else if (page === 'doctor') {
          this.set('pageTitle', '');
          resolvedPageUrl = this.resolveUrl('admin/views/manage-doctor-detail.html');
        } else if (page === 'calendar') {
          this.set('pageTitle', 'Calendar');
          this.set('queryParams', {});
          resolvedPageUrl = this.resolveUrl('admin/views/admin-calendar-view.html');
        } else if (page === 'newevent') {
          this.set('pageTitle', 'Create new event');
          resolvedPageUrl = this.resolveUrl('admin/views/admin-new-event.html');
        } else if (page === 'event') {
          resolvedPageUrl = this.resolveUrl('admin/views/admin-event-detail-test.html');
        } else if (page === 'newappointment') {
          resolvedPageUrl = this.resolveUrl('admin/views/admin-new-appointment.html');
        } else if (page === 'reschedule') {
          resolvedPageUrl = this.resolveUrl('admin/views/admin-reschedule-view.html');
        } else if (page === 'logout') {
          this.logout();
          return;
        }

        Polymer.importHref(
          resolvedPageUrl,
          null,
          this._showPage404.bind(this),
          true);

        if (!this.$.drawer.persistent) {
          this.$.drawer.close();
        }
      }

login-view 登录视图

performLogin() {
    this.$.auth.signInWithEmailAndPassword(this.username, this.password)
      .then(response => {
        this.username = '';
        this.password = '';
        this.getUserFromFirestore(response);
      }).catch(error => {
        console.error(error);
        this.$.inputPassword.invalid = true;
        this.$.inputPassword.errorMessage = error.message;
        this.$.inputPassword.validate();
      });
  }

  getUserFromFirestore(user) {
    firestore.collection('users').doc(user.uid)
      .get()
      .then(doc => {
        console.log(doc.data());
        //this.set('currentUser', this.docToObject(doc));
        this.changePage(user.uid, doc.data().role);
      })
      .catch(error => {
        console.error(error);
      });
  }

  changePage(uid, role) {

    let page = '';

    if (role === 'superuser') {
      page = '/superuser/dashboard';
    } else if (role === 'admin') {
      page = '/admin/calendar';
    } else if (role === 'doctor') {
      page = '/doctor/clinics';
    }

    setTimeout(() => {
      window.history.pushState({}, null, page);
      window.dispatchEvent(new CustomEvent('location-changed'));
    }, 1000);
  }

Nothing seems particularly wrong with this implementation and it seems straightforward. 此实现似乎没有什么特别错误,而且看起来很简单。 The login having its own view is also fine. 具有自己视图的登录名也可以。 There is displayable DOM associated with it ("please log in", username, password box), so there is nothing wrong with having it as a selectable view with authentication knowledge within it. 有与之关联的可显示DOM(“请登录”,用户名,密码框),因此将其作为具有身份验证知识的可选视图是没有错的。

Just make sure that the login page is not the gatekeeper to meaningful action. 只需确保登录页面不是采取有意义措施的守门员即可。 ie The user cannot add / delete data from firestore simply by changing pages in the inspector. 即,用户不能仅通过更改检查器中的页面来从Firestore添加/删除数据。 iron-pages !== security, only navigation. iron-pages !==安全性,仅用于导航。

Additionally, it is not against best practices to have nested iron-pages . 此外,嵌套iron-pages也不是最佳实践。 iron-pages is essentially a glorified element.setAttribute('hidden', '') and element.removeAttribute('hidden') . iron-pages本质上是荣耀的element.setAttribute('hidden', '')element.removeAttribute('hidden') Though, a common gotcha with nested iron-pages is to keep track of exit state when navigating away in the parent iron-pages . 但是,嵌套iron-pages的常见陷阱是在导航到父iron-pages时跟踪退出状态。 eg login goes to admin then in admin you navigate to reschedule, then you log in as a different admin, the admin portal probably shouldn't automatically be at reschedule. 例如,登录进入admin,然后在admin中导航至重新计划,然后以其他管理员身份登录,则管理门户可能不应该自动处于重新计划状态。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM