Scenario:
I have a component that has an <aura:renderIf>
within an <aura:iteration>
. The <aura:iteration>
references a component attribute called "selectedItems" to display a list of items, accompanied by a delete button. When the delete button is clicked, the following actions are performed on the Controller:
var selectedItem = component.get('v.selectedItems');
selectedItems.splice()
theevent.target.value
component.set('v.selectedItems', selectedItems);
Problem:
Uncaught TypeError: a.getAttribute is not a function appears.
Other observations:
Aside from the error message, the delete button functions as expected.
Attempted fixes:
- I have tried to wrap either the
<aura:renderIf>
or the<aura:iteration>
in a<span>
without success as instructed in the stack exchange link below.
Lightning: Bug in Lightning framework when using aura:renderIf?
When the code updating the component attribute is removed, there's no error, but the delete button does not function as desired.
Changing
splice()
topop()
still shows the error, but only if the delete button of the last item is clicked. The delete button does not function as desired.The
<aura:iteration>
in the component was stripped down to just the delete button with an onclick controller event and a value attribute of an ID, while the controller was also stripped down to the component.get('v.selectedItems'), selectedItems.splice() based on event.target.value, then component.set(). Still shows the error even though the item is deleted.
Component
<aura:attribute name="selectedItems" type="sObject[]" />
....
<aura:iteration items="{!v.selectedItems}" var="obj">
<tr>
<td>
<button type="button" class="btn btn-default" onclick="{!c.removeItem}" value="{!obj.x__Lookup_ID__c}">
Del
</button>
</td>
</tr>
</aura:iteration>
Controller
removeItem : function(component, event, helper) {
var target = event.target.value;
var selectedItems = component.get('v.selectedItems');
for (var i = 0; i < selectedItems.length; i++) {
if (target == selectedItems[i].x__Lookup_ID__c) {
selectedItems.splice(i, 1);
}
}
component.set('v.selectedItems', selectedItems);
}
Question:
How do I prevent the uncaught typeerror from showing?
Update (2016-05-19):
I have five components in a Lightning App. All components use Bootstrap CSS.
Everything described and performed above this update section refers to Component 3 (selected item list).
Component 1 (client list) implements a Bootstrap Tab and uses Require JS to load Bootstrap JS in its controller (please see below). When the Bootstrap JS is removed, the Uncaught TypeError goes away, but the Bootstrap Tab no longer works. I wish to retain full Bootstrap functionality without the error message if possible.
require.config({
paths: {
"jquery": "/resource/ns__jquery?",
"bootstrap": "/resource/ns__bootstrapjs?",
"jqueryui": "/resource/ns__jquery_ui_js?",
"bootstrapdatetimepickerjs": "/resource/ns__bootstrapdatetimepickerjs?"
}
});
Update (2016-06-16):
The upgraded error message post Summer 16 update revealed the following.
Uncaught TypeError: a.getAttribute is not a function
throws at https://domain-dev-ed.lightning.force.com/resource/ns__jquery?:2:12385
at Error (native)
at Ky.<anonymous> (https://domain-dev-ed.lightning.force.com/auraFW/javascript/dpJ3LUIaUXt-CtvrXqBcnQ/aura_prod.js:153:22)
at new Ky (https://domain-dev-ed.lightning.force.com/auraFW/javascript/dpJ3LUIaUXt-CtvrXqBcnQ/aura_prod.js:153:296)
at Object.handler_aura_systemError (https://domain-dev-ed.lightning.force.com/l/%7B%22mode%22%3A%22PROD%22%2C%22app%22%3A%22one%3Aone%22%2C%22fwuid%22%3A%22dpJ3LUIaUXt-CtvrXqBcnQ%22%2C%22loaded%22%3A%7B%22APPLICATION%40markup%3A%2F%2Fone%3Aone%22%3A%22NRYw4XaJ6u61jC4_l9chhA%22%7D%2C%22requestedLocales%22%3A%5B%22en_US%22%2C%22en%22%5D%7D/app.js:5058:225)
at https://domain-dev-ed.lightning.force.com/l/%7B%22mode%22%3A%22PROD%22%2C%22app%22%3A%22one%3Aone%22%2C%22fwuid%22%3A%22dpJ3LUIaUXt-CtvrXqBcnQ%22%2C%22loaded%22%3A%7B%22APPLICATION%40markup%3A%2F%2Fone%3Aone%22%3A%22NRYw4XaJ6u61jC4_l9chhA%22%7D%2C%22requestedLocales%22%3A%5B%22en_US%22%2C%22en%22%5D%7D/app.js:3512:244
at Object.handleEvent (https://domain-dev-ed.lightning.force.com/l/%7B%22mode%22%3A%22PROD%22%2C%22app%22%3A%22one%3Aone%22%2C%22fwuid%22%3A%22dpJ3LUIaUXt-CtvrXqBcnQ%22%2C%22loaded%22%3A%7B%22APPLICATION%40markup%3A%2F%2Fone%3Aone%22%3A%22NRYw4XaJ6u61jC4_l9chhA%22%7D%2C%22requestedLocales%22%3A%5B%22en_US%22%2C%22en%22%5D%7D/app.js:3511:338)
at handleDelegatedEvent (https://domain-dev-ed.lightning.force.com/l/%7B%22mode%22%3A%22PROD%22%2C%22app%22%3A%22one%3Aone%22%2C%22fwuid%22%3A%22dpJ3LUIaUXt-CtvrXqBcnQ%22%2C%22loaded%22%3A%7B%22APPLICATION%40markup%3A%2F%2Fone%3Aone%22%3A%22NRYw4XaJ6u61jC4_l9chhA%22%7D%2C%22requestedLocales%22%3A%5B%22en_US%22%2C%22en%22%5D%7D/app.js:3509:72)
at G.oc (https://domain-dev-ed.lightning.force.com/auraFW/javascript/dpJ3LUIaUXt-CtvrXqBcnQ/aura_prod.js:274:169)
at https://domain-dev-ed.lightning.force.com/auraFW/javascript/dpJ3LUIaUXt-CtvrXqBcnQ/aura_prod.js:228:72
at https://domain-dev-ed.lightning.force.com/auraFW/javascript/dpJ3LUIaUXt-CtvrXqBcnQ/aura_prod.js:264:424
In addition, removing the nested <aura:renderIf>
does not make the error message go away.
Update (2016-06-22):
Also tried applying noConflict() with jQuery without success.
Of the five components that uses Bootstrap CSS, only the following components use Bootstrap JS: component 1 (client list) uses it for the tabs and component 4 (sub-items list) uses it for modals.
It was decided to customize Bootstrap JS by retaining only the aforementioned functionality. By doing this, the error message no longer appears (as of Summer 16) and the desired Bootstrap functionality remains intact.
Stripped Bootstrap JS
+function ($) {
'use strict';
// MODAL CLASS DEFINITION
// ======================
var Modal = function (element, options) {
this.options = options
this.$body = $(document.body)
this.$element = $(element)
this.$dialog = this.$element.find('.modal-dialog')
this.$backdrop = null
this.isShown = null
this.originalBodyPad = null
this.scrollbarWidth = 0
this.ignoreBackdropClick = false
if (this.options.remote) {
this.$element
.find('.modal-content')
.load(this.options.remote, $.proxy(function () {
this.$element.trigger('loaded.bs.modal')
}, this))
}
}
Modal.VERSION = '3.3.6'
Modal.TRANSITION_DURATION = 300
Modal.BACKDROP_TRANSITION_DURATION = 150
Modal.DEFAULTS = {
backdrop: true,
keyboard: true,
show: true
}
Modal.prototype.toggle = function (_relatedTarget) {
return this.isShown ? this.hide() : this.show(_relatedTarget)
}
Modal.prototype.show = function (_relatedTarget) {
var that = this
var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
this.$element.trigger(e)
if (this.isShown || e.isDefaultPrevented()) return
this.isShown = true
this.checkScrollbar()
this.setScrollbar()
this.$body.addClass('modal-open')
this.escape()
this.resize()
this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
this.$dialog.on('mousedown.dismiss.bs.modal', function () {
that.$element.one('mouseup.dismiss.bs.modal', function (e) {
if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true
})
})
this.backdrop(function () {
var transition = $.support.transition && that.$element.hasClass('fade')
if (!that.$element.parent().length) {
that.$element.appendTo(that.$body) // don't move modals dom position
}
that.$element
.show()
.scrollTop(0)
that.adjustDialog()
if (transition) {
that.$element[0].offsetWidth // force reflow
}
that.$element.addClass('in')
that.enforceFocus()
var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
transition ?
that.$dialog // wait for modal to slide in
.one('bsTransitionEnd', function () {
that.$element.trigger('focus').trigger(e)
})
.emulateTransitionEnd(Modal.TRANSITION_DURATION) :
that.$element.trigger('focus').trigger(e)
})
}
Modal.prototype.hide = function (e) {
if (e) e.preventDefault()
e = $.Event('hide.bs.modal')
this.$element.trigger(e)
if (!this.isShown || e.isDefaultPrevented()) return
this.isShown = false
this.escape()
this.resize()
$(document).off('focusin.bs.modal')
this.$element
.removeClass('in')
.off('click.dismiss.bs.modal')
.off('mouseup.dismiss.bs.modal')
this.$dialog.off('mousedown.dismiss.bs.modal')
$.support.transition && this.$element.hasClass('fade') ?
this.$element
.one('bsTransitionEnd', $.proxy(this.hideModal, this))
.emulateTransitionEnd(Modal.TRANSITION_DURATION) :
this.hideModal()
}
Modal.prototype.enforceFocus = function () {
$(document)
.off('focusin.bs.modal') // guard against infinite focus loop
.on('focusin.bs.modal', $.proxy(function (e) {
if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
this.$element.trigger('focus')
}
}, this))
}
Modal.prototype.escape = function () {
if (this.isShown && this.options.keyboard) {
this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
e.which == 27 && this.hide()
}, this))
} else if (!this.isShown) {
this.$element.off('keydown.dismiss.bs.modal')
}
}
Modal.prototype.resize = function () {
if (this.isShown) {
$(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
} else {
$(window).off('resize.bs.modal')
}
}
Modal.prototype.hideModal = function () {
var that = this
this.$element.hide()
this.backdrop(function () {
that.$body.removeClass('modal-open')
that.resetAdjustments()
that.resetScrollbar()
that.$element.trigger('hidden.bs.modal')
})
}
Modal.prototype.removeBackdrop = function () {
this.$backdrop && this.$backdrop.remove()
this.$backdrop = null
}
Modal.prototype.backdrop = function (callback) {
var that = this
var animate = this.$element.hasClass('fade') ? 'fade' : ''
if (this.isShown && this.options.backdrop) {
var doAnimate = $.support.transition && animate
this.$backdrop = $(document.createElement('div'))
.addClass('modal-backdrop ' + animate)
.appendTo(this.$body)
this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
if (this.ignoreBackdropClick) {
this.ignoreBackdropClick = false
return
}
if (e.target !== e.currentTarget) return
this.options.backdrop == 'static'
? this.$element[0].focus()
: this.hide()
}, this))
if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
this.$backdrop.addClass('in')
if (!callback) return
doAnimate ?
this.$backdrop
.one('bsTransitionEnd', callback)
.emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
callback()
} else if (!this.isShown && this.$backdrop) {
this.$backdrop.removeClass('in')
var callbackRemove = function () {
that.removeBackdrop()
callback && callback()
}
$.support.transition && this.$element.hasClass('fade') ?
this.$backdrop
.one('bsTransitionEnd', callbackRemove)
.emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
callbackRemove()
} else if (callback) {
callback()
}
}
// these following methods are used to handle overflowing modals
Modal.prototype.handleUpdate = function () {
this.adjustDialog()
}
Modal.prototype.adjustDialog = function () {
var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
this.$element.css({
paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
})
}
Modal.prototype.resetAdjustments = function () {
this.$element.css({
paddingLeft: '',
paddingRight: ''
})
}
Modal.prototype.checkScrollbar = function () {
var fullWindowWidth = window.innerWidth
if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8
var documentElementRect = document.documentElement.getBoundingClientRect()
fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left)
}
this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth
this.scrollbarWidth = this.measureScrollbar()
}
Modal.prototype.setScrollbar = function () {
var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
this.originalBodyPad = document.body.style.paddingRight || ''
if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
}
Modal.prototype.resetScrollbar = function () {
this.$body.css('padding-right', this.originalBodyPad)
}
Modal.prototype.measureScrollbar = function () { // thx walsh
var scrollDiv = document.createElement('div')
scrollDiv.className = 'modal-scrollbar-measure'
this.$body.append(scrollDiv)
var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
this.$body[0].removeChild(scrollDiv)
return scrollbarWidth
}
// MODAL PLUGIN DEFINITION
// =======================
function Plugin(option, _relatedTarget) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.modal')
var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
if (typeof option == 'string') data[option](_relatedTarget)
else if (options.show) data.show(_relatedTarget)
})
}
var old = $.fn.modal
$.fn.modal = Plugin
$.fn.modal.Constructor = Modal
// MODAL NO CONFLICT
// =================
$.fn.modal.noConflict = function () {
$.fn.modal = old
return this
}
// MODAL DATA-API
// ==============
$(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
var $this = $(this)
var href = $this.attr('href')
var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
if ($this.is('a')) e.preventDefault()
$target.one('show.bs.modal', function (showEvent) {
if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
$target.one('hidden.bs.modal', function () {
$this.is(':visible') && $this.trigger('focus')
})
})
Plugin.call($target, option, this)
})
}(jQuery);
I don't want to call this case closed since this seemed like a lot of work to get around the problem than a real solution.