Ember HTML 渲染的组件未被 FB.XFBML.parse() 找到

Ember component rendered HTML isn't found by FB.XFBML.parse()

更新 1

我已将问题追溯到 FB.XFBML.parse() 使用的 html5Info 函数。它有一个条件。这是两个元素不同的布尔语句:element.getAttribute('fb-xfbml-state') 变为 "rendered"(真)或 null(假),具体取决于处理的元素。

所以问题缩小到...

为什么我的 ember-组件的渲染 HTML 没有将其 'fb-xfbml-state' 设置为“渲染”,而模板中的静态 HTML 设置了?

更新 2

通过使我的 Facebook 组件包含这样的 .setAttribute 调用来解决我的问题。

/* global FB */
import Ember from 'ember';

export default Ember.Component.extend({
    tagName: 'div',
    classNames: 'fb-share-button',
    attributeBindings: [
        'data-href',
        'data-layout',
        'data-action',
        'data-show-faces',
        'data-share'
    ],

    onDidInsertElement: function() {
        Ember.run.debounce(this, function() {
            console.log(document)
            _.forEach(document.getElementsByClassName('fb-share-button'), function(element) {
                element.setAttribute('fb-xfbml-state', 'rendered');
            });
            FB.XFBML.parse();
        }, 250);
    }.on('didInsertElement'),
});

更新 3

似乎该组件生成了子节点,如 document.getElementById('myComponentElementInTheDOM').childNodes 中所示,这些是生成的组件 HTML 与普通组件 HTML 的不同之处。 FB-SDK 有一个条件 IF 语句,对于没有子节点的普通 HTML 产生 true,而对于具有子节点的组件生成的 HTML 产生 false,导致只考虑普通 HTML 作为渲染目标。

问题...

我有一种情况,我使用 chrome 检查器检查 DOM,我发现两个相同的 html 片段:

<div id="ember403" class="ember-view fb-share-button" data-href="https://mypage.com" data-layout="button_count" data-action="like" data-show-faces="false" data-share="true"></div>
<div id="ember407" class="ember-view fb-share-button" data-href="https://mypage.com" data-layout="button_count" data-action="like" data-show-faces="false" data-share="true"></div>

其中一个,作为简单的 HTML 放在路由把手文件中,另一个是使用我制作的 Ember 组件呈现的。当我在页面加载后手动调用 FB.XFBML.parse() 时,只会找到一个。

有什么区别!?

我使用了 debug.js 版本的 facebook SDK,当我调用 FB.XFBML.parse() 时,它说:

XFBML Parsing Finish 2, 1 tags found

// Debug version: connect.facebook.net/en_US/all/debug.js
// Normal version: connect.facebook.net/en_US/sdk.js

但是...

如果我现在首先使用 chrome 检查器以一些非常简单的方式修改 DOM,比如添加一个新的空行。之后我可以再次调用 FB.XFBML.parse() 并突然找到两个标签。

我不明白...

我该怎么做才能让 ember 组件生成的 HTML 可以被 FB.XFMBL.parse() 找到,就像普通 [=78] 生成的 HTML 一样=] 来自路线车把文件?

我的路线模板生成相同的 HTML

{{social-facebook data-href="https://mypage.com" data-layout="button_count" data-action="like" data-show-faces="false" data-share="true"}}

<div id="ember407" class="ember-view fb-share-button" data-href="https://mypage.com" data-layout="button_count" data-action="like" data-show-faces="false" data-share="true"></div>

上面的代码在chrome inspector中生成如下所示:

我的组件

/* global FB */
import Ember from 'ember';

export default Ember.Component.extend({
    tagName: 'div',
    classNames: 'fb-share-button',
    attributeBindings: [
        'data-href',
        'data-layout',
        'data-action',
        'data-show-faces',
        'data-share'
    ],

    onDidInsertElement: function() {
        Ember.run.schedule('afterRender', FB.XFBML.parse);
    }.on('didInsertElement'),
});

我的组件模板

{{yield}}

我的初始化程序

/* global FB */
import ENV from '../config/environment';

export function initialize(container, application) {
    var debug = false;

    // Wait for Facebook to load before allowing the application
    // to fully boot. This prevents `ReferenceError: FB is not defined`
    application.deferReadiness();

    var fbAsyncInit = function() {
        console.log("SOCIAL DEBUG: fbAsyncInit invoked.");
        initFacebook(window.FB);
        application.advanceReadiness();
    };

    loadFacebookSDK(debug);
    window.fbAsyncInit = fbAsyncInit;
}

function initFacebook(FB) {
    console.log("SOCIAL DEBUG: FB.init invoked.", ENV.fbAppId, ENV.fbSDKVersion);
    FB.init({
        appId      : ENV.fbAppId,
        xfbml      : true,
        version    : ENV.fbSDKVersion
    });
}

function loadFacebookSDK(debug) {
    (function(d, s, id){
        console.log("SOCIAL DEBUG: Load facebook SDK code", d, s, id);
        var js, fjs = d.getElementsByTagName(s)[0];
        if (d.getElementById(id)) {return;}
        js = d.createElement(s); js.id = id;
        js.src = debug ? "//connect.facebook.net/en_US/all/debug.js" : "//connect.facebook.net/sv_SE/sdk.js";
        fjs.parentNode.insertBefore(js, fjs);
    }(document, 'script', 'facebook-jssdk'));
}

export default {
  name: 'social-facebook',
  initialize: initialize
};

这个组件修改就可以了

此组件代码可用于解决由组件创建的用于处理数据绑定的隐藏子节点引起的问题。这个解决方案与我想象的一样接近完美,因为我无法调整 ember 组件和 facebook sdk 的工作方式。

/* global FB */
import Ember from 'ember';

export default Ember.Component.extend({
    tagName: 'div',
    classNames: 'fb-share-button',
    attributeBindings: [
        'data-href',
        'data-layout',
        'data-action',
        'data-show-faces',
        'data-share'
    ],

    onDidInsertElement: function() {
        // This is needed to make the FB SDK accept the component rendered HTML even though child nodes due to databinding exists
        // To understand more about this, follow the code inside connect.facebook.net/en_US/all/debug.js
        // 1: Read up on the function FB.XFBML.parse, found by searching for: 'function parse(/*DOMElement*/ dom'
        // 2: Read up on the function html5Info, whos returnvalue will be affected, found by searching for: 'function html5Info('
        // 3: Notice the conditional inside html5Info containing the booleans below:
        //        element.childNodes.length === 0           --- supposed to evaluate to true, but isn't due to childNodes used for databinding
        //        element.getAttribute('fb-xfbml-state')    --- used to make sure that the conditional becomes true for the component
        document.getElementById(this.get('elementId')).setAttribute('fb-xfbml-state', 'rendered');

        // This makes FB-SDK render the HTML-stubs inserted by the ember components
        Ember.run.debounce(this, FB.XFBML.parse, 100);
    }.on('didInsertElement'),
});

作为参考,这是我使用的相关初始化程序

/* global FB */
import ENV from '../config/environment';

export function initialize(container, application) {
    var debug = true;

    // Wait for Facebook to load before allowing the application
    // to fully boot. This prevents `ReferenceError: FB is not defined`
    application.deferReadiness();

    var fbAsyncInit = function() {
        initFacebook(window.FB);
        application.advanceReadiness();
    };

    loadFacebookSDK(debug);

    window.fbAsyncInit = fbAsyncInit;
}

function initFacebook(FB) {
    FB.init({
        appId      : ENV.fbAppId,
        xfbml      : true,
        version    : ENV.fbSDKVersion
    });
}

function loadFacebookSDK(debug) {
    (function(d, s, id){
        var js, fjs = d.getElementsByTagName(s)[0];
        if (d.getElementById(id)) {return;}
        js = d.createElement(s); js.id = id;
        js.src = debug ? "//connect.facebook.net/en_US/all/debug.js" : "//connect.facebook.net/sv_SE/sdk.js";
        fjs.parentNode.insertBefore(js, fjs);
    }(document, 'script', 'facebook-jssdk'));
}

export default {
  name: 'social-facebook',
  initialize: initialize
};

以及有关如何使用该组件的示例

{{social-facebook data-href="https://yourwebpage.com" data-layout="button_count" data-action="like" data-show-faces="false" data-share="true"}}