angular / angular.js

AngularJS - HTML enhanced for web apps!
https://angularjs.org
MIT License
58.85k stars 27.52k forks source link

Custom <select> directive: Transclude and replace <option>s not working in IE9 #6926

Open aeharding opened 10 years ago

aeharding commented 10 years ago

Please check out the following JSfiddle (since Plunker doesn't work in IE9): http://jsfiddle.net/Rpe36/

Basically, I have myDirective that is an element. I transclude and replace with the following template:

<select class="some" ng-transclude></select>

It doesn't work. :(

If I use the ngOptions attribute and nothing transcluded, it works fine! Go figure.

I don't know if this is a larger problem with IE9, or something that could be fixed in Angular.

Thanks! Alex

tbosch commented 10 years ago

Hi, thanks for reporting. I could verify this bug against the latest 1.3.0-beta.4 release.

We already fixed this problem for table elements like tbody, ... but not yet for option elements (see https://github.com/angular/angular.js/commit/31c450bcee53d0a3827b7e0a611e9013b2496506).

A workaround would be to define your directive as an attribute on a select element, see this fiddle: http://jsfiddle.net/Rpe36/3/

tbosch commented 10 years ago

Given the commit linked above, this should be easy to implement. Would you like to try to create a PR for this?

aeharding commented 10 years ago

Definitely! I'll see what I can do.

Also, thanks for the workaround, I am doing something similar for the moment.

Thanks again

aeharding commented 10 years ago

So the pull request I just made only partially fixes the root problem.

@tbosch, I think you slightly misunderstood me -- I'm talking about transcluding these troubled elements, not putting them in a template. Essentially, if I transclude items (whether they are <option>s or <thead>) inside of a directive the a template of <select> or <table> (respectfully), I have this problem.

(This pull request is still useful though, as it fixes a directive with a template of <option> root element that is replaced...)

From my research, the larger issue might be something that would need to be changed in JQLite. I see that JQLite adds content using innerHTML and a dummy <div> preceding it.

Unfortunately, IE9 and before has a major bug with completely ignoring elements such as <thead> and <select>, instead just grabbing its contents and throwing them inside a text node...

For example:

angular.element('<option>Hello!</option><option>There!</option>');

This ONLY breaks if JQLite is not replaced by jQuery. jQuery has somehow gotten around this issue to get it working in IE9. However, since angular still uses JQLite internally, the problem persists when adding jQuery into the picture.

The reason the pull request above fixes this for templates is that the elements are surrounded by their specific parent element to be happy. For example, the following works great:

angular.element('<select><option>Hello!</option><option>There!</option></select>');

Overall, I'm not terribly worried, and I do not think this problem is worth everyone's time given the workarounds that exist. I do think it should be documented somewhere (if not here).

caitp commented 10 years ago

Here's the thing: HTML5 has some super silly rules about "allowed content" for particular nodes. What this means is, there's very little we can actually do about this.

One thing that you CAN do, if you want to transclude table content into a directive which uses a select tag for its template, is to make the directive an attribute directive. This way, the browser's HTML parser won't care that you are putting <option> tags as children of the <select> tag, and you'll be able to transclude them just fine.

This is a problem with the HTML parser, and sadly, there is very little we can do to circumvent that with custom elements. Theoretically, WebComponents might address this (and may have already addressed this for the <content> tags), but for Angular 1.x, there ain't much we can do. Take it up with hixie maybe? :p

caitp commented 10 years ago

I'm not sure if we actually need this fix for <option> tags though, does jqLite('<option>') produce an empty collection?

aeharding commented 10 years ago

@caitp I totally agree with you. It's kinda unfortunate that the spec is that strict, and even worse that the browser is so strict while the HTML is being manipulated (before it's actually inserted into the DOM).

Also: Yes, it looks like it does on IE9.

caitp commented 10 years ago

Hmm, I see why jqLite('<option>') fails in angular 1.x but not jQuery 2.x

Our constructor for elements is extremely simple. I think we could improve this without adding too much code. The question is whether or not it's worth it.

I need something to do today though, so I'll hack on improving the jqLite HTML parser for a few minutes, we can look at it.

mgol commented 6 years ago

I'm not convinced we'll fix it given age of the ticket.

But for anyone interested, JSFiddle no longer works in IE 9 so I ported the test case to JS Bin & updated AngularJS to 1.6.9; it's still broken in IE 9 & works in IE 10+: http://output.jsbin.com/qofuhag.