Add an item to a firebase collection - angularjs

I need to append an item in one or more collections in Firebase Database using angularfire (AngularJS)
For example, if I have:
users {
Marcelo: {
userid: 1
},
Javier: {
userid: 2
}
}
How can I append a new item in each collection, getting something like this?
users {
Marcelo: {
userid: 1
state: "enabled"
},
Javier: {
userid: 2
state: "enabled"
}
}

There is no specific AngularFire operation for this. But since AngularFire is just built on top of the Firebase JavaScript SDK, you use that to read the data and loop over it and then update each item.
A short snippet to get you started:
var ref = firebase.database().ref("users");
users.once("value", function(snapshot) {
snapshot.forEach(function(userSnapshot) {
userSnapshot.ref.update({ state: "enabled" });
});
});

In this sample we create a function to insert param in our object
//object = users object
//key = state or something else
//value = true or something else
var insertParam = function(object, key, value) {
for (var _key in object) {
object[_key][key] = value;
}
}
var app = angular.module("app", []);
app.controller("ctrl", [
"$scope",
function($scope) {
$scope.users = {
Marcelo: {
userid: 1
},
Javier: {
userid: 2
}
}
var insertParam = function(object, key, value) {
for (var _key in object) {
object[_key][key] = value;
}
}
insertParam($scope.users, "state", true);
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<ul>
<li ng-repeat="(key, option) in users">
{{key}}
<ul>
<li><b>userId:</b> {{option.userid}}</li>
<li><b>state:</b> {{option.state}}</li>
</ul>
</li>
</ul>
</div>

Related

AngularJS - Watch filtered list for changes

Within angular I have a filtered list of people that takes the filter criteria from a predicate function. I want to watch a variable of the filtered list (called filteredPeople) every time the filtered list changes. But I am unable to see when that variable changes.
My code is below:
HTML:
<ul>
<li ng-repeat="person in ($ctrl.filteredPeople = ($ctrl.people | filter: $ctrl.filter))">
...
</li>
</ul>
JS:
controller: ['$scope',
function ($scope) {
var $ctrl = this;
$ctrl.people = {...}
$ctrl.filteredPeople = [];
$scope.$watch($ctrl.filteredPeople, function () {
console.log("called"); //not being called
});
$ctrl.filter = function (p) {
//custom filter function for each item in the array of people
}
}]
I can answer any questions of provide more code if needed
angular.module('app', []).controller('ctrl', function($scope) {
var vm = this;
vm.items = [
{ name: 'Sam' },
{ name: 'Max' },
{ name: 'Tom' },
{ name: 'Henry' },
{ name: 'Jack' },
{ name: 'Kate' }
]
var counter = 1;
$scope.$watchCollection('vm.filtered', function(){
console.log('Changed' + counter++);
})
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js">
</script>
<div ng-app='app' ng-controller='ctrl as vm'>
<input type='text' ng-model='vm.filter' />
<ul>
<li ng-repeat='item in vm.filtered = (vm.items | filter : vm.filter)'>{{item}}</li>
</ul>
</div>

Custom Filter not firing in angular1

I have two fields in a digest: genres and secondary_genre which are integers.
I have a dropdown fields of genres which returns a value (ng-model = query.genres)
I have tried to create a custom function which will compare genre and secondary genre to query.genre and return if EITHER condition is met. Individually they work fine i.e with filter:{secondary_genre:query.genre) but for the OR condition I need a custom.
The custom filter I have written is called with
<li ng-repeat="film in listOfTitles | customGenreFilter:query.genre">
The filter is
.filter('customGenreFilter', function() {
return function(data, query) {
if (data) {
return data.filter(data.genres == query || data.secondary_genre == query);
}
else {
return [];
}
};
});
but it is throwing errors. How can I write this custom filter to return the item if the condition genres = query.genre OR secondary_genre = query.genre.
Your filter is not working because you are not using the .filter() method properly, the method require the argument to be a lambda function in which return whether the item should or not stay in the list. In order to fix your code you have to change the following line:
return data.filter(data.genres == query || data.secondary_genre == query);
To:
return data.filter(function(item) {
return item.genre == query || item.secondary_genre == query
});
The following example implements a working version of your filter.
angular.module('myApp', [])
.component('app', {
templateUrl: '/app.html',
controller: App
})
.filter('customGenreFilter', CustomGenreFilter);
function App($scope) {
$scope.query = {
genre: ''
};
$scope.listOfTitles = [
{ title: 'test 1', genre: 'genre1' },
{ title: 'test 2', genre: 'genre2' },
];
}
function CustomGenreFilter() {
return function(data, query) {
if (query === '') return data;
if (data) return data.filter(function(item) {
return item.genre == query || item.secondary_genre == query
});
return [];
};
}
angular.element(function() {
angular.bootstrap(document, ['myApp']);
});
<script id="/app.html" type="text/ng-template">
<select ng-model="query.genre" ng-options="item as item for item in ['', 'genre1', 'genre2']">
</select>
<ul>
<li ng-repeat="film in listOfTitles | customGenreFilter:query.genre">
{{ film.title }}
</li>
</ul>
</script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.js"></script>
<app />

Firebase & Angular - Retrieve and display flattening data

I have flattening data in my firebase with the following code. But when I want display favorite user posts list with ng-repeat, the template gets repeated a second time and comes out totally blank. How can I correct this?
"Post": {
"xKkdfjjfld856i": {
"name": "My first post",
"author": "Miguel"
},
"xKkdfjj556FGHh": { ... },
"xK499DF6FlHjih": { ... }
},
"users": {
"John": {
favorited_posts {
"xKkdfjjfld856i": true,
"xKkdfjj556FGHh": true
}
},
"Mia": { ... },
"Patrick": { ... }
},
HTML:
<div ng-repeat="post in favoritedPosts track by $index">
<div class="card post-card">
<h1>{{post.name}}</h1>
<p>{{post.author}}</p>
</div>
</div>
Controller :
var userRef = new Firebase("https://myApp.firebaseio.com/users")
var userFavoriteRef = userRef.child($scope.user_id).child("favorited_posts")
var favoritedPosts = $firebaseArray(userFavoriteRef)
userFavoriteRef.once("value", function(snapshot) {
snapshot.forEach(function(childSnapshot) {
var key = childSnapshot.key();
var postRef = new Firebase("https://myApp.firebaseio.com/Posts")
postRef.child(childSnapshot.ref().key()).once('value', function(postSnapshot) {
console.log("Look",postSnapshot.val());
$scope.favoritedPosts = postSnapshot.val();
});
});
});
Try working with the $firebaseArray and $getRecord (documentation) to get the object value based on the object key. Then you will have everything you need without looping over assync calls.
Controller
var userRef = new Firebase("https://myApp.firebaseio.com/users")
var userFavoriteRef = userRef.child($scope.user_id).child("favorited_posts")
$scope.favoritedPosts = $firebaseArray(userFavoriteRef)
var postRef = new Firebase("https://myApp.firebaseio.com/Posts")
$scope.posts = $firebaseArray(postRef)
HTML
<div ng-repeat="post in favoritedPosts">
<div class="card post-card">
<h1>{{posts.$getRecord(post.$id).name}}</h1>
<p>{{posts.$getRecord(post.$id).author}}</p>
</div>
</div>

Re-binding a tree (Wijmo tree) with AngularJS

I am fairly new to AngularJS, and really struggling to re-bind a Wijmo tree (or even a tree implemented using UL and LI elements wth ng-repeat) with new data on changing of value of a Wijmo combobox (or, even a regular dropdown of HTML select elem).
Below is the code I have written, which is working fine in initial page load. But on changing the dropwdown, the tree is not being reloaded with new data fetched by loadDomainTree method; it is still showing old data. Can somebody help me figure out what's wrong with this code?
HTML:
<div ng-controller="DomainCtrl">
<select id="domain" ng-model="currentDomain" ng-options="item.Name for item in domainList"></select>
<div>
<ul id="wijtree">
<li ng-repeat="item in domainEntityList" id={{item.Id}}>
<a>{{item.Name}}</a>
</li>
</ul>
</div>
</div>
JS:
$(document).ready(function ()
{
$("#domain").wijcombobox({
isEditable: false
});
$("#wijtree").wijtree();
});
function DomainDropdownModel(data) {
this.Id = data.Id.toString();
this.Name = data.Name;
};
function DomainTreeModel(data) {
this.Id = data.Id;
this.Name = data.Name;
};
function DomainCtrl($scope, $locale) {
$scope.domainList = [];
$.ajax({
url: dropDownUrl,
async: false,
success: function (data) {
$(data).each(function (i, val) {
var domain = data[i];
var domainId = domain.Id.toString();
var domainName = domain.Name;
$scope.domainList.push(new DomainDropdownModel({ Id: domainId, Name: domainName }));
});
}
});
$scope.currentDomain = $scope.domainList[0];
$scope.loadDomainTree = function (domainId) {
domainEntitiesUrl = DOMAIN_API_URL + DOMAIN_ID_PARAM + domainId;
//alert(domainEntitiesUrl);
$scope.domainEntityList = [];
$.ajax({
url: domainEntitiesUrl,
async: false,
success: function (data) {
$(data).each(function (i, entity) {
var domainEntity = data[i];
var domainEntityId = domainEntity.Id.toString();
var domainEntityName = domainEntity.Name;
$scope.domainEntityList.push(new DomainTreeModel({ Id: domainEntityId, Name: domainEntityName }));
});
}
});
};
//Will be called on setting combobox dfault selection and on changing the combobox
$scope.$watch('currentDomain', function () {
$scope.loadDomainTree($scope.currentDomain.Id);
});
}
You may $watch for the selectedItem of WijCombobox and then, re-load the wijtree accordingly. Here is the code:
$scope.$watch('selectedItem', function (args) {
if (args === 'Tree 1') {
$("#wijtree").wijtree("option", "nodes", $scope.nodes1);
}
else {
$("#wijtree").wijtree("option", "nodes", $scope.nodes2);
}
});
HTML Code
<wij-combobox data-source="treeList" selected-value="selectedItem">
<data>
<label bind="name"></label>
<value bind="code"></value>
</data>
</wij-combobox>

How to watch a deferred service property?

If I have the following simple controller & service.
<div ng-controller="MessagesCtrl">
<ul>
<li ng-repeat="message in data.messages">
{{ message }}
</li>
</ul>
</div>
myApp.factory('Data', function () {
var _messages = [];
return {
messages: _messages,
getMessages: function() {
$http.get('/messages').then(function(response) {
_messages = response.data;
});
}
};
});
function MessagesCtrl($scope, Data) {
$scope.data = Data;
$scope.data.getMessages();
}
I would expect the messages model to auto-update when the AJAX request completes and fills the object, however it does not. How is this supposed to work? (I'm sort of asking what the best practice is for this structure).
Edit - working solution:
<div ng-controller="MessagesCtrl">
<ul>
<li ng-repeat="message in data.messages">
{{ message }}
</li>
</ul>
</div>
myApp.factory('Data', function () {
return {
getMessages: function() {
var _this = this;
$http.get('/messages').then(function(response) {
_this.messages = response.data;
});
}
};
});
function MessagesCtrl($scope, Data) {
$scope.data = Data;
$scope.data.getMessages();
}
I'm fairly certain you have a JavaScript issue here and not an AngularJS related issue. Let's remove the AngularJS related code and leave just the JavaScript piece:
function factory() {
var _messages = [];
return {
messages: _messages,
getMessages: function() {
_messages = [1, 2, 3, 4];
}
}
}
> var test = factory();
undefined
> test.messages
[]
> test.getMessages()
undefined
> test.messages
[]
test.messages points to the origin _messages array whereas later on _messages is getting changed but test.messages is not.

Resources