-2

I am very new to AngularJS. I have to make what seems like a very small change to our website, but it's stumping me.

  • We have a boolean value in our Django user model, let's call it user.myValue
  • On page load, display text "A" if that boolean is True, and text "B" if it is False
  • In that message text, we have a link element that fires an AngularJS function to open a modal.
  • In the modal, there are two buttons. Clicking them will toggle the boolean value by calling a Django REST endpoint.
  • If the call to that REST endpoint is successful, the value in the database will be updated. (Either way, we close the modal.)
  • On successful update of the database, I now want the message on the main page to be updated to show the correct message based on the new value.

I've managed to get everything up to the last step. I've played with a lot of potential solutions and nothing is quite working. For the element holding the message text,

  • Binding would be ideal because then I can just use $scope.apply() to update the element, but because it's a boolean using ng-bind isn't great: I don't want to just show the user "true" or "false".
  • ng-if is ugly because on page load it displays both messages for a split second and then removes one. I also can't figure out how to get it to re-run the "if" condition when we've had a successful database update.
  • I've played with creating a directive, but that also doesn't seem right.
  • ng-model="user.myValue" ng-value="true" isn't useful because it doesn't conditionally show the message...

Here's my code so far.

Template:

<div ng-controller="myCtrl"> <div id="modal-my-value-toggle" class="modal"> <a ng-click="myValueToggle('false')">No</a> <a ng-click="myValueToggle('true')">Yes</a> </div> {% if user.myValue %} <div name="myValueTrue"> Message A: Your value is Yes. <a ng-click="openModal()">click here to change your value</a> </div> {% else %} <div name="myValueFalse"> Message B: Your value is No. <a ng-click="openModal()">click here to change your value</a> </div> {% endif %} </div> 

Controller:

function myCtrl ($scope, $http, Notification) { $scope.username = context.targetUsername; $scope.openModal = function() { var $myModal = $('div').find('#modal-my-value-toggle'); $myModal.modal({ keyboard: false, backdrop: "static" }); $myModal.modal('show'); }; $scope.myValueToggle = function(userProvidedValue) { // hide the modal var $myModal = $('div').find('#modal-my-value-toggle'); $myModal.modal('hide'); if (userProvidedValue === 'true') { var success = "Your value is now Yes"; } else { var success = "Your value is now No"; } // ping the api to set flag to true $http({ method: 'GET', url: '/api/user/' + $scope.username + '/myValue/?myValue=' + userProvidedValue, headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).then(function(response) { Notification.success(success); }, function(response) { Notification.error("There was an error."); }); }; } 

Some other stuff I tried...

This is based on this https://coderwall.com/p/ybbopq/use-ng-value-for-ng-model-with-boolean-values It just displays both messages and isn't really what I want. It's meant to provide a radio input that you can switch between to update the value in $scope.

<div name="myValue_true" ng-model="user.myValue" ng-value="true"> Message A: Your value is Yes. <a ng-click="openModal()">click here to change your value</a> </div> <div name="myValue_false" ng-model="user.myValue" ng-value="false"> Message B: Your value is No. <a ng-click="openModal()">click here to change your value</a> </div> 

This one is just a different way of deciding which message to display on initial page load. It flashes both messages and then removes one.

<div ng-if="user.myValue"> Message A: Your value is Yes. <a ng-click="openModal()">click here to change your value</a> </div> <div ng-if="!user.myValue"> Message B: Your value is No. <a ng-click="openModal()">click here to change your value</a> </div> 

I also tried this janky thing in the controller (didn't work):

function myCtrl ($scope, $http, Notification) { $scope.username = context.targetUsername; $scope.openModal = function() { var $myModal = $('div').find('#modal-my-value-toggle'); $myModal.modal({ keyboard: false, backdrop: "static" }); $myModal.modal('show'); }; $scope.myValueToggle = function(userProvidedValue) { // hide the modal var $myModal = $('div').find('#modal-my-value-toggle'); $myModal.modal('hide'); var message_ids = { 'yes': 'myValue_true', 'no': 'myValue_false' } if (userProvidedValue === 'true') { var success = "Your value is now Yes"; var show_message = message_ids['yes'] var hide_message = message_ids['no'] } else { var success = "Your value is now No"; var show_message = message_ids['no'] var hide_message = message_ids['yes'] } // ping the api to set flag to true $http({ method: 'GET', url: '/api/user/' + $scope.username + '/myValue/?myValue=' + userProvidedValue, headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).then(function(response) { $('div').find('#' + show_message).show(); $('div').find('#' + hide_message).hide(); Notification.success(success); }, function(response) { Notification.error("There was an error."); }); }; } 

Am I going about this completely the wrong way? Am I supposed to be making a directive out of the message element? Or something else? Any help would be greatly appreciated. Thank you!

1 Answer 1

0

The reason behind the message flashing and then being removed in your ng-ifs is that user.myValue isn't defined when it first renders the page. This can be fixed by doing the following:

Add the following function to the controller

$scope.checkMyValue = function() { if (typeof $scope.user == 'undefined') { return false; // Prevent error on check of undefined user } else { return typeof $scope.user.myValue != 'undefined'; } }; 

You can then change your ng-if to be

ng-if="!user.myValue && checkMyValue()" 
Sign up to request clarification or add additional context in comments.

4 Comments

BShaps, good to know - thank you! That will likely come in handy because I'll have to use this way of displaying the message initially if I want both messages to be in the DOM for later. Any idea how to get it to update when we've successfully updated the DB value? I don't even need to re-query the DB; I can assume that a successful call to my REST endpoint means that we can safely show the user that their value has changed.
The digest cycle should run whenever that value is changed. If it doesn't, wrap it in a $scope.$apply or $timeout with 0 milliseconds ($timeout runs the digest cycle). If you want to assume the value successfully flips and it is always a bool you can just add user.myValue = !user.myValue to the success callback
Thanks BShaps! I ended up figuring out a way to do it with ng-bind. I set the values of the DOM element I wanted to change in the controller; when the page is first loaded, I selected the value to show based on what's in the db. then I changed the value upon successful response from the API. Basically, moved the initial if/else into the controller instead of in the template. Worked like a charm.
Good to hear, glad you got it fixed!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.