9

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:

  1. var selectedItem = component.get('v.selectedItems');
  2. selectedItems.splice() the event.target.value
  3. 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:

  1. 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?

  1. When the code updating the component attribute is removed, there's no error, but the delete button does not function as desired.

  2. Changing splice() to pop() still shows the error, but only if the delete button of the last item is clicked. The delete button does not function as desired.

  3. 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.

0

You should get the attribute value like this:

var target = event.target.getAttribute('value');
  • Thanks for the answer, but following your suggestion returned instead an Uncaught TypeError: Undefined is not a function – Bryan May 19 '16 at 16:49
  • I might be missing something, but when I loaded the exact same scripts using <ltng:require>, I didn't see any issue with your code. – prashanthkr May 20 '16 at 5:06
  • Thank you for your effort, I will try the same thing you did using <ltng:require> when I get the chance. – Bryan Jun 10 '16 at 17:07
  • I have switched resource call from RequireJS to <ltng:require> in Component 1, but the error message still appears. – Bryan Jun 16 '16 at 21:20
0

I got the same problem for bootstrap carousel. I also get a "Uncaught TypeError: a.getAttribute is not a function" error message as well. This is a locker service issue since turning it off removes the error.

0

Sometimes the event.target will be undefined so if you try to access event.target.getAttribute('sommething') it will throw error as "undefined" does not have any function named getAttribute().

<aura:if> <aura:renderIf> will not allow event.target so it will give error. Try giving some class or id to your element

<button type="button" class="btn btn-default" onclick="{!c.removeItem}" value="{!obj.x__Lookup_ID__c}" id="{!btn-"v.iterator"}> Del </button>

try to get the element through class or id(which ever is unique)

var element = event.getSource().get('v.class')

It will give you the component.

0

It was definitely related to aura:if & aura:renderIf So for alternative solution in my case, I have created the outer div and add class based on the same condition which i was using in aura:if, and hide using 'slds-hide'.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.