Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUGFIX release-1-13] Fix {{input}} helper on IE8. #12596

Merged
merged 4 commits into from
Nov 12, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions packages/ember-htmlbars/tests/helpers/input_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
50 changes: 45 additions & 5 deletions packages/ember-views/lib/system/build-component-template.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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');
Expand Down
4 changes: 3 additions & 1 deletion packages/ember-views/lib/views/text_field.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down