opitzconsulting / jquery-mobile-angular-adapter

jquery mobile angular adapter
MIT License
517 stars 114 forks source link

Changing element text with .text() breaks radio and text button rendering #46

Closed jupiterplanet closed 12 years ago

jupiterplanet commented 12 years ago

This is the most hard to explain problem I had with rc2. I have a directive which I use to replace text with translations. In the jsFiddle http://jsfiddle.net/jupiter/hUtqV/5/ I have called this directive replace. The directive replaces attribute values with

<input type="button" replace="value" value="replace me" />

or the HTML element's text as in

<div replace>replace me</div>.

The el.text() which replaces the text breaks the rendering of the radio buttons if I have the replace directive in the enclosing div to replace the page title and at the same time in the radio button to replace the radio buttons text.

If I either remove replace="data-title" of the page element, the replace of the radio button or change jquery-mobile-angular-adapter-1.0.7-rc2.js to jquery-mobile-angular-adapter-1.0.7-rc1.js it works.

The button in the footer is always broken with the replace directive.

I have used jquery mobile 1.1.0 and AngularJS 1.0.0rc9.

jupiterplanet commented 12 years ago

I further analyzed the problem and found out that overriding the compile function and returning a link function which compiles the children once it is linked caused the problem for the radio buttons.

The following code works for the radio buttons.

angular.module('test',[]).
directive ('replace', ['$compile', function factory($compile) {
    return {           
        priority: 10,
        restrict: 'ECMA',
        link: function postLink(scope, el, attrs) {
            if (attrs.replace.length>0) {
                // Translate attributes
                angular.forEach(attrs.replace.split(' '), function(v, k) {
                        el.attr(v, 'replaced with el.attr');
                    });
            };                         
            // Translate text nodes
            if (attrs.replace.length == 0) {
                el.text('replaced with el.text');
            };
        }
    };
}]);

So far I haven't further looked into why this is no longer possible in RC2 nor if there are any special use cases which actually require this approach.

The button in the footer is not working because it seems that the replace directives in the label element and the replace directive in the a element are called at two different points of time. The replace directive in the label element is called before jquery Mobile creates the radio button, the replace directive in the a element is called after jQuery mobile has enhanced the a element to a jQuery mobile button. This causes the el.text(...) statement to overrides the spans which jQuery mobile has inserted. I couldn't find out yet why they are handled differently.

The workaround is to wrap the link text into spans like

<a href="#"><span replace>Text</span></a>
tbosch commented 12 years ago

Updated to current versions: http://jsfiddle.net/hUtqV/7/ angular 1.0.1, adapter 1.0.7-rc3-SNAPSHOT, jqm 1.1.0

tbosch commented 12 years ago

Hi, the problem in your jsfiddle was that you call $compile again in the compile function of your directive. Here is a corrected version: http://jsfiddle.net/hUtqV/8/ In that version everything works fine except for the button in the lower toolbar.

However, this approach for i18n does not work with the adapter in general for the following reasons: We are doing a precompile before angular compiles the page (why is explained in the readme of the adapter). There we are doing the normal jquery mobile enhancement for some tags, including <a data-role="button">, which jqm modifies to the following:

<a href="#">
    <span>
        <span class="ui-btn-text">Button</span>
        <span class="ui-icon ui-icon-check ui-icon-shadow">&nbsp;</span>
    </span>
</a>

So if you are doing element.text('...') your are removing the <span> elements that jquery mobile created inside the <a> tag. A better approach would be to use a global controller on the body tag that contains an object with key/value mappings for your translations. In the html you can then just use {{...}} to do the replacement that are needed.

Here is a version of the jsfiddle that does that: http://jsfiddle.net/hUtqV/9/

So I am closing this issue. If you have further questions, just post a comment.

However, thanks for taking the time to report this, and for using the adapter!

Tobias