setValue (value, type) {
    let childValue, child
    let i, j
    const updateDom = false
    const previousChilds = this.childs

    this.type = this._getType(value)

    // check if type corresponds with the provided type
    if (type && type !== this.type) {
      if (type === 'string' && this.type === 'auto') {
        this.type = type
      } else {
        throw new Error('Type mismatch: ' +
            'cannot cast value of type "' + this.type +
            ' to the specified type "' + type + '"')
      }
    }

    if (this.type === 'array') {
      // array
      if (!this.childs) {
        this.childs = []
      }

      for (i = 0; i < value.length; i++) {
        childValue = value[i]
        if (childValue !== undefined && !(childValue instanceof Function)) {
          if (i < this.childs.length) {
            // reuse existing child, keep its state
            child = this.childs[i]

            child.fieldEditable = false
            child.index = i
            child.setValue(childValue)
          } else {
            // create a new child
            child = new Node(this.editor, {
              value: childValue
            })
            const visible = i < this.getMaxVisibleChilds()
            this.appendChild(child, visible, updateDom)
          }
        }
      }

      // cleanup redundant childs
      // we loop backward to prevent issues with shifting index numbers
      for (j = this.childs.length; j >= value.length; j--) {
        this.removeChild(this.childs[j], updateDom)
      }
    } else if (this.type === 'object') {
      // object
      if (!this.childs) {
        this.childs = []
      }

      // cleanup redundant childs
      // we loop backward to prevent issues with shifting index numbers
      for (j = this.childs.length - 1; j >= 0; j--) {
        if (!hasOwnProperty(value, this.childs[j].field)) {
          this.removeChild(this.childs[j], updateDom)
        }
      }

      i = 0
      for (const childField in value) {
        if (hasOwnProperty(value, childField)) {
          childValue = value[childField]
          if (childValue !== undefined && !(childValue instanceof Function)) {
            const child = this.findChildByProperty(childField)
            if (child) {
              // reuse existing child, keep its state
              child.setField(childField, true)
              child.setValue(childValue)
            } else {
              // create a new child, append to the end
              const newChild = new Node(this.editor, {
                field: childField,
                value: childValue
              })
              const visible = i < this.getMaxVisibleChilds()
              this.appendChild(newChild, visible, updateDom)
            }
          }
          i++
        }
      }
      this.value = ''

      // sort object keys during initialization. Must not trigger an onChange action
      if (this.editor.options.sortObjectKeys === true) {
        const triggerAction = false
        this.sort([], 'asc', triggerAction)
      }
    } else {
      // value
      this.hideChilds()

      delete this.append
      delete this.showMore
      delete this.expanded
      delete this.childs

      this.value = value
    }

    // recreate the DOM if switching from an object/array to auto/string or vice versa
    // needed to recreated the expand button for example
    if (Array.isArray(previousChilds) !== Array.isArray(this.childs)) {
      this.recreateDom()
    }

    this.updateDom({ updateIndexes: true })

    this.previousValue = this.value // used only to check for changes in DOM vs JS model
  }
Set value. Value is a JSON structure or an element String, Boolean, etc.
@param {*} value
@param {String} [type]  Specify the type of the value. Can be 'auto',
                        'array', 'object', or 'string'