ng-class not updating inside of ng-repeat when array is changed - angularjs

The code below is from a chat interface in an Angular application. The user will select the users they wish to send the message to by clicking on user bubbles which will add those users to an array selectedChatUsers
Lists out the users. Highlights the ones that are currently selected.
<div ng-repeat="user in PlayerController.chatUsers | orderBy:['type','name']"
class="chat-recipient"
ng-class="{'selected-recipient' : PlayerController.selectedChatUsers.indexOf(user) >= 0 }"
ng-click="PlayerController.selectRecipient(user)">
<div class="chat-recipient-name" ng-bind="user.name"></div>
<div class="chat-recipient-icon"></div>
</div>
Another feature of the application is the ability to click on a chat bubble from a sent message, and have the selectedChatUsers array be populated with the "to" property, which is an array of users that this message was sent to.
This functionality works, but the ng-class directive that should show those users are selected, does not function.
function chatReply(message){
/* one attempt was to try and manipulate the array rather than copy the information directly. This attempt had no more success. I'm including it here just to show that it was tried */
/****
self.selectedChatUsers.length = 0;
angular.forEach(message.to, function(recipient){
self.selectedChatUsers.push(recipient);
});
****/
/* Also tried wrapping the whole thing in $timeout and $scope.$apply, this also doesn't update the ng-class in the view */
$timeout(function() {
$scope.$apply(function(){
self.selectedChatUsers = angular.copy(message.to);
});
});
}
Suggestions on how I can make the ng-class in the view be updated when selectedChatUsers is changed?

Both class and ng-class will not work together, always apply class:
class="chat-recipient"
ng-class="{'selected-recipient' : condition }"
remove class and change it to:
ng-class="condition == true ? 'chat-recipient selected-recipient' : 'chat-recipient'"

Related

Tag Manager Variable from a parent of click element (toggle)

Here is the structure of the toggle:
<div data-id="thisIsTheID" class="abc">
<div class="edf">
<button value="false"></button>
</div>
</div>
The trigger only works with click >> all elements >> click classes contains edf. I tried click element matches css selector
div.abc
div.abc *
div.abc button
but none will work as trigger.
However, I want the data-id as variable in Google Tag Manager in order to differentiate between different toggles.
How do I create a trigger that works and how do I get the variable?
Here the actual code and debugger view:
Try making a custom JS variable like so:
function(){
return {{Click Element}}.parentElement.getAttribute("data-test-id");
}
It should return the value of your attribute on click.
But the classes look auto-generated. They feel like they can and will change on cache refresh or on front-end builds. I would try to use selectors like div[data-test-id] * or even div[data-test-id=isAgePolicy] *

AngularJS Dynamic Class - Background Image not working

Unable to dynamically change class on "ng-repeat" list with background image contained in "style.css". Tried suggested solutions such as "ng-style="{ 'backgound-image': 'image_name.jpg' }" directly onto the element without much luck.
The class would need to change conditionally.
ng-class to the rescue. Managed to achieve the conditional effect without needing to modify my "style.css".
Within my template "ng-repeat (item in productList)" i used the following, note your repeat will need to use the "track by $index" syntax:
<div ng-class="{ 'no' : 'image_off', 'yes': 'image_on' }[item.isOn]" ng-click="switchOnOff($index, item.productId, item.isOn); $event.stopPropagation();"></div>
NOTE: "$event.stopPropagation()" prevents the page submitting.
"image_off/image_on" refers to two different classes under my stylesheet. Each with their own respective background image.
In your controller use the following to action the event on the UI inside a function e.g "switchOnOff" :
$scope.productList[index].isOn = $scope.productList[index].isOn == 'no' ? 'yes' : 'no';

Update binding after server response / How do I selectively update bindings?

I'm trying to prevent the value of a model from updating in a form.
For example :
I have a payment details form that lists the user's saved information (name, address etc) along with the form that is used to edit the same information.
I've been experimenting using the :: for one time binding as I don't want the displayed information to changed when input controls are changed (but I obviously want the models updated values so i can send them to the server for processing).
How do I update the displayed model values after the server responds that the changes have been saved, are ok etc? I can't seem to find a way to update the one time binding (as I'm guessing this is fully the intended functionality).
So I guess my question boils down to :
How do I selectively update bindings on some controls but not others?
Actually you just want to display different vars.
You should try with a temporary model object (that is a copy of your object like "editedObject") and when you validate you will update the original object.
See it working in this plunker
The editing space :
<input ng-model="editCopy.value"> <button ng-click="validateChange()">Change</button>
The ng-repeat :
<td ng-repeat="item in items" ng-click="editItem(item)">
{{item.value}}
</td>
The functions :
$scope.editItem = function(item){
$scope.editCopy = angular.copy(item);
$scope.editingItem = item;
}
$scope.validateChange = function(){
$http.get('index.html').success(function(){
$scope.editingItem.value = $scope.editCopy.value;
});
}

Auto-scrolling multiple chat logs using AngularJS

In this simplified example, I have:
$scope.channels = [channel1, channel2, ...]
and a channel is an object containing:
chatLog: ["msg1", "msg2", ...]
In the .html, I have something similar to:
<div ng-repeat="channel in channels" ng-show="channel.isActiveWindow">
<div class="chatlog" style="height:500px;">
<div ng-repeat="msg in channel.msgLog">
<div>{{msg}}</div>
</div>
</div>
</div>
I'd like to implement automatic scrolling when a new message is added to channel.chatLog. What would be a proper way to do this with AngularJS? I can compute where to scroll to, but I need an event to fire when the chatLog is appended.
I was thinking of setting up a watch on the chatLog of every channel, but this becomes way too manual and I need to be careful about removing the watch as well. Another way would be to not rely on watches but make sure every place in the code that will append to a chatLog will also fire the event and provide some info about which channel the event relates to.
Also, when this event is fired, is there a way to access the div with the class "chatlog" in the code above for the given channel without assigning it a unique 'id' and looking it up by that?
Thanks in advance :)
You could create a chatlog directive and add it to your markup like this:
<div chatlog ng-repeat="msg in channel.msgLog">
Then in the linking function of your directive you can do something like:
link: function(scope, element, attrs) {
if (scope.$last) {
// this will run each time the channel.msgLog array changes
// more precisely, every time the last element is linked
}
}

How to get hold of template html in directive

I'm trying to create a simple time picker directive. http://plnkr.co/edit/VYGqhPbHf1yqXLpemGEP
When user click on input field I want to display the content of my template html as a dropdown below the input (will take care of css later) so that user can select a time from the list. I'm not sure how to get hold of the template (something like angular.element(template).show())
Thanks!
Edit: I managed to come up with this: http://plnkr.co/edit/zAplNKVfohXLbIzwjhy4?p=preview
Everything works except when I click any of the list, it does not set the correct model value.
Try the following:
Embed the the HTML for the date picker list
Hide the list from the html
If the input gets focus change the visibility.
Pseudo code:
HTML:
<ul ng-show="listVisible">
<li> .... </li>
</ul>
JavaScript
scope.listVisible = false;
element.$on('focus', function() {
scope.listVisible = true;
});
Do something similar in reverse.
I managed to get it working. I had to create a new scope for the dropdown element. http://plnkr.co/edit/zAplNKVfohXLbIzwjhy4?p=preview

Resources