diff --git a/packages/ember-htmlbars/tests/helpers/input_test.js b/packages/ember-htmlbars/tests/helpers/input_test.js index 50f735a34e5..cedd56a72cf 100644 --- a/packages/ember-htmlbars/tests/helpers/input_test.js +++ b/packages/ember-htmlbars/tests/helpers/input_test.js @@ -8,6 +8,10 @@ import ComponentLookup from "ember-views/component_lookup"; import TextField from 'ember-views/views/text_field'; import Checkbox from 'ember-views/views/checkbox'; import EventDispatcher from 'ember-views/system/event_dispatcher'; +import { + disableInputTypeChanging, + resetInputTypeChanging +} from 'ember-views/system/build-component-template'; var view; var controller, registry, container; @@ -265,6 +269,48 @@ QUnit.test("should change if the type changes", function() { equal(view.$('input').attr('type'), 'text', "it changes after the type changes"); }); +QUnit.module("{{input type='text'}} - dynamic type in IE8 safe environment", { + setup() { + commonSetup(); + + disableInputTypeChanging(); + + controller = { + someProperty: 'password', + ie8Safe: true + }; + + view = View.extend({ + container: container, + controller: controller, + template: compile('{{input type=someProperty}}') + }).create(); + + runAppend(view); + }, + + teardown() { + resetInputTypeChanging(); + + runDestroy(view); + runDestroy(container); + } +}); + +QUnit.test("should insert a text field into DOM", function() { + equal(view.$('input').attr('type'), 'password', "a bound property can be used to determine type in IE8."); +}); + +QUnit.test("should NOT change if the type changes", function() { + equal(view.$('input').attr('type'), 'password', "a bound property can be used to determine type in IE8."); + + run(function() { + set(controller, 'someProperty', 'text'); + }); + + equal(view.$('input').attr('type'), 'password', "it does NOT change after the type changes in IE8"); +}); + QUnit.module("{{input}} - default type", { setup() { commonSetup(); diff --git a/packages/ember-views/lib/system/build-component-template.js b/packages/ember-views/lib/system/build-component-template.js index d6957caafa4..f57e9fd57b3 100644 --- a/packages/ember-views/lib/system/build-component-template.js +++ b/packages/ember-views/lib/system/build-component-template.js @@ -26,7 +26,7 @@ export default function buildComponentTemplate({ component, layout, isAngleBrack // element. We use `manualElement` to create a template that represents // the wrapping element and yields to the previous block. if (tagName !== '') { - var attributes = normalizeComponentAttributes(component, isAngleBracket, attrs); + var attributes = normalizeComponentAttributes(component, isAngleBracket, attrs, tagName); var elementTemplate = internal.manualElement(tagName, attributes); elementTemplate.meta = meta; @@ -43,6 +43,30 @@ export default function buildComponentTemplate({ component, layout, isAngleBrack return { createdElement: !!tagName, block: blockToRender }; } +// Static flag used to see if we can mutate the type attribute on input elements. IE8 +// does not support changing the type attribute after an element is inserted in +// a tree. +var isInputTypeAttributeMutable = (function() { + var docFragment = document.createDocumentFragment(); + var mutableInputTypeTextElement = document.createElement('input'); + mutableInputTypeTextElement.type = 'text'; + try { + docFragment.appendChild(mutableInputTypeTextElement); + mutableInputTypeTextElement.setAttribute('type', 'password'); + } catch (e) { + return false; + } + return true; +})(); + +var canChangeInputType = isInputTypeAttributeMutable; +export function disableInputTypeChanging() { + canChangeInputType = false; +} +export function resetInputTypeChanging() { + canChangeInputType = isInputTypeAttributeMutable; +} + function blockFor(template, options) { Ember.assert("BUG: Must pass a template to blockFor", !!template); return internal.blockFor(render, template, options); @@ -113,7 +137,8 @@ function tagNameFor(view) { // Takes a component and builds a normalized set of attribute // bindings consumable by HTMLBars' `attribute` hook. -function normalizeComponentAttributes(component, isAngleBracket, attrs) { +function normalizeComponentAttributes(component, isAngleBracket, attrs, tagName) { + var hardCodeType = tagName === 'input' && !canChangeInputType; var normalized = {}; var attributeBindings = component.attributeBindings; var i, l; @@ -135,17 +160,32 @@ function normalizeComponentAttributes(component, isAngleBracket, attrs) { if (colonIndex !== -1) { var attrProperty = attr.substring(0, colonIndex); attrName = attr.substring(colonIndex + 1); - expression = ['get', 'view.' + attrProperty]; + + if (attrName === 'type' && hardCodeType) { + expression = component.get(attrProperty) + ''; + } else { + expression = ['get', 'view.' + attrProperty]; + } } else if (attrs[attr]) { // TODO: For compatibility with 1.x, we probably need to `set` // the component's attribute here if it is a CP, but we also // probably want to suspend observers and allow the // willUpdateAttrs logic to trigger observers at the correct time. attrName = attr; - expression = ['value', attrs[attr]]; + + if (attrName === 'type' && hardCodeType) { + expression = getValue(attrs[attr]) + ''; + } else { + expression = ['value', attrs[attr]]; + } } else { attrName = attr; - expression = ['get', 'view.' + attr]; + + if (attrName === 'type' && hardCodeType) { + expression = component.get(attr) + ''; + } else { + expression = ['get', 'view.' + attr]; + } } Ember.assert('You cannot use class as an attributeBinding, use classNameBindings instead.', attrName !== 'class'); diff --git a/packages/ember-views/lib/views/text_field.js b/packages/ember-views/lib/views/text_field.js index 629c82a2e74..b9e9b2bd35c 100644 --- a/packages/ember-views/lib/views/text_field.js +++ b/packages/ember-views/lib/views/text_field.js @@ -98,7 +98,9 @@ export default Component.extend(TextSupport, { value: "", /** - The `type` attribute of the input element. + The `type` attribute of the input element. To remain compatible with IE8, this + cannot change after the element has been rendered. It is suggested to avoid using + a dynamic type attribute if you are supporting IE8 since it will be set once and never change. @property type @type String