Using ng-grid with angularFire - key naming issues - angularjs

I've played around with this a bit now, but can't get my head around it:
(Using angular 1.2.2, angularFire 0.5.0 and latest ng-grid)
So I have a firebase and I'm using angularFire to extract my data into an ng-grid component like this:
//First I go get my data, in this case something called 'grades' from ..../db/grades
var promise = angularFire(new Firebase($scope.fireBaseUrl), $scope, 'grades');
//When they're fetched I start up a watcher
promise.then(function(grades) {
startWatchGrade($scope, filterFilter, appSettings);
});
//Then I bind that grades as a datasource to my ng-grid
$scope.gridOptions = {
data: 'grades',
enableCellSelection: false,
enableRowSelection: true,
etc....,
Works great and I've done controls that add new items to 'grades' (push) and remove items (splice) and everything gets reflected to the ng-grid quite nicely. Like so:
//Adding new like this is ok
$scope.grades.push({some-new-data-here});
//Deleting old like this is ok
$scope.grades.splice(row.rowIndex, 1);
But this approach does auto-generated int-based keys to the firebase that change all the time when modified so I wanted to get control of the keys so I changed adding the items to use instead:
var fbRef = new Firebase($scope.fireBaseUrl);
var newRef = fbRef.push({some-items-here});
And everything works ok storing the data into firebase with my non-integer ids, but it doesn't bind to the ng-grid anymore. Dumping the 'grades' into the console shows all the rows being returned, but it doesn't show up in the ng-grid.
So in a nutshell: Integer based index generated by angularFire works ok in ng-grid, custom alphanumeric created by firebase.push doesn't.
I hope it doesn't sound too cryptic. Thought of doing a fiddle, but let's hope it's some 'gotcha' I've overlooked and simple to solve. If not I'll try to patch one up.
Thanks!

As per the documentation, $firebase always returns objects, not arrays, which don't work so well with ng-grid. In this case, angularfire provides a filter to help, orderByPriority. Make sure to inject $filter into your controller.
(Note: I did not use promises or watchers, only the built in events with angularfire)
$scope.grades = $firebase(new Firebase($scope.fireBaseUrl));
$scope.gradesData=[];
$scope.gridOptions = { data: 'gradesData'}
$scope.grades.$on("loaded", function(data) {
var arrData = $filter("orderByPriority")(data);
$scope.$apply(function(){$scope.gradesData = arrData;});
});
You can try a similar approach for $on.('change'...) for new rows.
hope that helps

Related

Angular CRUD, update view when backend/database changes ($resource and REST)

I am currently making an application in angular which does this:
(On page load) Make an api call in angular controller (to symfony2 end point) to get: items.
$scope.items = ItemsService.query(function(data){
$scope.loading = false;
}, function(err){
$scope.loading = false;
});
items is an array containing many item objects.
Each item contains parameters e.g. item.param1 item.param2.
I have built it in a similar way to this tutorial:
http://www.sitepoint.com/creating-crud-app-minutes-angulars-resource/
i.e. The angular controller calls a service which calls the (symfony2) backend api endpoint.
The endpoint passes back items which is gets from a database. Items are then put into the view using ng-repeat (item in items).
This all works fine.
Now, I have a button (in the ng-repeat) which effectively causes a PUT request to be made to (another symfony2 endpoint), thus updating item.param1in the database. This also happens in the tutorial I linked to.
The problem is that (in my application and in the tutorial) I have to again make an api call which updates ALL the items, in order to see the change.
I want to just update the data in the view (immediately) for one object without having to fetch them all again.
i.e. something like:
$scope.items[4] = Items.get({id: item.id}, function(){});
Except the application array key isn't known so I cant do that.
(so something like: $scope.items.findTheOriginalItem(item) = Items.get({id: item.id}, function(){});.
Another possible solution (which seems like it may be the best?). I have looked here:
http://teropa.info/blog/2014/01/26/the-three-watch-depths-of-angularjs.html
And tried doing the equality $watch: $scope.$watch(…, …, true);. Thus using watch to see when the item sub-array is updated. This doesn't seem to update the data in the view though (even though it is updating in the database).
Any advice on the best way of doing this (and how to do it) would be great! Thanks!
Essentially the button click should use ng-click to execute a function and pass the item to that function. Example:
...ng-repeat="item in items"...
<button type="button" ng-click="updateItem(item)">Update</button
...
Then in the function you have the exact item that you want to update. If you are using $resources, it would be something like:
$scope.updateItem = function(item) { item.$update(...); };
Unless I didn't understand you

RESTAngular PUT not saving entire payload

I am trying to make a PUT request using RESTAngular. I am fairly new to Angular as well as RESTAngular.
Following is code snippet which works.
$scope.itemToUpdate = Restangular.all($scope.slug);
$scope.itemToUpdate.getList().then(function(items){
var item = items.one($routeParams.id);
item.name = $scope.singular.name;
item.description = $scope.singular.description;
item.put();
});
This doesn't work.
$scope.itemToUpdate = Restangular.all($scope.slug);
$scope.itemToUpdate.getList().then(function(items){
var item = items.one($routeParams.id);
item = $scope.singular;
item.put();
});
Don't know what am I doing wrong.
$scope.singular gets it data initially as following. Restangular.one('roles', $routeParams.id).getList().$object.
Basically idea is to update this model from form and also prepopulate the form with relevant data when slug matches the id. I can change the way things are wired up if required. So feel free to suggest best practices.
Edit 2
This official demo is very helpful in solving the issue.
http://plnkr.co/edit/d6yDka?p=preview
When Restangular returns resouce array\object it adds some methods on the object such as put which has been wired up to update the object on put call to server.
In second case you are assigning item=$scope.singular. $scope.singular may not be a Restangular object and hence does not work.
This official demo is very helpful in solving the issue. http://plnkr.co/edit/d6yDka?p=preview

How to clear a model when my AngularFire app loads initially?

I am new to AngularFire and am trying to understand a simple concept. If you go through the AngularFire tutorial located at the following url: http://angularfire.com/tutorial/index.html#gettingstarted there is a rudimentary example of using AngularFire to build a primitive "chat" application.
The tutorial is very clear and concise but I do not understand one primary point with it:
function MyCtrl($scope, angularFire){
$scope.messages = [];
var ref = new Firebase("https://<xxxxxx>.firebaseio.com/messages");
angularFire(ref, $scope, 'messages');
$scope.messages = []; //shouldn't this clear the data locally and remotely?
}
The issue is that a model is first created, and then the binding magic with AngularFire is setup such that there is now a 3-way binding to the model. If there is an array of data already stored in Firebase, that data is fetched and synced and your model will now have this data locally.
What I simply do not understand is, when the controller code runs, suppose I set the model to an empty array AFTER the angularFire binding is wired up, why doesn't the Firebase data get cleared out? Never mind the fact, that refreshing the page would basically keep wiping out the data (the behavior I want).
Now, I can get this behavior to work, if I wire up an ng-click event to a button, that calls a method named clear defined on my $scope object. If within, that method, I simply call: $scope.messages = [];, then my model is cleared locally, and remotely.
But why doesn't this work on initialization?
Help is always appreciated.
I think I may have found an answer to my own problem. It looks like, you must wait until the promise returns to actually start modifying the model like so. Now whenever I refresh my page, when my .then() runs, it will clear out my data.
I suppose this is how it should be done. Can anyone confirm?
$scope.messages = [];
var ref = new Firebase("https://<xxxxxx>.firebaseio.com/items");
var prom = angularFire(ref, $scope, 'messages');
prom.then(function(){
console.log("data loaded");
$scope.messages = [];
});
I'm guessing the remote data hasn't returned yet and is populated after your second call to $scope.messages =[]
edit:
Why not just explicitly remove your data from FB before binding it to a local list.
var ref = new Firebase("https://<xxxxxx>.firebaseio.com/messages");
ref.remove();

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;
}
}

How can I fill select2 data dynamically using angularjs

My case is that I want to use the createSearchChoice feature of the Select2 widget. So I found out I need to use an html input element instead of a select element and so I cannot use ng-repeat to populate the select2 widget. I find out there is a 'data' option and have been able to populate the select2 widget with static data, but not when I've tried to fill it dynamically.
What works:
html:
<input class='select2' ui-select2='sel2props' type='hidden'>
in the controller:
$scope.sel2props = {
createSearchChoice: ...
data: [
{ id: 0, text: 'yabba' }
etc
]
};
But if I try to set data to a variable which I can then set to whatever the database feeds me the widget isn't populated.
data: $scope.data;
function to retrieve data {
$scope.data = retrieved data;
}
the retrieved data is exactly in the way specified.
If i set up a button to append the data key it will work:
$scope.appenddata = function () {
$scope.data.push({id:1, text: 'anot'});
};
So I'm thinking it's a timing issue and I try $digest and $apply but they don't work in controllers. I tried to set up a directive and actually can do simple widgets, but not select2, so I was hoping not to go down that path, well that is to say I went down that path and drowned. If anyone could help out that would be great.
The solution is straight forward. Just push the elements onto the select2 data array rather than referencing another array.
function (result) {
$scope.lookupOptions.data.length = 0; // remove old items
angular.extend($scope.lookupOptions.data, result.data); // add new items
}
A trick I've recently made use of is to use Select2's query option to pass in your latest data on demand.
I've put together an example, wrapped in a custom directive. See this Plunk.

Resources