I've just started using angular-flash and the messages are coming up, but where should I clear the messages?
Do I just set flash.message('') under every action or put it in the route somewhere?
Is there a way to do this on a timer so it fades out? I see in the code that there is a timeout feature but where do I initialise that?
I'm looking into the source code to try and figure out what is going on, a quick and dirty fix would be to do this when you want to clear the messages:
flash([]);
Since all of your flash messages are in an array, I assume you can handle it like any array.
The reason I'm saying it's dirty is because I'm assuming there's a better way to do this, with something like a clear() or empty() function.
I have found my answer to clearing:
flash('notice', 'User details updated!')
$location.path "/users/#{$stateParams['id']}"
$timeout (-> flash()), 2000
You can wrap it with your own service:
'use strict';
angular.module('myApp')
.service('ff', ['$rootScope', 'flash', '$timeout', function ($rootScope, flash, $timeout) {
var log = function(level) {
function inner(msg, sticky) {
if (level == 'info') flash(msg);
else flash(level, msg);
var promise = $timeout(function() {
var idx = _.findIndex($rootScope.messages, ['text', msg]);
$rootScope.messages.splice(idx, 1);
}, 2000);
if (sticky) {
$timeout.cancel(promise);
}
}
return inner;
};
return {
info: log('info'),
error: log('error'),
warn: log('warning'),
success: log('success')
};
}]);
And in the controller:
ff.info("This will go away.");
ff.error("This will stick", true);
Now it's just a pity multiple calls to flash doesn't just append to an array, i.e. replace this line with
if (typeof $scope.messages !== 'undefined') {
$scope.messages = $scope.messages.concat(messages);
} else {
$scope.messages = messages;
}
Related
I'm new in AngularJS and I try to understand how to use it. I'm using angular-translate to translate my website and it works but I have a problem with the dynamic content from the database.
I get the dynamic content by an api request. I would like to "redo" the request with the good language to get the content reloaded in the good language.
I catch the "translateChangeSuccess" event but how can I "redo" the previous api request ?
Thank you very much for your help :)
ps : sorry for my english
Edit :
// my run block :
(function ()
{
'use strict';
angular
.module('fuse')
.run(runBlock);
function runBlock($rootScope, $timeout, $state, $cookieStore)
{
$rootScope.$on('$translateChangeSuccess', function () {
// catch translateChangeSuccess event
// redo the previous api request
});
}
})();
// my change language function
/**
* Change Language
*/
function changeLanguage(lang)
{
angular.forEach(vm.languages, function(value, key) {
if (lang.code == key)
$translate.use(lang.code); // launch translateChangeSuccess event
});
}
// my api service
function apiService($http, $resource, $translate, CONFIG_API)
{
// change header with good language
$http.defaults.headers.common["Accept-Language"] = $translate.proposedLanguage();
var api = {};
// Base Url
api.baseUrl = CONFIG_API.base_url;
// request to reload when user changes language
api.Documents = $resource(api.baseUrl + 'documents/:id',
{id: '#id'},
{update: {method: 'PUT'}}
);
...
}
On the translateChangeSuccess event do the request again with the current parameter for the desired language (implying that the server sends you different content depending on language)
I cannot give you a solid example since there are so many ways to manage it.
With some code you can have a better explanation.
Ok, so I found how to do that. I just ask data to the api again through a service (apiResolver)
test.module.js :
(function ()
{
'use strict';
angular
.module('app.test_module', [])
.config(config);
/** #ngInject */
function config($stateProvider, msApiProvider)
{
// State
$stateProvider.state('app.test_module', {
url : '/myurl',
views : {
'content#app': {
templateUrl: 'mytemplate.html',
controller : 'MyController as vm'
}
},
resolve : {
test : function (apiResolver)
{
return apiResolver.resolve('myquery#query');
}
}
});
}
})();
and test.controller.js :
(function ()
{
'use strict';
angular
.module('app.test_module')
.controller('testController', testController);
/** #ngInject */
function testController($rootScope, apiResolver, dataToDisplay)
{
var vm = this;
// Data
vm.dataToDisplay = dataToDisplay;
$rootScope.$on('$translateChangeSuccess', function () {
// reload my content
apiResolver.resolve('myquery#query')
.then(function(result) {
vm.dataToDisplay = result;
});
});
}
// more code here but not usefull in this example
})();
There is maybe a better way but it works, my data are translated when the user changes language :)
I read about the Angular '$cacheFactory' but could not find any documentation on setting an expiration date for cached content.
What if I want to cache all GET requests for 30 seconds, how to I define this in the '$cacheFactory' or do I need to extend the functionality myself.
for TTL 1h, see below example
add factory:
.factory('cacheInterceptor', ['$cacheFactory', function($cacheFactory) {
var http_ttl_cache = {};
return {
request: function(config) {
var N;
if (config.timeToLive) {
config.cache = true;
N = config.timeToLive;
delete config.timeToLive;
if (new Date().getTime() - (http_ttl_cache[config.url] || 0) > N) {
$cacheFactory.get('$http').remove(config.url);
http_ttl_cache[config.url] = new Date().getTime();
}
}
return config;
}
};
}])
then init in config push your interceptor.
An interceptor is simply a regular service factory that is registered to that array.
.config(['$routeProvider', '$httpProvider', function($routeProvider, $httpProvider) {
$httpProvider.interceptors.push('cacheInterceptor');
example of request
$http.get('/permissions.json', {timeToLive: Constant.timeToLive}).then(function(result){
Constant is:
.constant('Constant', {
url: {
logout: '/auth/logout'
},
timeToLive: 60*60*1000
})
I faced the problem too. The default $cacheFactory have no time to live (TTL).
You will need to implement this yourself. But before, you could give a look around, to see if someone already did it :
This one look pretty complete - http://jmdobry.github.io/angular-cache/
If you really want to implement your own solution (by implementing your own $cacheFactory) and need some help, feel free to ask.
Hope it gave you some clue.
I think #miukki answer's is great. Adding my modification to the request of #Vil
I modified the 'request' function of the 'cacheInterceptor' to use $timeout instead of relying on Date. It allows TTL to be more global for requests, So if one request sets a TTL and the 2nd doesn't but data is still in cached, it will still be used.
.factory('cacheInterceptor', ['$cacheFactory', '$timeout', function($cacheFactory, $timeout) {
var ttlMap = {};
return {
request: function(config) {
if (config.ttl) {
var ttl = config.ttl;
delete config.ttl;
config.cache = true;
// If not in ttlMap then we set up a timer to delete, otherwise there's already a timer.
if (!ttlMap[config.url]) {
ttlMap[config.url] = true;
$timeout(ttl)
.then(function() {
$cacheFactory.get('$http').remove(config.url);
delete ttlMap[config.url];
});
}
}
return config;
}
};
}])
I have this piece of code:
.service('webSocket', function ($rootScope, socketFactory, CONFIG, $timeout) {
angular.element(document).ready(function () {
$rootScope.log('Waiting for connection...',CONSOLE_INFO);
});
And I am getting this error:
TypeError: $rootScope.log is not a function
This service is injected into this controller:
.controller('mainCtrl', function mainCtrl($scope, $rootScope, webSocket, myConsole ...
In which I have:
$rootScope.log = function (msg, type) { myConsole.log(msg,type); ... };
Can you tell me where is the problem? Or at least point me in the right direction? The reason I am using document ready function is because apart from logging messages to browser console (console.log) I use notifications for user (pNotify library) which needs to be called after DOM is loaded.
Sharing something between services using $rootScope should be considered generally as anti-pattern. If you don't have some different implementation of console for different controllers, you can do it Angular-way and perform all configurations in config block. Subscribing to document ready event in the service is also not a good idea (I would prefer to do it in run block), since in angular service is instantiated once it is first time required by any other service or controller or whatever. In order to have configurable service that may have different console implementation I would implement it using provider as follows:
angular.module('app',[]).
constant('console', console).
constant('PNotify', PNotify).
provider('myConsole', function() {
var log = angular.noop;
function MyConsoleFactory() {
return {
log: log,
debug: log
}
}
this.setLog = function(logImplementation) {
log = logImplementation
}
this.$get = [MyConsoleFactory];
}).
config(['myConsoleProvider', 'console', 'PNotify', function(myConsoleProvider, console, PNotify) {
myConsoleProvider.setLog(function(msg) {
console.log('[LOG] '+ Date.now() + ':\t' + msg);
new PNotify({
title: 'Title',
text: msg
});
});
}]).
run(['myConsole', '$document', function(myConsole, $document) {
$document.ready(function () {
myConsole.log('Waiting for connection...');
});
}]);
In this case you don't need any controller at all.
Plunker: http://plnkr.co/edit/aV9TIO07pnDs26xDBPtf?p=preview
That happens because service code runs before service was added to controller(where $rootScope.log method is defined). You can move $rootScope.log = function (msg, type) { myConsole.log(msg,type); ... }; into app.run(...) method and it will work.
I asked the wrong question yesterday (and got a goodanswer that worked), but am realizing it's not what I needed. I need to be able to retrieve JSON data (preferably once), store it, and access it throughout my service. The challenge I'm having is that all the examples I can find talk about using JSON and passing to the app/controller, whereas in this case I need to get it, check it, and then it dictates what my module/service does.
For instance, I have my App and Controller, and then I have a module such as (this is psuedo-code, not meant to run):
angular.module("myModule")
.service("myService1", function($q, myService2, $http) {
this.getModel = function() {
return {
title: "My Title",
desc: "My Desc"
options: function () {
if (condition A)
return "option1";
else
return "option2";
}
};
};
})
.service("myService2", function($q, $http) {
this.getCfgInfo = function () {
var defer = $q.defer();
$http.get("my/json/url").then(function(response) {
defer.resolve(response.data);
});
return defer.promise;
};
})
In this example, I'm wanting to get the JSON, and use it within myService1 for both literal values (title, desc) as well as for conditions (condition A within the if).
I know I can do something like this (thanks to Joel for helping yesterday):
service("myService1", function($q, myService2, $http) {
// get a promise object for the configuration info
var cfgProm = rtDataMapper.getCfgInfo()
this.getModel = function() {
return {
title: cfgProm.then(function(response) {
return response.JSON_NAME;
}),
and it works fine as I've got the title mapped back into my model and there is a watch(), but I'm stumped as to how I get, store, and use the JSON within the service itself as a conditional (i.e. if (condition A) where condition A is coming from the JSON. Trying to wrap these in .then() doesn't seem to make sense, or at least I can't figure out how to do it.
I'm new to Angular and am attempting to modify some code that was left to us. I'm guessing I don't need the myService2 just to get the JSON. Can anyone help point me in the right direction? I've spent several hours online but can't seem to find a relevant reference/example.
Thanks
Live demo (click).
I'm having the service immediately get the data when it is injected (that code will only run once no matter how many times you inject it). That's nice because you won't have to call a function to get the data - it's called for when creating the service.
Your service method that returns that data will need to return the promise of the data, of course, since you aren't guaranteed that it will have come through when you ask for it. You can pass arguments to that method to use to determine your conditions. All you need to do for that is use promise.then in the method and resolve the promise with the modified data. Since that method is returning the promise already, the modification will be updated on the resolve. See all of this below and in the demo.
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, myService) {
myService.getData(15).then(function(data) {
$scope.myData = data;
});
});
app.factory('myService', function($q, $timeout) {
//this code only runs once when you first inject the service
//get data immediately
var deferred = $q.defer();
$timeout(function() { //simulate ajax call
var data = { //ajax response data
foo: 15,
bar: 'Some data!'
};
data = modifyData(data, 1);
deferred.resolve(data);
}, 500);
function modifyData(data, fooVal) {
if (data.foo === fooVal) {
data.baz = 'Conditional data!';
}
return data;
}
var myService = {
//data can be modified when it comes from the server,
//or any time you call this function
getData: function(fooVal) {
if (fooVal) { //if you want to modify the data
deferred.promise.then(function(data) {
data = modifyData(data, fooVal);
deferred.resolve(data);
});
}
return deferred.promise;
}
};
return myService;
});
I've got an app defined this way:
angular.module("myApp", [...])
.config(function ($stateProvider, $controllerProvider) {
if (isControllerDefined(controllerName)) {
do_stuff();
}
})
The controllers are defined this way:
angular.module("myApp")
.controller("myController", function ($scope) { ... });
How can I define isControllerDefined() (in the config above) to check whether a given controller exists if I have the name of the controller? I feel like I should be able to do something like one of these:
var ctrl = angular.module("myApp").getController("myController");
var ctrl = $controllerProvider.get("myController");
or something like that... but I can't find any functionality for this. Help?
An example of a service that can check if a controller exists. Note that it looks for a global function with specified name as well as a controller in the $controller provider.
angular.service('ControllerChecker', ['$controller', function($controller) {
return {
exists: function(controllerName) {
if(typeof window[controllerName] == 'function') {
return true;
}
try {
$controller(controllerName);
return true;
} catch (error) {
return !(error instanceof TypeError);
}
}
};
}]);
See the fiddle for usage.
I came across this exact same issue the other day. I had a few issues with the currently accepted answer, namely because one of my controllers was performing an initialization call out to the server upon instantiation to populate some data (i.e):
function ExampleController($scope, ExampleService) {
ExampleService.getData().then(function(data) {
$scope.foo = data.foo;
$scope.bar = data.bar
});
}
As it stands, the currently accepted answer will actually instantiate the controller, before discarding it. This lead to multiple API calls being made on each request (one to verify that the controller exists, one to actually use the controller).
I had a bit of a dig around in the $controller source code and found that there's an undocumented parameter you can pass in called later which will delay instantiation. It will, however, still run all of the checks to ensure that the controller exists, which is perfect!
angular.factory("util", [ "$controller", function($controller) {
return {
controllerExists: function(name) {
try {
// inject '$scope' as a dummy local variable
// and flag the $controller with 'later' to delay instantiation
$controller(name, { "$scope": {} }, true);
return true;
}
catch(ex) {
return false;
}
}
};
}]);
UPDATE: Probably a lot easier as a decorator:
angular.config(['$provide', function($provide) {
$provide.delegate('$controller', [ '$delegate', function($delegate) {
$delegate.exists = function(controllerName) {
try {
// inject '$scope' as a dummy local variable
// and flag the $controller with 'later' to delay instantiation
$delegate(controllerName, { '$scope': {} }, true);
return true;
}
catch(ex) {
return false;
}
};
return $delegate;
}]);
}]);
Then you can simply inject $controller and call exists(...)
function($controller) {
console.log($controller.exists('TestController') ? 'Exists' : 'Does not exist');
}
There is currently no easy way of fetching a list of controllers. That is hidden for internal use only. You would have to go to the source code and add a public method that return the internal controllers variable (in $ControllerProvider function)
https://github.com/angular/angular.js/blob/master/src/ng/controller.js#L16
this.getControllers = function() {
return controllers;
// This will return an object of all the currently defined controllers
};
Then you can just do
app.config(function($controllerProvider) {
var myCtrl = $controllerProvider.getControllers()['myController'];
});
Since angular 1.5.1 (not released yet at the time of writing), there is a new way to check whether a controller exists or not through the $ControllerProvider.has('MyCtrlName') method.
Github issue: https://github.com/angular/angular.js/issues/13951
Github PR: https://github.com/angular/angular.js/pull/14109
Commit backported in 1.5.1 directly: https://github.com/angular/angular.js/commit/bb9575dbd3428176216355df7b2933d2a72783cd
Disclaimer: Since many people were interested by this feature, I made a PR, because I also need it is some of my projects. Have fun! :)
This PR has been based on #trekforever answer, thanks for the hint :)
You could use the $controller service and do $controller('myController') and wrap a try-catch arround it so you know if it fails.