I'm using an external filtering service and ngGrid to display my data. There are some controls (text box, search buttons) that I want be hidden until the promise to fill my table data is resolved, but only on the page load. After the page is loaded, I want the static HTML external to ngGrid to remain visible. I currently have a flag like
//first line in controller
$scope.isUpdating = true;
var getData = function(){
$scope.isUpdating =true;
ngGridDataPromise.then(function(){//operations..})
.finally(function(){ $scope.isUpdating = false });
}
So I have a function that, as its first action is sets isUpdating = true, and a promise inside of that function whose finally will set isUpdating = false. The problem I have is that as that request is happening, there's a small time window where the promise isn't resolved and the ng-show I have on my template doesn't show the controls I need.
HTML
<div ng-show="!isUpdating"><buttons></buttons></div>
How can I make the isUpdating binding only care about the initial page load, or are there some more framework type facilities that I can take advantage of for this? Angular version - 1.2.26
EDIT: using another external dependency is not an option, I'm looking for an angular way, or a clever JS solution.
I'm not 100% sure I follow what you're asking but I believe you want the controls hidden the first time getData is called until it is fully resolved but not for subsequent requests on that same session? If so, I believe this would work:
//first line in controller
$scope.isUpdating = true;
var firstLoad = true;
var getData = function(){
if(firstLoad) {
$scope.isUpdating = true;
firstLoad = false;
}
ngGridDataPromise.then(function(){//operations..})
.finally(function(){ $scope.isUpdating = false });
}
Related
I am trying to create a search box that is supposed to do the following when the input value changes or if the user clicks the search icon.
Set the value of searchText field. This is used for searching and
also, controls which icon (search | clear) to show next to the search input box.
Before searching, the value of the field previewTableLoading is set
to true which is used in the template to display a spinner.
After a time-consuming search process, the value of the field
previewTableLoading is set back to false to stop the spinner.
Here's the template for the search box -
<div class="ui icon input">
<input type="text" (change)="onSearchFieldChange($event)" placeholder="Search Fields..." #searchInput>
<i class="search link icon" (click)="searchInput.change()" *ngIf="!searchText || searchText === ''"></i>
<i class="times link icon" (click)="clearSearch()" *ngIf="searchText && searchText !== ''"></i>
</div>
And here is the implementation code -
onSearchFieldChange(event) {
this.searchText = event.target.value;
this.search();
}
search() {
this.previewTableLoading = true;
// SOME HEAVY SEARCHING ALGORITHM THAT TAKES TIME
this.previewTableLoading = false;
}
clearSearch() {
this.searchText = '';
this.search();
}
PROBLEM:
When the input value changes, the value of the field previewTableLoading isn't updated instantaneously. It waits for the entire search method to complete. That is why my spinner which is bound to that field never shows up. I feel like the method call from the template is acting synchronously and all the updates within that method are only coming into effect when the method completes.
I hope I was clear explaining my issue. It'd be great if anyone can help me with understanding the issue. Thanks.
This would be a great time to use an Observable.
I'm not sure how you are doing your search but lets say you are calling some service that you have which returns an observable (i.e. a http request)
onSearchChange(event) {
this.search(event.target.value);
}
search(searchTerm) {
this.previewTableLoading = true;
this.searchService.search(searchTerm)
.subscribe( (results) => {
//this is the result set that we get back
this.resultSet = results;
this.previewTableLoading = false;
});
}
Your changes will not reflect on UI while your script is running. As a simple test, have an infinite loop and update background color of an element before entering the loop. You will not see the changes. It wouldn’t reflect the changes until function done execution. A solution would be
search() {
this.previewTableLoading = true;
setTimeout( () => { // Causes Angular change detector to kick in
// SOME HEAVY SEARCHING ALGORITHM THAT TAKES TIME
this.previewTableLoading = false;
}, 0);
}
This will let the changes reflect before searching. Also, simply setting the previewTableLoading to true inside a setTimeout works as well.
I have a date input in my page, which I'm using Daterangepicker framework to populate it.
Here is the code of how I start my page!
$(function(){
startSelectors();
var variaveis = returnInputVars();
var rede = variaveis[0];
var codLoja = variaveis[1];
var period = variaveis[2];
console.log('1.'+rede+' 2.'+codLoja+' 3.'+period);
});
function returnInputVars(){
var rede = $("#dropdown-parceria").val();
var codLoja = $("#dropdown-loja").val();
var periodo = $("#datepicker-range").val();
return [rede, codLoja, periodo];
};
The function startSelectors() is set to start my datepicker and other fields, which is working perfectly. After it, I create a var called "variaveis" to fill
with the values of each field because I will use then later (this functions also works perfectly at other scripts of my page).
Running the page, my console returns this:
The funny thing is, if I type at the console this, the value is shown, just while starting the script is does not work!
Anybody experienced something like this?
***UPDATE
Adding this script to my start function:
console.log($("#datepicker-range"));
The value is shown, but the second console.log don't:
EDIT 1. FIDDLE (Suggested by #halleron)
To ensure things are loaded in the correct order, it is useful to apply a page sniffer code snippet that will scan the page continuously until a condition is met, or until a preset counter limit is reached (to prevent strain on browser memory). Below is an example of what I typically use that would fit your scenario.
I think because you are dealing with asynchronous loading, you can't have a global variable that holds the values in a global scope without an interval to detect when it can be used. Otherwise, it will attempt to read the variable when it is not yet ready.
You can invoke functions anywhere you like. But I would keep all of your variables contained within the page_sniffer_2017() because that is a controlled environment where you know that everything successfully loaded and you know that the variables are ready to be accessed without error.
That way, regardless of connection speed, your functions will only fire when ready and your code will flow, sequentially, in the right order.
Within the ajax success options, always add a class to the body of the document that you can search on to determine if it has finished loading.
$(document).ready(function() {
page_sniffer_2017();
});
function page_sniffer_2017() {
var counter = 0;
var imgScanner = setInterval(function() {
if ($("#datepicker-range").length > 0 && $("#datepicker-range").val().length && jQuery('body').hasClass('date-picker-successfully-generated')) {
var periodoDatepicker = $("#datepicker-range").val(); // ok
console.log(periodoDatepicker); // ok
var variaveis = returnInputVars(replaceDate(periodoDatepicker)); // ok
console.log(variaveis[0], variaveis[1], variaveis[2]);
//startNewSelectors(variaveis);
// start ajax call
generateData(variaveis[0], variaveis[1], variaveis[2]);
clearInterval(imgScanner);
} else {
//var doNothing = "";
counter++;
if (counter === 100) {
console.log(counter);
clearInterval(imgScanner);
}
}
}, 50);
}
So, my instructor thought it would be a great idea to throw in local storage and all that great nonsense the second week into learning Angular JS and basically told us to copy her code but change the names (as if that's learning). But anyways I have no idea how to work with angular js except for a few concepts and I need help finding the issues in my code. Any input would help. So far it looks as if the information from the form isn't being inputed to the html where it will be displayed. Here is my js fiddle. Any input is greatly appreciated
http://jsfiddle.net/g3tg5L15/1/
var app = angular.module('myApp', []);
app.controller("EmployeeController", function($scope, DataService){
$scope.empInfo = DataService.getEmpInfo();
$scope.newempInfo = {};
$scope.addNewEmpInfo = function(){
DataService.saveEmpInfo($scope.newempInfo.employee, $scope.newempInfo.street,
$scope.newempInfo.city,$scope.newempInfo.state, $scope.newempInfo.zip);
$scope.newempInfo = {};
};
$scope.removeEmpInformation = function(index){
DataService.removeEmpInfo(index);
};
$scope.clearInfo = function(){
DataService.destroyLocalStorage();
};
});
angular.module('myApp').service("DataService", function(){
var empInfoArray = [];
this.getEmpInfo = function(){
var employeeInfoArray = JSON.parse(localStorage.getItem("employeeInformationLS")) || [];
var empInfoArray = employeeInfoArray;
return empInfoArray;
};
this.saveEmpInfo = function(aName, aStreet, aState, aCity, aZip){
var savedEmpInfo = {employee : aName, street : aStreet, state : aState, city : aCity, zip : aZip};
empInfoArray.push(savedEmpInfo);
localStorage.setItem("employeeInformationLS", JSON.stringify(empInfoArray));
};
this.removeEmpInfo = function(aIndex){
empInfoArray.splice(aIndex, 1);
localStorage.setItem("employeeInformationLS", JSON.stringify(empInfoArray));
};
this.destroyLocalStorage = function(){
empInfoArray.splice(0);
localStorage.clear();
};
});
The main reason for the lack of response and debugging ability is due to AngularJS not loading correctly. For it to load you must change the dropdown in the left menu bar of jsFiddle from onLoad to No wrap - in <body> to load Angular correctly (as shown in the following screenshot).
The following surmises the issues I observed when debugging the code.
The getEmpInfo function within the DataService returns a new array each time it is called which prevents Angular from effectively monitoring it for changes. Instead of this function checking localStorage each time it is called it should just return the local array. The array can be simply loaded from localStorage when the service is first initialized.
The following update to the fiddle demonstrates this http://jsfiddle.net/g3tg5L15/6/. The changes implemented are as follows:
Change the dropdown in the menu bar of jsFiddle from onLoad to No
wrap - in <body> to load Angular correctly.
Added ng-click to 'Add Entry' button in HTML
<!-- Added ng-click to call addNewEmpInfo function on scope -->
<button ng-click='addNewEmpInfo()'>Add Entry</button>
Amended text on employeeInfo header to employee name rather than being hard coded value and added ng-click to remove in HTML.
<!-- Amended to add.employee rather than hardcoded value -->
<h3>{{add.employee}}</h3>
<!-- Added ng-click to call removeEmpInformation function on scope -->
<button ng-click='removeEmpInformation($index)'>X</button>
Amended the DataService to load from localStorage when it is initialized rather than initializing as an empty array.
var empInfoArray = JSON.parse(localStorage.getItem("employeeInformationLS")) || [];
Amend the getEmpInfo object to just return the local Array
this.getEmpInfo = function(){
return empInfoArray;
};
If necessary you can also watch for events triggered when localStorage changes, as included in the above fiddle. This will pick up changes from different tabs / windows if multiple are open. To monitor for these you must:
Include the services $window and $timeout in the DataService
angular.module('myApp').service("DataService", function($window, $timeout){
Add a trigger when a storage change occurs.
//Watch for storage events indicating changes to storage
angular.element($window).on('storage', function(event) {
//Check if the storage change was for our key
if (event.key === 'employeeInformationLS') {
//In a timeout (i.e. on next digest) update the array
//This could be done in a smarter way rather than clearing
//and rebuilding the entire array
$timeout(function(){
empInfoArray.splice(0);
var newArr = JSON.parse(localStorage.getItem("employeeInformationLS")) || [];
for (var i=0; i<newArr.length; i++){
empInfoArray.push(newArr[i]);
}
});
}
});
In my app I have a search page which loads in data based on a user's query. Each result (comes in waves of 10) has two fields I want to dynamically switch between: "imgs" and "smallImgs".
In short, if a result's smallImgs.length == true then I want to load up those images. If it has no length then I want to load up its imgs.
I'm aware of ng-hide and ng-show, and I could do a simple true/false statement in the template to show or hide the imgs/smallImgs. But from what I can tell, even if I have ng-hide enabled on an element it still loads in that data (and would hence download the images to the client).
The only way I've thought of achieving this is in my controller, on the $resource call that grabs my data:
$scope.itemSearch.get({query:$routeParams.query, page:$routeParams.page}, function(data){
$scope.posts = data.posts;
for(var i = 0; i<$scope.posts.length; i++){
$scope.posts[i].imgs = testImgs($scope.posts[i]);
}
});
var testImgs = function(data){
if (data.smallImgs.length){
return data.smallImgs;
} else{
return data.imgs;
}
}
Unfortunately I haven't gotten this working yet. Is this the best way to achieve this? Is there an Angular way to take care of this in the view?
What I'd do is add a method to your controller for this:
$scope.getImages = function () {
// add the logic to get the correct array here
};
Then just use that in the view, e.g.
<div ng-repeat="image in getImages()" />
Here's an example jsfiddle.
$scope.itemSearch.get({query:$routeParams.query, page:$routeParams.page}) - if this returns promise you can do so
$scope.getImages = $scope.itemSearch.get({query:$routeParams.query, page:$routeParams.page});
<div ng-repeat="image in getImages" />
I am modifying a third party - web client application in which I only have access to certain js files.
The search function is limited to search in one given server node at a time, and as a work around, I hardcoded all the server nodes and created a for loop, invoking the "search" several times, at different nodes.
The server response (in a form of FORM - without getters) are automatically handled by a callback, which then renders the view of the form. This means I am only able to display the last response and thus displaying only one set of result.
To handle this, I added $trs = $(tr).clone(true) on the callback function, saving all the rows from previous forms and then - I made the last loop to "search" to have another callback - which will then append the collected rows from $tr and display the last form complete with all the results from all nodes.
But the result is inconsistent. It sometimes just displays result from one server node. I would think this is caused by some delay in server response which caused that form to render last. I tried to put delay by setTimeout function, but that keeps me from getting any result at all
I am very new with all the web programming - JS and JQUERY both (well CSS and HTML even lol) and I would like to ask for your suggestions on a better way to handle this.
Thank you!
_handleConfigSubmit: function (form, error) {
//alert("_handleConfigSubmit");
if (form) {
var formView = new jabberwerx.ui.XDataFormView(form);
var that = this;
formView.event("xdataItemSelected").bind(function(evt) {
that.jq.find(".muc_search_button_join").removeAttr("disabled");
var resultTable = that.jq.find(".muc_search_results table.result_table");
resultTable.find("tr.selected").removeClass("selected");
that._selectedItem = evt.data.selected;
resultTable.find("tr#"+evt.data.selected._guid).addClass("selected");
});
var searchResultsDiv = jabberwerx.$(".muc_search_results", this.jq);
searchResultsDiv.empty();
this.update();
var dim = {
width: searchResultsDiv.width(),
height: searchResultsDiv.height()
};
formView.render().appendTo(searchResultsDiv);
formView.dimensions(dim);
$trs = $("table.result_table tbody>tr:not(:first)").clone(true);
if ($trList!=null){
$trList = $trList.add($trs);
}else{
$trList = $trs;
}
$("table.result_table tbody>tr:not(:first)").remove()
if (ctr<=3){
$("table.result_table tbody").append($trList);
}else{
ctr++;
}
} else {
this._showError(error);
}
}