SignaR + AngularJS Array property change is not modifying UI - angularjs

I am trying to create a chat application and wanted to display availability of users on UI.
I am using SignalR and AngularJS.
But UI is not updating after I update members online property like below,
self.hubProxy.client.isAvailable = function (loggedinuser, isOnline) {
$scope.$apply(function () {
self.allMembers[loggedinuser].isOnline = isOnline;
});
I am using ng-repeat="member in allMembers" to bind with all members and using its isOnline property to apply a class.

Assuming your method gets called, and assuming that loggedinuser is the name of the user, then your problem might be with ngRepeat, because you should be enumerating by key:
ng-repeat="(key, value) in allMembers"
Check this: How can I iterate over the keys, value in ng-repeat in angular

Related

How to render only after a function has completed?

I had a list of objects defined in my controller, and was sorting it after vm.init() like this:
const myItems = [{},{},{}...{}]
vm.init().then(function() {
$scope.ajaxLoading = true; // Loader
$scope.itemsToRender = [];
// Pushing items from myItems in this list according to some conditions
$scope.itemsToRender.sort(function(a, b) {
return a.order - b.order;
})
}).finally(function() {
$scope.ajaxLoading = false;
});
This was working fine as expected,
but now I am getting myList from api response in vm.init(), and it does not sort the list when I come to this page. But if I reload the page, it sorts the list and renders perfectly.
I tried debugging and put console.log in the function, and the function was being called.
What am I doing wrong? Is the list rendering before sorting is complete? How do I fix this?
AngularJS needs to be aware of changes in order to digest and reflect those in the template. The sort() method you are using is Javascript, so despite applying it directly to the $scope.itemsToRender variable Angular is still not aware its content has changed, probably because it uses properties such as length for arrays, so if the length does not change it won't recognize those changes.
Anyway I'm pretty sure you can use the orderBy filter instead which will handle all this for you and it's the best practise for this case
<tr ng-repeat="item in itemsToRender | orderBy:'id'">
https://docs.angularjs.org/api/ng/filter/orderBy

Knockout binding for gijgo treeview with checkbox

I like the gijgo treeview with checkbox as its clean and neat and it solves the purpose of showing the hierarchy information. Check below link for documentation.
https://gijgo.com/tree/demos/bootstrap-treeview-checkbox
Since knockout.js is preferred for the front end development hence its needed to develop a knockout binding for this particular requirement.
The idea is to populate the hierarchy data from the backend and bind it to the custom knockout binding.
The user selects/un-selects some checkboxes and then hits the save button. the selected/unselected data is again sent back to the server for the save.
The below code is the usage of the control in jquery.
The function tree.getCheckedNodes() returns the array of selected checkboxes.
How would one call the above function from an knockout binding.
ko.bindingHandlers.tree = {
init: function (element, valueAccessor, allBindingsAccessor) {
},
update: function (element, valueAccessor, allBindingsAccessor) {
var options = valueAccessor() || {};
var value = ko.utils.unwrapObservable(valueAccessor());
var tree = $(element).tree(value);
}
}
In the init method:
Unwrap the widget's initial data passed by your viewmodel from the valueAccessor
Convert the initial data to the format the tree widget understands
Initialize the widget with the correct settings $(element).tree({ /* ... */ })
Attach an event listener (.on("change", function() { }) to track user-input
In the event listener function, write back the data from the UI to the viewmodel (e.g. valueAccessor() (tree.getCheckedNodes()))
Optional: add custom disposal logic to clean up the widget if knockout removes it from the DOM
In the update method, which is called if your view model's value changes
Implement the logic that updates the widget based on your new settings. Probably something like tree.check(ko.unwrap(valueAccessor())). Make sure the update is "silent", if it would trigger a change event, you'd end up in an infinite loop.

How to display the value of other controllers in one view dynamically

I am working on an application were index page and inside that I am showing multiple views, In index nav bar I have to show notification count and User name which comes from 2 different controllers,
I am able to display the notification count and User name successfully, but the issue is the values are not changing dynamically.
We need to refresh the page for the new values.
What can I do in this situation can any one please guide me.
I'm guessing you're watching the value directly and not by some object wrapper. In this case javascript isn't actually updating the variable, but assigning a complete new one. Anything out of the function scope that updates the variable will never receive the new value.
The solution is simple; wrap the value in an object and share/inject that object.
angular.module('myApp')
.value('myPageState', {
notificationCount: 0
});
angular.module('myApp')
.controller('myController', function($scope, myPageState) {
$scope.myPageState = myPageState;
});
<div class="my-notification-thingy"> {{ myPageState.notificationCount }} </div>
You can achieve it by:
Maintain those values in rootScope so that you
will have the two way binding.
Making use of emit to notify the parent controller about value changes. This will work only if those two controllers are present in child elements.
In child controllers fire event on value update:
$scope.$emit('valueChanged', {value: val});
In parent controller receive event value:
$scope.$on('valueChanged', function(event, args) {
console.log(args.value);
});

Selecting and accessing items in ng-repeat

I have an array of objects in a service that I want to display in a view like this
<li ng-repeat="item in filteredItems = (resource.items | filter:multipleFilters...">
<span class="title">{{item.escaped_name}}</span>
</li>
I want these objects to be selectable which is the easy part. Then I'd need to get the count of the selected items and be able to iterate over all of them to process/change data.
What is the best method to save and access the selected items?
Note that the selected items could change, for example a selected item could fall away due to changed filters. Also, I don't want to set a selected property on the objects in the array directly - the array of objects is in a service and used throughout the app in many lists and I don't want to "clean up" the selected property for each view.
I usually include a ng-click tag:
<li ng-repeat="item in ..." ng-click="select_item(item)">
The item passed to select_item() will be the one the user selected.
You can take the object oriented approach and create models to do your work for you basically. I've been in the same scenario and it's worked for me, plus I think it's a good tool angular gives you to work with. You would have a listModel and an itemModel - the list model would have a list of your single item itemModels. The list model you would use 1 instance of between where ever you are using this list of items. Take this with a grain of salt because this is just an example.
So you'd have the listModel
.factory('listModel', [singleItemModelInject,
function(singleItemModel) {
function listModel(items) {
this.items = _.map(items, listModel.create);
}
listModel.create = function(value, name) {
return new listModel(value);
};
listModel.prototype = {
get whatever() {
},
set whatever() {
}
}
return listModel;
}
]);
Notice it injects singleItemModel - this would be the individual item model it would look the same except it would have all your information on creation with what you pass it like
.factory('singleItemModel', [whateverYouNeedInjected,
function() {
function singleItemModel(item) {
this.name = item.name;
//default to not selected
this.selected = item.selected || false;
this.whateverElseYouNeed = item.whateverElseYouNeed
}
singleItemModel.create = function(value) {
return new singleItemModel(value);
};
So then you would have a single instance of the listModel you would use across your app, you can toggle a hide or show with whatever property, as long as you have setters and getters for your properties you want to access (like the name and isSelected or whatever you want) and have it 2 way bound, if it is changed to selected anywhere, it is universal because of the single instance you are using.
You can also you individual instances if you dont want the selected values to persist across the app, and only in that one page (or where ever you are using it)

AngularFire objects

I've been playing with AngularFire, and I understand the documentation for collections. But I feel like I'm totally missing things when it comes to loading specific items inside the collection, by anything besides position in the array.
All of the examples in the Firebase data have pretty names for the api like user/name/first
But when I use angularFireCollection to save a collection I get my object inside a unique $id. (not as pretty)
Is that the expected behavior? And if so, how would I get() an item based on a value instead?
ex. I created a key called slug. That has 'my-theme' in the collection. And I want to load it by $routeParams.
.when('/themes/:slug/', {
templateUrl: 'views/theme.html',
controller: 'ThemesCtrl'
})
How would I load an object into themes/my-theme instead of themes/-J50neNBViK9l7P4QAYc
Thanks in advance...
angularFireCollection automatically creates a list of items with auto-generated incremental IDs (generated by Firebase's push() method). If you want to create a list of items with custom names, angularFire might be a better service to use (it uses set instead of push). For example:
function ThemesCtrl($scope, angularFire) {
$scope.themes = {};
angularFire(new Firebase(URL), $scope, 'themes');
$scope.addTheme = function() {
$scope.themes["my-theme"] = $scope.currentTheme;
}
}

Resources