AngularJS + Hoodie: How to do a correct data flow? - angularjs

I would like to combine AngularJS and Hoodie, but I am not sure how to do it the correct way.
Let's say I have a basic AngularJS controller
app.controller("c", function($scope) {
$scope.table = [];
$scope.onAdd = function(newEntry) {
$scope.table.push(newEntry);
};
});
After clicking on e.g.
<button ng-click="onAdd('test');">add</button>
Everything works as expected. But now I want to use Hoodie. Currently I am trying this:
app.controller("c", function($scope) {
$scope.table = [];
$scope.onAdd = function(newEntry) {
$scope.table.push(newEntry);
};
hoodie.store.on('entries:add', $scope.onAdd);
});
and anywhere: hoodie.store.add("entries", tmpObject);
The onAdd() gets called and is inserted into the table array (verified in the developer console), but my html table (via ng-repeat="entry in table") is not updated.
First question: Do you know why? What do I think wrong here?
Second question: What would be a good way to combine Hoodie and AngularJS? Would it be better to $watch on the table and insert items into it and store it via Hoodie after getting a change or the other way around and add a new item to the hoodie.store and then on("entries:add") do some Angular stuff?
Thanks in advance!

Heyho =)
First: If you update data-structure via an 'external' event(in this case hoodie), you need to trigger the Digest-Cycle e.g. $rootScope.$apply();
Second: There is a hoodie-angular-plugin that is written by Elmar and me. That solved the most of your basic tasks. That would be the easiest way in this case.

Related

Using ng-repeat with the Pokeapi

I'm trying to get the data for two random pokémon from the Pokéapi. I can grab the data fine, but my angular $scope variables are overwriting the first pokémon with the second pokémon, instead of storing both:
$http.get(endPoint)
.then(function(res){
$scope.pokemonName = res.data.name; // only one 'pokemonName' in $scope
});
You can visually see the pokémon being loaded and displayed incorrectly (two identical pokémon instead of two different ones) in this Plunker example.
I could manually declare two separate $scope variables, eg:
$scope.pokemonNameOne;
$scope.pokemonNameTwo;
But I want to use ng-repeat in the HTML markup, like this:
<div class='pokemon' ng-repeat="pokemon in pokemonPair">
<h2 class='name'>{{ pokemonName }}</h2>
<img src='{{ pokemonImage }}' />
<!-- etc -->
</div>
I feel like what I should be doing, is looping through the required data from the API, then pushing it into a new array of my own creation, and then using my new array in combination with ng-repeat in order to display the two random pokémon. However, I'm unsure as to whether this is an overly convoluted solution. Should I really be re-creating the API data in my own array? It feels like that would be reinventing the wheel. However, I'm not sure how else to solve this problem besides 'Don't use ng-repeat', which isn't a route I want to go down if I can help it, as I'm trying to learn Angular.
Should I be pushing the API data into an array of my own making in order to display it correctly, or is there a smarter way to use ng-repeat?
You are replacing the existing variables, instead of pushing it to an array. Do something like this:
function getStats(pokemonIndex){
var pokemon = $scope.pokemonPair[pokemonIndex];
$http.get(pokemon.endPoint)
.then(function(res){
pokemon.pokemonName = res.data.name;
pokemon.pokemonExperience = res.data.base_experience;
pokemon.pokemonImage = res.data.sprites.front_default;
pokemon.pokemonStats = res.data.stats;
console.log("pokemonName = " + pokemon.pokemonName);
});
}
and call it like this:
function populateStats(){
for (var i = 0; i < $scope.pokemonPair.length; i++){
getStats(i);
}
}
See this edited plunker
Also notice that I have changed the pokemonPair property to be an array of objects, to be able to add the properties to it and use the ng-repeat.
Here is a working plunker:
https://plnkr.co/edit/c5seK6wr8rQjMAj5GZA9?p=preview
Just push the response from the api into an array and databind everything from there!
function getStats(endPoint){
$http.get(endPoint)
.then(function(res){
$scope.pokemons.push(res.data);
console.log("pokemonName = " + $scope.pokemonName);
});
}
You have to read about Arrays in JavaScript. Your question doesn't actually related to Angular itself.
$scope.pokemonNames = [];
$http.get(endPoint)
.then(function(res){
var pokemon = {};
pokemon.name = res.data.name;
pokemon.attribute = res.data.attribute;
$scope.pokemonNames.push(pokemon); // then iterate over pokemonNames array in ng-repeat.
});

AngularJS synchronisation between $scope and $localStorage

Slightly new to AngularJS so please bear with me.
I'm trying to implement ngStorage but had a doubt. If I assign something like this:
$scope.$storage = $localStorage.default({
var: 'foo'
});
And then if $scope.storage.var is changed when an AJAX call is made, does it also change the value of $localStorage.var or do I have to manually re-assign it?
If I do have to manually re-assign it, what's the best way to do so among the options below? (Please do let me know if there's any other way to do it)
$localStorage.var = 'foo2';
$localStorage.$reset({
var : 'foo2'
});
delete $localStorage.var followed by $localStorage.var = 'foo2'
Thanks in advance.
I just used the third option and it seemed to work correctly. That is:
delete $localStorage.var;
$localStorage.var = 'foo2';

finding it difficult to test AngularJS UI Grid with protractor

I am pretty much new to protractor and testing AngularJS is my first time. I have to test a table which is populated with Angular grid. These values don't have any unique id's to identify them. After doing some research, i found we need to use gridTestUtils.spec.js. I have tried this in my spec file as below:
var gridTestUtils = require('./gridTestUtils.spec.js');
describe('get row count of grid', function(){
it('get the row count of grid1', function(){
gridTestUtils.expectRowCount('grid1',8);
});
});
This is the only spec file and there is no pageobject file. But this code doesn't seem to run. it says pageObject is not defined. Can you please let me know how to proceed from here and i am not an expert. please answer in simple and detailed manner as it will be easy for me to understand.
Thank you for your help.
Thanks,
Mallesh
gridTestUtils definitely feels like it may be overkill. Have you thought about testing the page with lower level calls to protractor directly? It's not a lot of code.
describe('get row count of grid', function () {
before(function () {
browser.get('http://localhost:9000/orwhatever');
});
it('get the row count of grid1', function(){
expect(element.all(by.repeater('row in rows')).count()).to.eventually.equal(8);
});
});
Replace the url with the url of your app, and replace the repeater string with the same string that is in ng-repeat in your DOM. If you're not using a repeater, just use the $$() function to grab all elements by css-selector instead.

how to force angular to re-bind/update even there is no change in the model

Ok -- so you might be thinking why would you want this but I am trying to render some HTML using ng-html-bind like so (in HAML):
#my-visualization-panel{'ng-bind-html' => 'htmlSource'}
the htmlSource has some html which renders a visualization using c3.js visualization library. The htmlSource looks something like this
<script>
var MY_DATA = localStorage.getItem('MY_DATA');
c3.generate({
data: {
columns: MY_DATA
}
});
</script>
So the problem is that I update the visualization by re-setting localStorage['MY_DATA']. However, while the data that MY_DATA refers to might change, the actual htmlSource does not, so the view fails to update.
Is there a way to force the view to update even if the model, ostensibly, does not?
you can use the apply method of the $scope object:
$scope.apply();
if you are still getting the digest in progress errors, you can also make use of the $timeout object that will run the function in the next digest cycle:
$timeout(function() {
//code
});
as per your latest comment in this answer it seems to be that you are looking for the $scope.watch method. You can add a watcher in order to listen when something changes.
I would recommend changing the HTML anyway. For example
<script>
var MY_DATA = localStorage.getItem('MY_DATA');// 2482486284968248968 (hash of my_data, guid, or serial number)
c3.generate({
data: {
columns: MY_DATA
}
});
</script>
This will allow your script to operate as you expect, directly, without having to do big sweeping updates or hacks. Even if you "force a refresh", if the HTML doesn't change, Angular will not re-execute your script.
Another possibility is to call localStorage.getItem('MY_DATA') in your angular controllers / directives, instead of indirectly hoping Angular will run it for you via HTML updates. That seems to be the kind of control you're looking for.
I have spent around 2-3 hours to resolve this issue. Finally I found solution
$scope.$apply();

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

Resources