Global variable undefined if assign in from connectedTarget method

//controller name is location_controller.js

export default class extends Controller {
    static targets = [ "visible", "map" ]
    mapTargetConnected(element) {
      this.name = "aaa"
    }

    add(event) {
      console.log(this.name) // this will be undefined in the browser log
    }
}

here is the HTML code

<%= form_with(model: @location, local: false, url: location_path(), data: {controller: 'location', action: 'ajax:beforeSend->location#add'}) do |form| %>
....
<% end %>

this is the code regarding form submit via ajax request. if i access the this.name variable inside the add method or click event its says the variable is undefined… but if i same name variable assign it in connect() method than it’s working…

but i want to assign variable at targetConnected method and use it in the add action method.

1 Like

Hi,

A solution is to create another small controller dedicated to the target element, when it get connected then it emit a window event. The main location controller listen this event…

<%= form_with(model: @location, local: false, url: location_path(), 
  data: { 
    controller: 'location', 
    action: 'setName@window->location#setName ajax:beforeSend->location#add' 
  }) do |form| %>
  <div data-controller="target-location" data-location-target="visible"></div>
<% end %>
// target_location_controller.js
export default class extends Controller {
  connect() {
    const name = "a name"
    const event = new CustomEvent("setName", { "detail": name })
    window.dispatchEvent(event)
  }
}
// location_controller.js
export default class extends Controller {
  static targets = [ "visible", "map" ]

  setName(event) {
    this.name = event.detail
  }

  add(event) {
    console.log(this.name)
  }
}

VoilĂ .

thanks for the reply… But problem is there is i want to set the variable in the connectedTarget method. is that limitation to not assign variable like this? i want to understand this? sorry if i asked to much questions

Problem is in this above code i’m going to use the map, and map is load from the another file. basically i want to load map in the connectedTarget method and then use map in the click add event? hope you got my point

Hi,

Reading the stimulus doc… I found this:

https://stimulus.hotwired.dev/reference/targets

export default class extends Controller {
  static targets = [ "item" ]

  itemTargetConnected(element) {
    this.sortElements(this.itemTargets)
  }

  itemTargetDisconnected(element) {
    this.sortElements(this.itemTargets)
  }

  // Private
  sortElements(itemTargets) { /* ... */ }
}

And this what your are doing… excuse me.

Stimulus invokes each element callback any time its target elements are added or removed after connect()and beforedisconnect() lifecycle hooks.

Are your adding your target “after” connect() life cycle ?

Yes after connect()…but is this above solution is going to able to use global variable in the add event

You should ask @tleish to help you.

@Akshaysavaliya16 - I don’t use UJS, but here are some thoughts on troubleshooting:

  • What version of Stimulus are you using and does it include [name]TargetConnected?
  • Have you placed console.log inside #mapTargetConnected to confirm it’s getting executed?
  • What does the the rendered HTML before/after the map target is added look like?
  • What is the output of console.log(this.element) inside #add(event) and does it match the expected element?

FYI, stimulus best practice saves state in HTML (vs in JavaScript memory). If it were me, I’d change the above to:

export default class extends Controller {
    static targets = [ "visible", "map" ]
+   static variables = { name: String }
    mapTargetConnected(element) {
-     this.name = "aaa"
+     this.nameValue = "aaa"
    }

    add(event) {
-     console.log(this.name)
+     console.log(this.nameValue)
    }
}

Thank you @tleish for the reply…i found the issue that issue is regarding the turbolinks… so basically its called mapTargetConnected method twice due to turbolinks… mapTargetConnected connected twice without called mapTargetDisconnected method.

Hi @Akshaysavaliya16,

With turbo, you have a preview of the page when you back to it. During the preview, it load the page and replace what it have to replace.

In a stimulus controller, you can check if document.documentElement.hasAttribute(“data-turbo-preview”)) to know it and prevent, sometime, to get method called twice.

  mapTargetConnected() {
    if (document.documentElement.hasAttribute("data-turbo-preview")) return

    // do what you want
  }