angularJS shows empty slots in array but it's not - arrays

It's weird and I don't have any idea of how to solve this issue...
I have my controller with an ajax call using a service with promise, which works great.
horariosOcupadosService.getHorariosOcupados($scope.formData.cmbUnidade, $scope.formData.cmbDiaSemana).then(function(response) {
//when I set my variable with the result, everything is fine
//and I can iterate with ng-repeat with no problem
$scope.horariosOcupados = response;
$scope.formData.qtdeHorarios = $scope.horariosOcupados.length;
}), function(error) {
console.log(response);
};
But in my HTML, I have a problem... after my ng-repeat, which works fine, if I try to print $scope.horariosOcupados ({{ horariosOcupados | json }}), it prints:
[{},{},{}]
It only shows something if I change the value of the ng-model field.
<div id="horarios">
<div ng-repeat="horarioOcupado in horariosOcupados track by $index" id="horario{{$index}}" class="form-group" style="margin: 15px; 0px;">
<div class="col-md-2 text-center" id="remove"><a ng-href="#" ng-click="removeHorario($index)"><i class="fa fa-trash-o fa-2x"></i></a></div>
<div class="col-md-3">
<div class="input-group">
<span class="input-group-addon">Horário inicial</span><input name="hrEntra{{$index}}" type="text" ng-model="horariosOcupados[$index].hrInicial">
</div>
</div>
</div>
</div>
<!-- GRADE DE HORÁRIOS - FIM -->
<pre>{{ horariosOcupados | json }}</pre>
Could anyone help me to figure it out?
Thanks!

You are probably doing something outside of the angular-world.
How do you fetch your data asynchronously ?
Do you maybe use a ajax call besides the angular built-in $http service ? or maybe with the "normal" setTimeout function instead of using the angular $timeout version ?
My guess is that you are getting it somehow asynchronously without staying inside the angular world. Therefore angular does not know anything about these changes and will not update the view accordingly in time.
What you can do for the moment is try
$scope.horariosOcupados = response;
$scope.formData.qtdeHorarios = $scope.horariosOcupados.length;
$scope.$apply();
to "tell" angular that something has changed hence angular will re-render the changes immediately.
Idealy though it's most of the times not necessary to do so if you stay inside the angular-world and use the angular built-in services

I figured it out. the problem was the maxlength of my INPUT field. The length of "1900-01-01 08:00" > 5, so angularJS set it to undefined.
Thank you all for helping me!

Related

AngularJS Scope not updating in view after async call

I am having trouble updating my scope on the front-end while making a request to an API. On the backend I can see that the value of my $scope variable is changing but this is not being reflected in the views.
Here is my controller.
Controllers.controller('searchCtrl',
function($scope, $http, $timeout) {
$scope.$watch('search', function() {
fetch();
});
$scope.search = "Sherlock Holmes";
function fetch(){
var query = "http://api.com/v2/search?q=" + $scope.search + "&key=[API KEY]&format=json";
$timeout(function(){
$http.get(query)
.then(function(response){
$scope.beers = response.data;
console.log($scope.beers);
});
});
}
});
Here is a snippet of my html
<div ng-if="!beers">
Loading results...
</div>
<p>Beers: {{beers}}</p>
<div ng-if="beers.status==='success'">
<div class='row'>
<div class='col-xs-8 .col-lg-8' ng-repeat="beer in beers.data track by $index" ng-if="beer.style">
<h2>{{beer.name}}</h2>
<p>{{beer.style.description}}</p>
<hr>
</div>
</div>
</div>
<div ng-if="beers.status==='failure'">
<p>No results found.</p>
</div>
I've tried several solutions including using $scope.$apply(); but this just creates the common error
Error: $digest already in progress
The following post suggested to use $timeout or $asyncDefault
AngularJS : Prevent error $digest already in progress when calling $scope.$apply()
The code I have above uses $timeout and I have no errors but still the view is not updating.
Help appreciated
I you are using AngularJS 1.3+, you can try $scope.$applyAsync() right after $scope.beers = response.data; statement.
This is what Angular documentation says about $applyAsync()
Schedule the invocation of $apply to occur at a later time. The actual time difference varies across browsers, but is typically around ~10 milliseconds. Source
Update
As others have pointed out, you should not (usually) need to trigger the digest cycle manually. Most of the times it just points to a bad design (or at least not an AngularJS-friendly design) of your application.
Currently in the OP the fetch method is triggered on $watch. If instead that method was to be triggered by ngChange, the digest cycle should be triggered automatically.
Here is an example what such a code might look like:
HTML
// please note the "controller as" syntax would be preferred, but that is out of the scope of this question/answer
<input ng-model="search" ng-change="fetchBeers()">
JavaScript
function SearchController($scope, $http) {
$scope.search = "Sherlock Holmes";
$scope.fetchBeers = function () {
const query = `http://api.com/v2/search?q=${$scope.search}&key=[API KEY]&format=json`;
$http.get(query).then(response => $scope.beers = response.data);
};
}
As the comments suggest, you shouldn't need to use $timeout to trigger a digest cycle. As long as the UX that elicits the change is within the confines of an angular construct (e.g. controller function, service, etc.) then it should manifest within the digest cycle.
Based on what I can infer from your post, you are probably using a search input to hit an API with results. I'd recommend changing the logic up such that you are triggering your search on an explicit event rather than the $watcher.
<input ng-model="search" ng-change="fetch()">
Remove the $watch logic and the $timeout wrapper.
function fetch(){
var query = "http://api.com/v2/search?q=" + $scope.search + "&key=[API KEY]&format=json";
$http.get(query)
.then(function(response){
$scope.beers = response.data;
console.log($scope.beers);
//it's a good habit to return your data in the promise APIs
return $scope.beers;
});
}
The reasons I make this recommendation is:
You have finer control of how the ng-change callback is triggered using ng-model-options. This means you can put a delay on it, you can trigger for various UX events, etc.
You've maintained a clearer sequence of how fetch is called.
You have possibly avoided performance and $digest issues.
Hey guys I solved the issue but I'm not sure exactly why this changed anything. Rearranging my code on JS Fiddle I just put all my partials into the index.html file like so and the requests and scope variables updated smoothly. Is was there perhaps a controller conflict with my html above?
<body ng-app="beerify" ng-controller='searchCtrl'>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container"><!-- nav bar code -->
</div>
</nav>
<!-- Main jumbotron for a primary marketing message or call to action -->
<div class="jumbotron">
<div class="container">
<h1>Title</h1>
<form ng-submit="fetch()">
<div class="input-group">
<input type="text" ng-model="search"
class="form-control" placeholder="Search the name of a beer" name="srch-term" id="srch-term">
<div class="input-group-btn">
<button class="btn btn-default" type="submit"><i class="glyphicon glyphicon-search"></i></button>
</div>
</div>
</form>
</div>
</div>
<div class="container">
<div ng-if="!beers">
Loading results...
</div>
<div ng-if="beers.status==='success'">
<div class='row'>
<div class='col-xs-8 .col-lg-8' ng-repeat="beer in beers.data track by $index" ng-if="beer.style">
<!-- ng-if will make sure there is some information being displayed
for each beer -->
<h2>{{beer.name}}</h2>
<h3>{{beer.style.name}}</h3>
<p>AbvMin: {{beer.abv}}</p>
<p>AbvMax: {{beer.ibu}}</p>
<p>{{beer.style.description}}</p>
<hr>
</div>
</div>
</div>
<div ng-if="beers.status==='failure'">
<p>No results found.</p>
</div>
</body>

Angularjs get data filtered in ng-repeat from controller

I have tried everything but I can't get it.
What i need is to access data filtered in ng-repeat from controller.
Data in ng-repeat is taken from a $http call.
$http.get('http://localhost:/test/test.php').success(function(data) {
$scope.registros = data;
});
and this is the view
<div ng-repeat="registro in (filteredregistros = (registros| filter:userSearch | filter:datefilter | filter:greaterThan('ID', greatersearch) | orderBy:'-ID'))">
{{registro.ID}}{{registro.date}}
<div class="rowboxdata ng-animate">
<div class=""><div class="name-business">{{registro.Name}}</div></div>
<div class=""><div class="name-business">{{registro.Phone}}</div></div>
<div class=""><div class="name-business">{{registro.Email}}</div></div>
<div class=""><div class="name-business">{{registro.Name}}</div></div>
<div class=""><div class="name-business">{{registro.City}}</div></div>
<div class=""><div class="name-business">{{registro.Service}}</div></div>
</div>
</div>
I have tried access data filtered from my controller with $scope.filteredregistros as i look in other post, but it didn't work to me.
What I'm trying to do, is to get data filtered and then, send it through ajax to php.
Would be nice some help thanks.
EDITED
Finally, I have found what I needed with this example;
In the View:
<input type="button" ng-click="results(filteredregistros)" />
In the Controller:
$scope.results = function (filteredregistros) {
console.log(filteredregistros);
// your ajax code
};
And if you want to get it only in the view, then do it as Per Hornshøj-Schierbeck says:
filteredregistros: {{filteredregistros | json}}
Thanks for the help Per Hornshøj-Schierbeck
Try adding
filteredregistros: {{filteredregistros | json}}
in your HTML somewhere. It should be clear if it contains data or not. Piping it through json will display the data in json format, which is nice for debugging the value inside.
The reason why you might not see if, when you do console.log is, that depending on when you console.log it, your http request might or might not have run and the data might not be filtered yet.

Trigger a click on first item of ng-repeat, initially and each time new data is loaded

I'm very very new to Angular JS and have the following requirement:
I have an ng-repeat as shown below:
<div class="panel" ng-repeat="(appname, value) in chart.accordionData" style="margin-top: 0;">
<div class="accordion collapsed" data-parent="#accordion1"
data-toggle="collapse"
data-target="#{{appname.replace(' ','')}}">
<div class="accordion-head" initial-select index="{{$index}}">
<div class="arrow chevron"></div>
<h4><i></i>{{appname}}</h4>
</div>
</div>
<div class="accordion-body collapse" id="{{appname.replace(' ','')}}">
<p class="highlightable" >
Some data
</p>
</div>
</div>
The data in this ng-repeat comes from the server.
So the problem statement I have is to perform a click trigger on the first element under ng-repeat, .accordion-head so that the first item in the list is always open. I tried various approaches of putting $watch etc. but when the number of items in the list are same, then the trigger doesn't fire.
(the first item in list has to be clicked even when new data is loaded)
I thought of writing a directive initial-select and perform click based on index but that happens only once. I really need an experts advice.
Any solutions?
I think the simplest option is to use data-ng-init="collapse = !$first;".
Another option:
Have you used Angular UI Bootstrap? Also, are you using $http or $resource to get data from the server?
If you use $resource, you can set a variable on the success callback (interceptor response) to open=true. Then, you can either bind this variable to Angular UI Bootstrap is-open attribute or to your data-toggle. Also, with $resource you can set data-ng-if="chart.accordionData.$resolved" (or chart.$resolved depending on your setup), which is nice.
You can use a watch to watch the array contents.
Check out the following fiddle:
http://jsfiddle.net/akonchady/w0qdsen7/3/
I have used a variable to keep track when the array content changes since directly watching the array will be more memory intensive.
Here is the main code that watches the variable:
$scope.$watch('arrModified', function(newValue, oldValue) {
if(newValue) { //array is modified
//Trigger ng-repeat div click
setTimeout(function() {
$('.item:first').trigger('click');
$scope.arrModified = false;
$scope.$digest();
}, 0);
}
});

AngularJS with WebRTC and Socket.IO scope issues

I'm currently working on a video-conference call application with AngularJS, WebRTC and Socket.IO. I've stumbled upon an issue and am not sure how to fix it.
Basically I have a list of rooms which is looped through by an ngRepeat and then a directive is included. The html looks as following:
My rooms:
<div class="room" ng-repeat="room in userRooms">
<div class="title">{{room.roomId}}</div>
<div class="content">
<conference-call></conference-call>
</div>
</div>
The conference-call directive includes a template with the following html:
<!-- local stream -->
<div class="clients-wrapper" ng-repeat="client in room.clients | filter: getMyId()">
<div class="client-wrapper">
<video ng-src="{{localVideoURL}}" muted autoplay></video>
<div class="display-name">socket-id: {{client}} (local), stream-id: {{localStream.id}}</div>
</div>
<div style="clear:both;"></div>
</div>
<!-- remote stream -->
<div class="clients-wrapper" ng-repeat="client in room.clients | filter: '!'+getMyId()">
<div class="client-wrapper">
<video ng-src="{{peerVideoURL}}" autoplay></video>
<div class="display-name">{{client}} (remote)</div>
</div>
<div style="clear:both;"></div>
</div>
Populating the local video URL goes as follows:
function handleUserMedia(stream) {
scope.localStream = stream;
scope.localVideoURL = $sce.trustAsResourceUrl(URL.createObjectURL(stream));
scope.$apply();
};
and for the remote video URL the following code is executed:
function handleRemoteStreamAdded(event) {
scope.remoteStream = event.stream;
scope.peerVideoURL = $sce.trustAsResourceUrl(URL.createObjectURL(event.stream));
scope.$apply();
};
In a later stage this will be replaced by something like peerVideos.append(stream). But that's not what this is about. Because here's the problem:
pc.onaddstream (the native WebRTC-function which is called when a
client joins) where pc = new RTCPeerConnection(pc_config,
pc_constraints); is never called.
I think this is because of the following:
Whenever someone joins the room the server emits a call which on the client side results in a $scope.$apply() call (the AngularJS way to update and refresh the scope and re-render the page). This makes sure the client sees the remote client in the room he's in. However, because of this $scope.$apply() call the whole conference-room directive is refreshed and re-initialized. This causes the problem where pc.onaddstream is never called because no-one's ever added to the peer connection, because it is re-initialized, or at least I think this is what happens.
Has anyone of you had a similar problem? And how should I approach this issue?

Avoid - ng-repeat to download the file each time in the loop

I tried to find the answer for this question but couldn't find any.
I am using bs-popover and i am using the popover code inside an ng-repeat ,
<div ng-repeat="i in [1,2,3,4.....100]">
<a data-title="test popover" data-template="/test/test.html" bs-popover trigger="click" placement="left">Click Me for Test </a>
</div>
Problem is since the popover code is in ng-repeat , the file is getting downloaded in for 100 times (n times the loop executes) as seen in the networks tab of the browser
Is there any way to avoid this issue? Is it possible to load the file only on click?
Any help will be appreciated. Thanks in advance.
Perhaps instead of passing in the file in the markup you could do it via JavaScript. So something like:
<div ng-repeat="i in [1,2,3,4.....100]">
<a data-title="test popover" bs-popover ng-click="popover($event)">Click Me for Test </a>
</div>
And then from a controller
$scope.popover = function($event){
angular.element($event.target).popover({
template: '/test/test.html',
placement: 'left'
});
}
you should force your html into the cache get it the first time through $hhtp and then put it in an instance of $cacheFactory.cache so the next $http calls to that url are served from there,
https://docs.angularjs.org/api/ng/type/$cacheFactory.Cache

Resources