My question comes from the selected answer here
<ui-select-choices repeat="hero in getSuperheroes($select.search) | filter: $select.search">
<div ng-bind="hero"></div>
works perfectly fine with:
$scope.getSuperheroes = function(search) {
var newSupes = $scope.superheroes.slice();
if (search && newSupes.indexOf(search) === -1) {
newSupes.unshift(search);
}
return newSupes;
}
However, when I put a breakpoint on $scope.superheroes I see its called as many times as my data. Data is very large. Its fetched from a $http.get() request and I'm handling not loading the data in the textbox all at once.
However, I don't want to call this until I actually start typing anything in the textbox. I tried to call getSuperheroes using
on-select="getSuperheroes($select.search)"
and similarly
refresh="getSuperheroes($select.search)"
but they do not help allowing manual entry of data.
What do I use to call the function and yet get the task done just like how it works in the referenced answer?
I recommend you to do this:
In order to improve performance get rid of repeat="hero in getSuperheroes($select.search) | filter: $select.search", since if your data doesn't change the function getSuperheroes will be triggered (anyway) every time a $digest is executed (no matter the var newSupes changes or not in getSuperheroes) (that's wahat's happening right now!). Instead put (for instance) repeat="hero in myHeroes" and then in your getSuperheroes func do this:
$scope.getSuperheroes = function(search) {
var aux = $scope.superheroes.slice();
if (search && aux.indexOf(search) === -1) {
aux.unshift(search);
}
$scope.myHeroes = aux;
}
Keep the refresh="getSuperheroes($select.search)", this will search the new values by calling getSuperheroes which will update the $scope.myHeroes var and then it will be refreshed in the view.
Add the property refresh-delay="500" (500 is just an example, put whatever fits your needs). This will delay the search that amount of milisecond. This is useful when you do not want start searching immediately. For instance if the user wants to search "Superman" and she/he type "Sup" in less than 500 milisec the search will start with the string "Sup". If you do not set that property the search would be executed 3 times (one for "S" another for "Su" and a third one for "Sup")
PS:
-- If you want to get the ui-select with some values initially, You must call the getSuperheroes func on your controller start.
-- You may need to declare $scope.myHeroes = []; at the beginning of your controller (it depends on your controller implementation).
-- You might want to read more info about $digest on the AngularJS official doc, on SO there are some related posts:
How exactly does the AngularJS Digest Loop work?
Why do i have to call $scope.$digest() here?
Angular $scope.$digest vs $scope.$apply
$apply vs $digest in directive testing
Related
In my website I'm Showing my database after user has given the database name, Is there any way I can constantly update the web shown databasebase without refreshing the page . I've tried using setInterval but it's not working for some reason .
function c(){
setInterval(beta, 1000);
}
function beta(){
var d = document.getElementById("opopo").value;
var firebaseRefff= firebase.database().ref('LOCATION/'+d);
firebaseRefff.on('child_added', snap=> {
var slot=snap.getKey();
var alloted=snap.child("ALLOTED").val();
var date=snap.child("DATE").val();
var limit=snap.child("LIMIT").val();
var time=snap.child("TIME").val();
$("table tbody").append(""+slot+""+alloted+""+date+""+limit+""+time+"Null");
});
}
You do not need, and should not use, setInterval to trigger the queries. What you have in your beta() function looks pretty good.
firebaseRefff.on('child_added', snap => {}) means "whenever a child is added under this location, trigger the callback function (empty in my example) with the parameter 'snap'". It will also be called once, initially, for each child that is already at that database reference location.
You need to make sure you've called beta() once to setup this trigger.
If you're still having problems, you might want to insert logging to make sure beta() is being called, what the full reference path is, if the callback is ever triggered, and if your jquery string is correct.
If there is a way in ui-grid that I can know a grid is finish updating the rows?(like a filter is being applied etc)? I want to run some function after the grid view changes.
I tried the following method:
$scope.filteredRows = $scope.gridApi.core.getVisibleRows($scope.gridApi.grid);
$scope.$watch('filteredRows', function(){console.log('view updated');});
The above approach works when the grid just finish initiating, after that, it won't work anymore.
I also tried using the filterChanged api:
$scope.gridApi.core.on.filterChanged($scope, function() {
console.log('filter changed');
foo();
});
The problem with this method is that although I can get notified when the filter is changed, but if the grid is very large, it needs some time to finish updating the view, and before that, the function foo() is being called before the grid update is finished.
Any idea will be appreciated.
I've seen use of $scope.grid.api.core.on.rowsRendered( $scope, $scope.col.updateAggregationValue ); in ui-grid-footer-cell.js. I'm not sure exactly when rowsRendered fires, but given it's being used to calculate aggregations and aggregations require knowledge whenever the rows are changed, and must run after the rowsProcessors finish running, there's a good chance that it's what you want.
EDIT: the framework to use it would be:
Define a function that you want to call when the visible rows have changed
var myFunction = function() {
do some stuff
};
Set this function to be called whenever rows are rendered
$scope.gridApi.core.on.rowsRendered( $scope, myFunction );
Well, I found a workaround, in order to call the function after the grid is updated, which takes some time, I added a delay in filterChanged event:
$scope.gridApi.core.on.filterChanged($scope, function() {
console.log('filter changed');
$timeout(foo(),800);
});
To use the $timeout, you need to add that to your controller first.
I currently have a few ng-repeats in my view for building an agenda. Whenever I have a ng-repeat I can include a directive checking if the ng-repeat is done to trigger a method. Works perfect...
The problem is, I have like 5 ng-repeats, I don't want to include the directive for all 5 ng-repeats and check in the method if all 5 have called the method...
I just want a way to detect if all my ng-repeats (and other angular stuff) is finished building the view so for example I can put appointments in the agenda via JQuery.
Because ofcourse, putting appointments in the agenda (divs) before the agenda (divs) have been created won't work.
Thanks in advance!
UPDATE:
I'm using a REST backend to get the appointments from. So i can't just retrieve the appointments and try to show them in the agenda because the view might not be finished generating (using the ng-repeats)....
So I need something that is triggerd ones the view is done generating (ALL ng-repeats must be done) so i can start placing the appointments.
You should use $viewContentLoaded rather than any directive as this fires after everything in the view loaded.
Documentation :
https://docs.angularjs.org/api/ngRoute/directive/ngView
Code Example :
$scope.$on('$viewContentLoaded', function(event) {
// Your Code Goes Here
});
Note that this will only work if you are using ng-view from routing.
thereĀ“s a trick in Angular using a $timeout to delay execution of certain parts of the code until all directives have redered. I'm not sure it will work in this instance but you could try.
In your controller simply add
$timeout(function() {
// code to execute after directives goes here
});
You're probably better off not using jQuery to populate the agenda, but using Angular instead. This allows you to just wait until the data is loaded, which is much easier to detect, and Angular will make sure the DOM gets uploaded whenever it can do so.
In your case you can probably do something like this:
Controller:
$scope.days = [];
//Here we create the days for the calendar (7 in this case)
for (var i = 0; i < 7; i++) {
var hours = [];
//and the hours, with an empty appointment array for each hour
for (var i = 0; i < 24; i++) {
hours.push({ appointments: [] });
}
$scope.days.push({
hours : hours
});
}
//Then we can get the appointments from your api
getAppointments().then(function(appointments) {
//and add the results to the arrays created above
appointments.forEach(function(appointment) {
//This is some simplified logic that only uses the day of the week
//and hour of the appointment. Your logic would probably a bit more complex
//to properly put the appointment in the correct array
var day = appointment.date.getDay();
var hour = appointment.date.getHour();
$scope.days[day].hours[hour].appointments.push(appointment);
});
});
Template:
<div class="days" ng-repeat="day in days">
<div class="hours" ng-repeat="hour in day.hours">
<!-- I assume you have one hours div in which all appointments for that hour will go -->
<div class="appointments" ng-repeat="appointment in hour">
{{ appointment.title }}
</div>
</div>
</div>
That said, if you really want to detect when the view has finished loading then you have a couple of options:
Wait for all your data being loaded and then use a $timeout to ensure it has been rendered.
It would look something like this:
var somePromise = getPromise();
var someOtherPromise = getOtherPromise();
$q.all([somePromise, someOtherPromise])
.then(function() {
//At this point all data is available for angular to render
$timeout(function() {
//and now everything should actually be rendered
});
Listen for the $viewContentLoaded, but this only works if you use ng-view and might fire too early if your data is loaded asynchronous (I'm not entirely sure about the details here, since I usually avoid detecting when the view is loaded).
If all of the above fails you could just continuously check if the desired elements are loaded on the page.
You can use angularjs's directive to make it. With directive you can set the ready event for any dom element.
For more detail, refer to this great post and question.
In angular we have an enrich method which runs some rest call to enrich a data object, then sets a variable which will cause a hidden details tab to be visible. Something like this overly simplified example:
$scope.enrich = function(team){
angular.forEach(team.members, function(member){
member.getSkills().then(function(skills){
member.skills=skills;
}
});
$scope.enrichFinished=true;
};
I'm getting exceptions in the detail pane which is opened when enrichFinished is true. The exceptions appear to be due to a filter which attempts to filter on member.skill and discovers skill is undefined for the member. I assume the problem is that we open the detail tab as soon as enrichFinished is set, which is before the then clause that sets member.skills=skill; thus we have a datarace where skills's may not yet be set by the time we try to filter on it.
What is the cleanest way to tell angular to wait to run the filter in the detail's tab until after I have actually generated and saved the data I need? The actual enrich method enriches 3-4 different variables within the for loop.
You can set $scope.enrichFinished=true; after all promisses solve, so you can use $q.all like this
$scope.enrich = function(team){
$q.all(team.members.map(
function(member){
return member.getSkills().then(
function(skills){
member.skills=skills;
}
);
}
)
).then(function(){
$scope.enrichFinished=true;
});
};
I have a list linked to a store filled with Facebook friends. It contains around 350 records.
There is a searchfield at the top of the list which triggers the following function on keyup:
filterList: function (value) {
// console.time(value);
if (value === null) return;
var searchRegExp = new RegExp(value, 'g' + 'i'),
all = Ext.getStore('Friends'),
recent = Ext.getStore('Recent'),
myFilter;
all.clearFilter();
recent.clearFilter();
// console.log(value, searchRegExp);
myFilter = function (record) {
return searchRegExp.test(record.get('name'));
}
all.filter(myFilter);
recent.filter(myFilter);
// console.timeEnd(value);
},
Now, this used to work fine with ST2.1.1 but since I upgraded the app to ST2.2. It's really slow. It even makes Safari freeze and crash on iOS...
This is what the logs give :
t /t/gi Friends.js:147
t: 457ms Friends.js:155
ti /ti/gi Friends.js:147
ti: 6329ms Friends.js:155
tit /tit/gi Friends.js:147
tit: 7389ms Friends.js:155
tito /tito/gi Friends.js:147
tito: 7137ms
Does anyone know why it behaves like this now, or does anyone have a better filter method.
Update
Calling clearFilter with a true paramater seems to speed up things, but it's not as fast as before yet.
Update
It actually has nothing to do with filtering the store.
It has to do with rendering list-items. Sencha apparently create a list-item for every record I have in the store instead of just creating a couple of list-items and reusing them
Could there be an obvious reason it's behaving this way ?
Do you have the "infinite" config on your list set to true?
http://docs.sencha.com/touch/2.2.0/#!/api/Ext.dataview.List-cfg-infinite
You probably don't want the list rendering 300+ rows at once, so setting that flag will reduce the amount of DOM that gets generated.