I use AngularJS and ngView in my project.
ngView works perfectly with ngRoute, but when I come back to the previus link, ngView reload again data (with Ajax).
Is it possible to "keep in memory" previus data, so that should not always recharge all?
You should store your data in localStorage (or cookieStorage if localStorage is not available), and manage your own cache policy. Then check in your controller if the data is Available and not expired before showing it up. I always use a service of my own that goes like:
(function () {
var __service = function () {
var __set = function (key, item) { localStorage.setItem(key, JSON.stringify(item)); };
var __get = function (key) { return (localStorage.getItem(key) ? JSON.parse(localStorage.getItem(key)) : null); };
var __clear = function (key) { localStorage.removeItem(key); };
var __clearAll = function () { localStorage.clear(); };
return {
Set: __set,
Get: __get,
Clear: __clear,
ClearAll: __clearAll
};
};
angular.module('myApp').service('StorageService', __service);
}());
Then in my controllers I put that service to work in order to cache information that I do not want to retrieve everytime I get there.
PD: I know i must specialize that service to check if localStorage is available.
Related
I am using the following service to pass data between two controllers
app.service('sharedDataService', function () {
var selectedObj = {};
var setSelectedObj = function (obj) {
selectedObj = obj;
};
var getSelectedObj = function () {
return selectedObj;
};
return {
SetSelectedObj: setSelectedObj,
GetSelectedObj: getSelectedObj
};
});
It works fine except when on the 2nd controller, the users press F5 or refresh the browser page. The selectedObj is cleared returns null.
Edit: based on some answers and comments - I am saving the data in the 2nd controller when I use the following line
$scope.Form = sharedDataService.GetSelectedObj();
Do I need to save it differently?
After getting the result please add it in session, after page refresh you can access those values again, even we can't root scope variable as well because those also cleared after page refresh.
I have two controllers. The first one sets a variable into my service and my second one is supposed to get this variable, but it's undefined.
Aren't angular services supposed to be singletons ? Because my service seems to be instantiated for each controller.
Here's my code :
First controller
angular.module('myApp').controller('HomeCtrl', ['$scope', 'User', function ($scope, User) {
$scope.join = function () {
User.setRoom("test");
console.log(User.getRoom()); // displays 'test'
$window.location.href = '/talk';
}
}]);
In my second controller, I've just a
console.log(User.getRoom()); // displays ''
And here's my service
angular.module('myApp').factory('User', function () {
var data = {
room: ''
};
return {
getRoom: function () {
return data.room;
},
setRoom: function (room) {
data.room = room;
}
};
});
Do you have an idea?
You are using $window.location.href = '/talk'; to navigate - this triggers a full page reload, and all services are therefore also reinitialized.
You probably want to use the $location service. See the documentation and/or this answer for a summary of the difference between the two.
I'm trying to pass the videoUrl variable in the showResponse function into my controller. I've been trying to figure out a solution without success. Can anyone guide me in the right direction?
var myApp = angular.module('myApp', []);
myApp.controller('mainCtrl', ['$scope', function($scope){
$scope.videoUrl = videoUrl;
}])
// Helper function to display JavaScript value on HTML page.
function showResponse(response) {
var videoUrl = [];
for (prop in response.items) {
videoUrl[prop] = "https://www.youtube.com/embed/" + response.items[prop].snippet.resourceId.videoId;
}
}
// Called automatically when JavaScript client library is loaded.
function onClientLoad() {
gapi.client.load('youtube', 'v3', onYouTubeApiLoad);
}
// Called automatically when YouTube API interface is loaded
function onYouTubeApiLoad() {
gapi.client.setApiKey('#######');
search();
}
function search() {
// Use the JavaScript client library to create a search.list() API call.
var request = gapi.client.youtube.playlistItems.list({
part: 'snippet',
playlistId: '########'
});
// Send the request to the API server,
// and invoke onSearchRepsonse() with the response.
request.execute(onSearchResponse);
}
// Called automatically with the response of the YouTube API request.
function onSearchResponse(response) {
showResponse(response);
}
It would probably better/easier if you could get this stuff into angular, so that it's all happening within services. That's how data sharing is supposed to happen in angular. But maybe that's challenging due to the nature of onClientLoad. The dirty way to do it is:
Get the controller's scope directly and set it on that scope. Assuming you've got something defined like:
<div ng-controller="mainCtrl"></div>
you can get that controller's scope using jQuery:
function showResponse(response) {
var videoUrl = [];
for (prop in response.items) {
videoUrl[prop] = "https://www.youtube.com/embed/" + response.items[prop].snippet.resourceId.videoId;
}
var scope = $('[ng-controller="mainCtrl"]').scope();
scope.videoUrl = videoUrl;
}
Note that this will cause angular purists to weep and gnash their teeth.
I am quite beginner level with both JS and Angular, and I am trying to return data from an API and store it in $scope.
Once I've stored it, I want to loop over each item and output it into the page, pretty basic stuff.
Problem I am having is the API and data is there, but it seems to be returning after the loop is running, is there any way of making the loop wait?
Heres the code;
Service (Hit the endpoint and retrieve the data)
'use strict';
function RecruiterDashJobs($resource, API_URL) {
var dashJobs = {};
dashJobs.getJobs = function(uuid) {
return $resource(API_URL + 'recruiters/' + uuid + '/jobs').get();
}
return dashJobs;
}
angular
.module('app')
.service('RecruiterDashJobs', RecruiterDashJobs);
Controller (Call the service and store the data)
$scope.currentRecruiter = User.getUser();
$scope.getJobs = function(uuid) {
var data = RecruiterDashJobs.getJobs(uuid);
data.$promise.then(
function(res) {
return res.jobs
},
function(err) {
return err;
}
)
};
$scope.recruiterJobs = $scope.getJobs($scope.currentRecruiter.uuid);
View (the Ng-repeat)
<div class="panel border-bottom pad-s-2x pad-e-1x" ng-repeat="job in recruiterJobs">
<div class="panel__body">
<aside class="valign">
<a class="icon--edit color--echo mar-r-2x" ui-sref="jobs/edit/{{job.uuid}"></a>
</aside>
<div class="valign">
<p>{{job.title}}</p>
<p class="color--charlie">Closing Date: {{job.closing_date}}</p>
</div>
</div>
</div>
EDIT: the "magical" approach below no longer works as of Angular 1.2
In your getJobs method, the return statements are inside child functions. You aren't returning the data from getJobs, you're returning it from the function you passed to then.
Two ways to fix it:
The magical Angular way for Angular less than 1.2
Angular views will work with promises for you, so you can just change your getJobs method to this:
$scope.getJobs = function(uuid) {
var data = RecruiterDashJobs.getJobs(uuid);
return data.$promise.then(
function(res) {
return res.jobs
},
function(err) {
return err;
}
)
};
Added return data.$promise...
If you want this to still work in Angular 1.2, you need to call $parseProvider.unwrapPromises(true) somewhere in your code, typically on your main modules config block.
Less magical way or for Angular 1.2 and above
If you want to better understand what is going on, then you can do it this way
$scope.getJobs = function(uuid) {
var data = RecruiterDashJobs.getJobs(uuid);
data.$promise.then(
function(res) {
$scope.recruiterJobs = res.jobs
},
function(err) {
console.log(err);
}
)
};
A $resource call is asynchronous, but $resource immediately returns an empty object that you can embed in your page and will be later populated by response contents. If all goes well, angular should spot the change (because it comes from a $resource process that angular monitors) and update your view accordingly.
So, the behaviour you observe is normal : the very premise of a $promise is that it will be done at a later stage and the process should proceed anyway.
Solutions :
Simply try :
$scope.getJobs = function(uuid) {
var data = RecruiterDashJobs.getJobs(uuid);
return data;
};
If you don't need to post-process data, this should be all you need (except that you might need to call recruiterJobs.jobs in your view, if your response does indeed return an object containing a jobs array, and not the array itself). The page will display, with an initial empty div, then update when data are retrieved and ng-repeat discovers new data to add to the page.
If you do need some post-processing, you can still use your callback :
$scope.getJobs = function(uuid) {
var data = RecruiterDashJobs.getJobs(uuid);
data.$promise.then(
function(res) {
//do something
},
function(err) {
return err;
}
);
return data;
};
If you really need to wait for your data (e.g. because there are some downstream processes that you need them for that can't be postponed), you can use the promise to do so :
$scope.getJobs = function(uuid) {
$scope.preparing = true;
var data = RecruiterDashJobs.getJobs(uuid);
data.$promise.then(function(res) {
$scope.preparing = false;
return data;
});
};
This way, the function will not return until the promise is resolved. I added an optional $scope.preparing flag that you can use in your page to inform the user that something is loading.
I have defined two AngularJS services ... one is for the YouTube Player API, and other for the YouTube iFrame Data API. They look like this:
angular.module('myApp.services',[]).run(function() {
var tag = document.createElement('script');
tag.src = "//www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
})
.factory('YtPlayerApi', ['$window', '$rootScope', function ($window, $rootScope) {
var ytplayer = {"playerId":null,
"playerObj":null,
"videoId":null,
"height":390,
"width":640};
$window.onYouTubeIframeAPIReady = function () {
$rootScope.$broadcast('loadedApi');
};
ytplayer.setPlayerId = function(elemId) {
this.playerId=elemId;
};
ytplayer.loadPlayer = function () {
this.playerObj = new YT.Player(this.playerId, {
height: this.height,
width: this.width,
videoId: this.videoId
});
};
return ytplayer;
}])
.factory('YtDataApi', ['appConfig','$http', function(cfg,$http){
var _params = {
key: cfg.youtubeKey
};
var api="https://www.googleapis.com/youtube/v3/";
var yt_resource = {"api":api};
yt_resource.search = function(query, parameters) {
var config = {
params: angular.extend(angular.copy(_params),
{maxResults: 10,
part: "snippet"}, parameters)
};
return $http.get(api + "search?q=" + query, config);
};
return yt_resource;
}]);
(also note that the 'setPlayerId' function of my player service is called by a custom directive ... but that's not important for my question).
So, here's the issue. I need to ensure that the Player API code is loaded before I set the video id and create the player, which is why I have it broadcasting the 'loadedApi' message. And this works great, if I then in my controller pass a hard-coded video id, like this:
function ReceiverCtrl($scope,$rootScope,$routeParams,ytplayer,ytdataapi) {
$scope.$on('loadedApi',function () {
ytplayer.videoId='voNEBqRZmBc';
ytplayer.loadPlayer();
});
}
However, my video IDs won't be determined until I make an API call with the data api service, so I ALSO have to ensure that the results of that call have come back. And that's where I'm running into problems ... if I do something like this:
$scope.$on('loadedApi',function () {
ytdataapi.search("Mad Men", {'topicId':$routeParams.topicId,
'type':'video',
'order':'viewCount'})
.success(function(apiresults) { // <-- this never gets triggered
console.log(apiresults); // <-- likewise, this obviously doesn't either
});
});
Then the interaction with the data service never happens for some reason. I know the data service works just fine, for when I un-nest it from the $on statement, it returns the api results. But sometimes latency makes it so that the results don't come back fast enough to use them in the player service. Any thoughts on what I can do to make the data search after receiving the message that the player API is ready, but still keep the two services as two separate services (because other controllers only use one or the other, so I don't want them dependent on each other at the service level)?
Figured it out; I had to call $scope.$apply(), like this:
function ReceiverCtrl($scope,$rootScope,$routeParams,ytplayer,ytdataapi) {
$scope.$on('loadedApi',function () {
ytdataapi.search("",{'topicId':$routeParams.topicId,'type':'video','maxResults':1,'order':'viewCount'}).success(function(apiresults) {
ytplayer.videoId=apiresults.items[0].id.videoId;
ytplayer.loadPlayer();
});
$scope.$apply();
});
}
Is there anyone who could shed light on why this works, though? $scope.$digest() also works ... but I thought those methods were only used when you need to update bindings because of some javascript code that Angular isn't aware of. Is the nesting I've got here doing that (I wouldn't think it should, as my ytdataapi service is using $http)?