简体   繁体   中英

Backbone Model triggers event conditionally, View doesn't hear it

I'm creating a Geolocation Model, fetching from localStorage, and checking if I have there is a latitude property there. If there isn't, a 'geolocation:city_state_blank' event is triggered. If 'geolocation:city_state_blank' is heard, @get_users_geolocation() is fired.

class App.GeoLocation extends Backbone.Model
    initialize: ->

       @on 'geolocation:city_state_blank', -> @get_users_geolocation()

       @fetch().always => 
         if @get('city_state').length is 0
           @trigger 'geolocation:city_state_blank'
         else
           @trigger('geolocation:done')




    get_users_geolocation: =>
        # Code to get / save (in localStorage) a users geolocation and city / state
        @trigger('geolocation:done')

After get_users_geolocation() is done the event geolocation:done is triggered.

I've removed details of fetching the users geoLocation / reverse Geolocation lookups, which are all asynchronous. But the end result of all that work boils down to triggering the geolocation:done event.

class App.GeolocationView extends Backbone.View
   initialize: =>
      @listenTo @model, 'geolocation:done', =>
        alert('geolocation:done was heard in the view!!!!')

Here is the problem:

In the scenario when the Geolocation model fetches from localStorage and determines the property latitude is not set , and thus calls get_users_geolocation — the View alerts geolocation:done was heard in the view!!!!

But in the scenario when Geolocation has a latitude property (the else), and triggers the geolocation:done right away, the View does not alert anything. The view doesn't hear it.

I've console.logged the hell out of this, and can say the flow paths are working. The if/else are working and the View is getting instantiated correctly. Logging within the callback after fetching from localStorage yields this..

@fetch().always => 
    console.log @get('current_city_state')
    console.log typeof @get('current_city_state')
    // Norfolk, VA
    // String

So, there is data there..

What is going on?? Help please!!

I'd guess that the latitude property of your App.GeoLocation is a single number. That would leave your test looking like this:

if some_number.length is 0

Numbers don't have length properties so @get('latitude').length will be undefined and you're left with:

if undefined is 0

That will always be false so @trigger('geolocation:done') always gets called.

If you're interested in the existence of the latitude property, then CoffeeScript's existential operator ( ? ) will server you better:

if @get('latitude')?
    # There is a latitude.
else
    # No latitude.

To see how ? behaves you could have a look at what this does:

class M extends Backbone.Model

m = new M(p: 0)

if m.get('p') == 0
    console.log('p == 0')
else
    console.log('p != 0')
if m.get('p')?
    console.log('there is a p')
else
    console.log('the p is a lie')
if m.get('no')?
    console.log('yes no')
else
    console.log('no no')    

That will give you p == 0 , there is ap , and no no .

Demo: http://jsfiddle.net/ambiguous/qmBNy/

I ended up doing this. It aint pretty, but it works.

@listenTo @model, 'geolocation:done', =>        
   @prefill_location_input()
   @initialize_autocomplete()

@prefill_location_input()
@initialize_autocomplete()

I think that your right, the event is triggered before init returns. Here, if Object is ready right away, the @prefill_location_input() and @initialize_autocomplete() are called, geolocation:done will overwrite it in cases its fetched from the network.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM