1

So, to frame this appropriately, I am inheriting a codebase here and I don't have a deep knowledge of angular. Basically there's an application that shows or hides a loading gif based on whether or not a database import has finished.

For some reason though, despite the fact that the dataset import has finished angular does not recognize it. Simplified code sample below:

<h4>Status
    <small ng-hide="dataset.failed">{{ dataset.status }}</small>
    <img class="pull-right" ng-hide="(dataset.import_complete || dataset.failed)" src="/url" height="12">
</h4>
    <span>{{ dataset.import_complete }}</span>

The span I added in for debugging purposes, but essentially the span shows True, while the img tag above is still being displayed. This implies that angular is evaluating dataset.import_complete || dataset.failed to False instead.

Why would this happen? Any suggestions to helping debug this behavior would be hugely appreciated.

Updates: I think the thought here is that some faulty type-checking is happening here. Specifically the following appear to be true:

# Always evaluates to True, regardless of import_complete
dataset.import_complete == True
# Always evaluates to False, again, regardless of import_complete
dataset.import_complete == 'True'
# result is bool, as it should be, making this only more baffling
type(dataset)

Also, here's a bit of the backend, but probably not very helpful:

@route(bp, '/dataset/<int:dataset_id>', methods=["GET", "POST"])

def dataset(dataset_id): """Returns the dataset management page"""

dataset = Dataset.query.get_or_404(dataset_id)

return render_template(
    'manage/dataset.html',
    dataset=dataset,
)

And for more info, here's the output of vars(dataset):

{'import_complete': True, 'failed': False, 'pending_deletion': False}
Slater Victoroff
  • 21,376
  • 21
  • 85
  • 144
  • I suspect this an issue with these values from Python not being serialized into correct JS? For example, Python's `True` => JavaScript's `true`. – Kathryn Gonzalez Apr 30 '16 at 19:24
  • You're setting `ng-hide` to the string `(dataset.import_compete || dataset.failed)`. You're using neither Python nor JavaScript variables there. – dirn Apr 30 '16 at 19:35
  • @dirn Really? Reading the angularjs documentation briefly seems to imply that I'm using the right syntax here. I'm also using this same syntax in many other places in the application without issue. What would I change? – Slater Victoroff Apr 30 '16 at 19:42
  • @dirn Also, removing the quotes results in a parsing error. – Slater Victoroff Apr 30 '16 at 19:43
  • @RyanGonzalez That seems possible, but I'm at a loss for how to debug that. I tried using the Batarang plugin, but it's not working. Suggestions there? – Slater Victoroff Apr 30 '16 at 19:44
  • @RyanGonzalez Did a quick test by setting the conditional to things like == "True" to no avail, so it doesn't seem to be a conversion error. – Slater Victoroff Apr 30 '16 at 19:51
  • @SlaterTyranus What does `

    {{dataset}}

    ` output? Also it's worth posting what's the Flask backend is sending to the Angular frontend
    – bakkal Apr 30 '16 at 20:03
  • @bakkal dataset isn't JSON serializable. – Slater Victoroff Apr 30 '16 at 20:58
  • @SlaterTyranus Hold it, you're not having the AnuglarJS syntax _inside_ the Jinja2 syntax are you? Because Jinja2 may be interpolating the variables from Python for you before AngularJS even gets to compile them. (both use the same `{{ expr }}` tokens) – bakkal Apr 30 '16 at 21:20

1 Answers1

1

Are your expression being interpolated by Jinja2 before NG even runs?

Don't forget that Jinja and Angular both are using the same {{ and }} tokens for their syntax.

And seems to me like you are using Jinja to render that HTML, and if that HTML contains your AngularJS code as well, then the {{ expr }} gets interpolated by Flask/Jinja before AngularJS gets to run on the browser.

My guess is the True you saw in the HTML, came about from Jinja2 rendering this expression (not Angular)

<span>{{ dataset.import_complete }}</span>

And that at least the key import_complete is undefined in the dataset JS object in your Angular app's $scope. (because someObject.undefinedKey == 'True' would give the JS boolean false)

The $scope.dataset object may very well exist in your ng app, but it doesn't seem like $scope.dataset contains the key import_complete (for ng-hide="dataset.import_complete" to work, you'll need $scope.dataset.import_complete to be defined obviously)

How to check?

To confirm that it is infact Jinja2 that rendered the above, try look at the HTML by saving it with curl or wget, you should see that it doesn't come with <span>{{ dataset.import_complete }}</span> in the static HTML for NG to run on, but with <span>True</span>

How to fix

If this is true, then try adapting more structured way this to your project, by separating the server/API and the client

Otherwise for a quick fix you can change the tokens for your angular app to something else, and then figure out how to get the variables into your $scope (e.g. render $scope.dataset = {{dataset_json}}; in Jinja to get it into the JS, a hack you want to avoid in the long run)

Debug

If this <span>{{ dataset.import_complete }}</span> shows True as output, that does suggest that the key import_complete actually holds a string with the value "True" in it, as opposed to the JS boolean true

I recon your comment you said you tried to no avail something along

ng-hide="dataset.import_complete == 'True'"

That seems possible, but I'm at a loss for how to debug that.

So I would suggest you look into the value of the dataset object, like this

<p>{{dataset}}</p> or maybe <p>{{dataset | json}}</p>, then you can bee sure of its value and types

You can also look at the browser's dev tools Network tab to see what the Flask backend has sent back to AngularJS.

Community
  • 1
  • 1
bakkal
  • 54,350
  • 12
  • 131
  • 107
  • Thanks for the thoughts! So, import_complete is in fact True, but not a string. I'm not exactly sure how types are being handled on the angular side here, but as posted the `== 'True'` evaluates to `false`. When removing the quotes however it evaluates to `true`. Seems pretty in-line with what @RyanGonzalez was suggesting. Any thoughts on what's happening with these types? – Slater Victoroff Apr 30 '16 at 20:48
  • Oh, also, to add clarity as to why this isn't currently accepted, for some reason, `== True` is true even when dataset.import_complete is False. I... I really have no idea what's going on here, but this seems like some awful type-conversion voodoo. Will accept regardless in a few minutes, but if you can shed some light on this it would be hugely appreciated. – Slater Victoroff Apr 30 '16 at 20:51
  • I'm confused as well, but I'm sure there's an explanation if you dig enough. e.g. what is `typeof dataset.import_complete` in the JS? Also anyway to see it what's sent in the HTTP? e.g. in the browser network tab? – bakkal Apr 30 '16 at 21:08
  • e.g. Presumably, the AngularJS pulls the data through the API `'/dataset/123'`, it's helpful to take a peek at that data as AngularJS receives it (don't probe it Python side with `type(x)`), but probe it JS side, perhaps also the browser's dev tool Network tab – bakkal Apr 30 '16 at 21:17