[SOLVED] {{> atOauth}} template not rendering after migration to Meteor 3.0

We recently migrated our Meteor application to Meteor 3.0. We are using custom Blaze templates for the account forms. In our main {{> atForm }} template, we include the {{> atOauth}} template. After migrating, however, it appears that the div for the {{> atOauth}} template is rendering, but has no child elements. Here is a screenshot where you can see the empty div in the developer console:

Some additional context:

  • Although the oauth button is not displaying properly, it does seem to be making the usual http requests to Google on render as it did before.
  • Relevant packages that we have:
    - accounts-ui@1.4.3
    - accounts-ui-unstyled@1.7.2
    - accounts-google@1.4.1
    - accounts-oauth@1.4.5
    - accounts-base@3.0.0
  • It’s worth noting that if we add the {{> loginButtons}} template from the Meteor docs, it does work and we can sign in with Google, but I’d like to avoid switching to that.
  • Here is our custom template that replaces {{> atForm }}
<template name="myAtForm">
  {{#unless hide}}
    <div class="at-form">
      {{#if showError}}
        {{> atError}}
      {{/if}}
      {{#if showResult}}
        {{> atResult}}
      {{/if}}
      {{#if showMessage}}
        {{> atMessage}}
      {{/if}}
      {{#if showPwdForm}}
        {{#if showOauth}}
          {{> atOauth}}
          <div style="font-size: 16px; margin-top: 30px; margin-bottom: 5px; text-align: center; color: #282738; font-family: mukta;"> Or...</div>
        {{/if}}
        {{> atPwdForm}}
      {{/if}}
      {{#if showTermsLink}}
        {{> atTermsLink}}
      {{/if}}
      {{#if showResendVerificationEmailLink}}
        {{> atResendVerificationEmailLink}}
      {{/if}}
    </div>
  {{/unless}}
</template>
  • The {{# if showAuth}} does seem to be working properly based on console.logs. Even if I remove the if statement so that {{> atOauth}} should display no matter what, it still doesn’t display
  • Here is our AccountsUIWrapper.jsx:
import { useLocation } from 'react-router-dom';
import { Template } from 'meteor/templating';
import { Blaze } from 'meteor/blaze';
import React, { useEffect, useRef } from 'react';
import ReactDOM from 'react-dom';
import '../styles/accounts.css';
import './at_bootstrap.css';
import './atForm.html';
import './myAtSocial.html';
import './myAtTextInput.html'
import './hiddenField.html';

Template.prototype.customReplaces = function customReplaces(otherTemplate) {
    var self = this;

    function replaceRender(template) {
      // String template names can be provided and template object is looked up
      if (typeof template === 'string') {
          template = Template[template];
      }

      if (!template) {
          return;
      }
      
      template.renderFunction = self.renderFunction;
    }
  
    // Accept an array as otherTemplate argument
    if (Array.isArray(otherTemplate)) {
      otherTemplate.forEach(replaceRender);
      return;
    }
  
    replaceRender(otherTemplate);
};

AccountsTemplates.configure({
    privacyUrl: `${Meteor.settings.public.HOMEPAGE}/privacy-policy`,
    termsUrl: `${Meteor.settings.public.HOMEPAGE}/terms-of-use`,
    continuousValidation: true,
    negativeFeedback: true,
    negativeValidation: true,
    positiveValidation: true,
    positiveFeedback: true,
    showValidating: true,
    texts: {
        button: {
            signUp: "Register"
        }
    }
    //showForgotPasswordLink: true
});

AccountsTemplates.removeField('email');
AccountsTemplates.addField({
    _id:'email',
    type: 'email',
    required: true,
    displayName: 'email',
    placeholder: {
        signIn: "Enter your email",
        signUp: "Enter your email"
    },
    re: /.+@(.+){2,}\.(.+){2,}/,
    errStr: 'Invalid email',
});

AccountsTemplates.removeField('password');
AccountsTemplates.addField({
    _id: 'password',
    type: 'password',
    placeholder: {
        signUp: "Enter your password",
        signIn: "Enter your password"
    },
    required: true,
    minLength: 6,
    re: /(?=.*\d)(?=.*[a-z])(?=.*[0-9])(?=.*[A-Z])(?=.*[^A-Za-z0-9]).{6,}/,
    errStr: 'At least 1 digit, 1 lowercase, 1 uppercase, and 1 special char',
});

const AccountsUIWrapper = () => {
    const containerRef = useRef(null);
    const location = useLocation();

    useEffect(() => {
        if (location.pathname === "/signin") {
            AccountsTemplates.setState("signIn");
        } else if (location.pathname === "/signup") {
            AccountsTemplates.setState("signUp");
        } else if (location.pathname === "/forgotpwd") {
            AccountsTemplates.setState("forgotPwd");
        } else if (location.pathname === "/resetpwd") {
            AccountsTemplates.setState("resetPwd");
        }

        Template["atForm"].helpers({
            showOauth: function () {
                return !Session.get("hideOauth");
            }
        });

        Template.myAtForm.customReplaces("atForm");
        // Template.myAtSocial.customReplaces('atSocial');

        Template["atTextInput"].helpers({
            equals: function(a, b) {
                return a === b;
            }
        });

        Template["atTextInput"].events({
            'focus #at-field-email'(event, instance) {
                instance.$('#email-icon').css("display", "none");
                instance.$('#email-icon-selected').css("display", "flex");
            },
            'focus #at-field-password'(event, instance) {
                instance.$('#password-icon').css("display", "none");
                instance.$('#password-icon-selected').css("display", "flex");
            },
            'focus #at-field-password_again'(event, instance) {
                instance.$('#password-again-icon').css("display", "none");
                instance.$('#password-again-icon-selected').css("display", "flex");
            },
            'blur #at-field-email'(event, instance) {
                instance.$('#email-icon').css("display", "flex");
                instance.$('#email-icon-selected').css("display", "none");
            },
            'blur #at-field-password'(event, instance) {
                instance.$('#password-icon').css("display", "flex");
                instance.$('#password-icon-selected').css("display", "none");
            },
            'blur #at-field-password_again'(event, instance) {
                instance.$('#password-again-icon').css("display", "flex");
                instance.$('#password-again-icon-selected').css("display", "none");
            },
        });

        Template["myAtTextInput"].customReplaces("atTextInput");

        const view = Blaze.render(Template.atForm, ReactDOM.findDOMNode(containerRef.current));

        return () => {
            Blaze.remove(view);
            if (Meteor.isProduction) {
                analytics.identify(Meteor.userId());
            }
        };
    }, [location]);

    return <span ref={containerRef} />;
};

export default AccountsUIWrapper;

Where is atOauth coming from? I can’t find it in the UI packages… can you share it’s definition?

I think this is coming from useraccounts.
@benarteaga do you use those packages?

Yes, we have the following:

  • useraccounts:bootstrap@1.15.3
  • useraccounts:core@1.17.1

We’re not sure exactly where the {{ atOauth }} is coming from but we suspect one of those packages.

Looks like it is coming from useraccounts:bootstrap because I see the following code in it:

<template name="atOauth">
  <div class="at-oauth">
    {{#each oauthService}}
      {{> atSocial}}
    {{/each}}
  </div>
</template>

Not idea how to solve the issue anyways.

1 Like

We managed to solve it. The issue was indeed in useraccounts:bootstrap, but it was due to a peer dependency. Specifically, useraccounts:bootstrap uses useraccounts:core@1.17.1, which has a global variable issue that was patched in a more recent version useraccounts:core@1.17.2. Someone should fix this version dependency in useraccounts:boostrap.

1 Like