I have a separate language json file for each partial/controller. To avoid loading all the json files at once, I have added the addPart statement in the controller instead of the module config:
$translatePartialLoader.addPart('editName');
When I browse to the partial, I see that the json file is requested from the server only when needed. But when I keep refreshing the partial by clicking the function key F5, at random times, the json file is not requested from the server and the text displayed on the view is not translated. Not sure what I can do to fix this. Any help is greatly appreciated. Here is the code:
angular.module('pp')
.controller('informationList', [
'$scope', '$rootScope', '$location', '$translatePartialLoader', '$translate',
function($scope, $rootScope, $location, $translatePartialLoader, $translate) {
$translatePartialLoader.addPart('informationList');
$translate.refresh();
}
]);
Your issue sounds similar to mine.
This (called from app config) works:
angular.module('myApp').config(function ($translateProvider, $translatePartialLoaderProvider) {
$translateProvider.useLoader('$translatePartialLoader', {
'urlTemplate': '{part}-{lang}.json'
});
$translateProvider.preferredLanguage('EN');
$translatePartialLoaderProvider.addPart('headline');
$translatePartialLoaderProvider.addPart('languages');
});
This (called from controller) does not:
angular.module('myApp').controller('Ctrl', ['$scope', '$translate', '$translatePartialLoader', function ($scope, $translate, $translatePartialLoader) {
$translatePartialLoader.addPart('headline');
$translatePartialLoader.addPart('languages');
$translate.refresh();
}]);
Here is a plunker to demonstrate the problem.
http://plnkr.co/edit/sZC2XST8BMZcMCYbgtxt?p=preview
Could you just add a
$translateProvider.use('EN');
to the config - section just right below the "preferredLanguage('EN');" - line?
This should solve your problem.
It is in in fact currently needed to define that additional line - even though it might sound a bit like "duplication" :-).
Related
I am building a small AngularJS project and I have encountered a problem that I want to ask you guys about.
I am using angular-local-storage module to store some data coming from my API into the browser's local storage.
In one of my controllers I assign this data to a variable in the $scope object and try to render it in the view as follows:
controller:
angular.module('Dashboard')
.controller('DashboardController',
['$scope', '$rootScope', 'localStorageService',
function ($scope, $rootScope, localStorageService) {
$scope.userData = localStorageService.get('userData');
}]);
And the view:
<div class="row">
<h4>Welcome to your dashboard <strong>{{userData.personalUserInfo.name}}</strong>!</h4>
When I log into the app (which is when the data is fetched from API and stored in local store by key 'userData'), the view is incomplete, I get only "Welcome to your dashboard !" without the name there. When I go to the dev console and look at the localStorage of my browser, the entry "userData" is there, it is just not rendered.
Then when I hit F5 and refresh the page, the name appears.
Do you have any ideas why that is and what can be done to fix that?
Cheers!
You have to use $scope.$watch for this, like following:
$scope.$watch(function() {
return localStorageService.get('userData');
}, function(newVal, oldVal) {
if (newVal !== oldVal)
$scope.userData = newVal;
})
$scope.$watch, will execute second function each time return value of first function is changed.
I'm working on an AngularJs/MVC app with Web API etc. which is using a CDN. I have managed to whitelist two URLs for Angular to use, a local CDN and a live CDN (web app hosted in Azure).
I can successfully ng-include a template from my local CDN domain, but the problem arises when I push the site to a UAT / Live environment, I cant be using a template on Localhost.
I need a way to be able to dynamically get the base url for the templates. The location on the server will always be the same, eg: rooturl/html/templates. I just need to be able to change the rooturl depending on the environment.
I was thinking if there was some way to store a global variable, possibly on the $rootScope somewhere that I can get to when using the templates and then set that to the url via Web API which will get return a config setting.
For example on my dev machine the var could be http://Localhost:52920/ but on my uat server it could be https://uat-cdn.com/
Any help would be greatly appreciated as I don't want to store Js, css, fonts etc on the CDN but not the HTML as it feels nasty.
Thanks I'm advance!
I think it's good practice to keep environment and global config stuff outside of Angular altogether, so it's not part of the normal build process and is harder to accidentally blow away during a deploy. One way is to include a script file containing just a single global variable:
var config = {
myBaseUrl: '/templates/',
otherStuff: 'whatever'
}
...and expose it to Angular via a service:
angular.module('myApp')
.factory('config', function () {
var config = window.config ? window.config : {}; // (or throw an error if it's not found)
// set defaults here if useful
config.myBaseUrl = config.myBaseUrl || 'defaultBaseUrlValue';
// etc
return config;
}
...so it's now injectable as a dependency anywhere you need it:
.controller('fooController', function (config, $scope), {
$scope.myBaseUrl = config.myBaseUrl;
}
Functionally speaking, this is not terribly different from dumping a global variable into $rootScope but I feel like it's a cleaner separation of app from environment.
If you decide to create a factory then it would look like this:
angular.module('myModule', [])
.factory('baseUrl', ['$location', function ($location) {
return {
getBaseUrl: function () {
return $location.hostname;
}
};
}]);
A provider could be handy if you want to make any type of customization during config.
Maybe you want to build the baseurl manually instead of using hostname property.
If you want to use it on the templates then you need to create a filter that reuses it:
angular.module('myModule').filter('anchorBuilder', ['baseUrl', function (baseUrl) {
return function (path) {
return baseUrl.getBaseUrl() + path;
}
}]);
And on the template:
EDIT
The above example was to create links but if you want to use it on a ng-include directive then you will have a function on your controller that uses the factory and returns the url.
// Template
<div ng-include src="urlBuilder('path')"></div>
//Controller
$scope.urlBuilder = function (path) {
return BaseUrl.getBaseUrl() + path;
};
Make sure to inject the factory in the controller
Let's say I have an MVC/WebAPI/AngularJS site that I'm running locally, e.g. ;
localhost/Test/
which I then want to move to
www.test.com
While local, I have a lot of references to various directories (jsfiles, etc) of the following format (in either JS or HTML files)
app.directive('rpdbSpinner', function() {
return {
restrict: 'E',
**templateUrl: '/Test/templates/directives/spinner.html',**
scope: {
isLoading:'='
}
}
})
when updating/web publishing, I'd have to change everything to:
app.directive('rpdbSpinner', function() {
return {
restrict: 'E',
**templateUrl: '/templates/directives/spinner.html',**
scope: {
isLoading:'='
}
}
})
I can do this manually (which is what I've been doing),but the larger the project grows, the harder it becomes. I could, of course, only change it once and then excluded the files during publishing phase (web.config/rest), but it still feels like I am going about it the wrong way. Using "~/" wouldn't work on plain HTML/JS files as far as I'm aware, and this I can't really use it...
Any suggestions to map to paths globally regardless of whether in a Virtual Directory or the root of a project?
Thanks :)
If you simply care about getting the root/base url of the site so you can append that to get the other url you are after, you may simply use / as the first character of your url.
var getUsersUrl = "/api/users";
Here is an alternate approach if you want more than just the app root (Ex : Specific urls( built using mvc helper methods such as Url.RouteUrl etc)
You should not hard code your app base path like that. You may use the Url.Content or Url.RouteUrl helper methods in your razor view to generate the url to the app base. It will take care of correctly building the url regardless of your current page/path.Once you get this value, assign it to a javascript variable and use that in your other js code to build your other urls. Always make sure to use javascript namespacing when doing so to avoid possible issues with global javascript variables.
So in your razor view (Layout file or specific view), you may do this.
<script>
var myApp = myApp || {};
myApp.Urls = myApp.Urls || {};
myApp.Urls.baseUrl = '#Url.Content("~")';
myApp.Urls.userListUrl = '#Url.Action("Index","User")';
</script>
<script src="~/Scripts/NonAngularJavaScript.js"></script>
<script src="~/Scripts/AngularControllerForPage.js"></script>
<script>
var a = angular.module("app").value("appSettings", myApp);
</script>
In your angular controller, you can access it like,
var app = angular.module("app", []);
var ctrl = function (appSettings) {
var vm = this;
console.log(appSettings.Urls.userListUrl);
vm.baseUrl = appSettings.Urls.baseUrl;
//build other urls using the base url now
var getUsersUrl = vm.baseUrl + "api/users";
console.log(getUsersUrl);
};
app.controller("ctrl", ctrl)
You can also access this in your data services, directives etc.
In your non angular java script files.
// With the base url, you may safely add the remaining url route.
var urlToJobIndex2= myApp.Urls.baseUrl+"jobs/GetIndex";
Using "~/" wouldn't work on plain HTML/JS files as far as I'm aware,
and this I can't really use it...
Yes, but you could inject it in your main server-side served webpage as a variable:
<script>
var baseUrl = ... get the base url from the server using ~/
</script>
and then in your external scripts simply concatenate the relative urls with it. As far as static html files are concerned, then it could be a little more problematic. You could serve them through some special server side handler that will take care of injecting this logic.
You can use module.constant to create an injectable which you can use.
app.constant("URL_BASE", "/Test");
app.directive('rpdbSpinner', function(URL_BASE) {
return {
restrict: 'E',
**templateUrl: URL_BASE + '/templates/directives/spinner.html',**
scope: {
isLoading:'='
}
}
})
You can also use module.value if you register it before you register your directive.
For more information see AngularJS Module Guide -- configuration.
I am trying to use the Async module of https://github.com/millermedeiros/requirejs-plugins to load the googlemap api. My index.html file contains the following requirejs configuration:
<script data-main="scripts/myscript" src="scripts/require.js"></script>
<script>
requirejs.config({
"baseUrl": "scripts",
"paths": {
"async": "require_plugins/async",
"gmaps": "gmaps",
"infobox":"http://google-maps-utility-library-v3.googlecode.com/svn/trunk/infobox/src/infobox",
"jquery":"//code.jquery.com/jquery-2.1.1.min",
"jquery_mob":"//code.jquery.com/mobile/1.4.3/jquery.mobile-1.4.3.min"
},
waitSeconds: 15
});
All my javascript files are stored in a scripts folder (relative to index.html)
e.g. script/myscript.js and script/require.js and the async plugins are stored in a subfolder of script called require_plugins e.g. script/require_plugins/async.js
The javascript where I define the googlemap module is called gmaps.js (stored in the script folder) and looks as follows:
define("GMAP",['async!https://maps.googleapis.com/maps/api/js? &key=xxxxxx®ion=uk&libraries=places,geometry'], function () {
return window.google.maps;
});
I have obfuscated the key parameter intentionally here. According to the documentation, I should be able to use the gmaps module anywhere in other javascript files just by invoking it like so:
require(["gmaps"],function(GMAP)
{
map= new GMAP.Map("#map-div");
//and then some code to display the map
}
Unfortunately, it does not work at all. It seems that the googlemap library has not loaded at all. I use absolute URLs for jquery and that works fine but googlemap fails miserably. My question is: Is there something wrong with my requirejs config? I can't think of anything else causing this fault :(
My understanding is that the name you set in define() is what you need to use when writing the dependencies.
e.g.:
define('GMAP', ['async!<path>'], function() { return window.google.maps; });
require(['GMAP'], function(GMaps) { ... });
This is how I get GMaps to load for me. I have a secondary problem now that other libraries that depend on Maps no longer load.
I am still learning angularjs so maybe there's something stupid I am not understanding but I have a really strange behaviour when using routing.
In my application I use the following code to define my routes:
var app = angular.module('app', []);
app.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
$routeProvider.
when('/pneumatici/:chunka', {}).
when('/pneumatici/:chunka/:chunkb', {});
$locationProvider.html5Mode(true);
}]);
And in a controller I manage them this way:
app.controller('appCtrl', ['$scope', '$route', '$routeParams', '$location', function ($scope, $route, $routeParams, $location) {
$scope.$on('$routeChangeSuccess', function (current,previous) {
if (!($location.path().indexOf('/pneumatici') === -1)) {
$scope.chunka = $route.current.params.chunka; $scope.chunkb = $route.current.params.chunkb;
/** do my stuff with chunka and chunkb **/
} else {
window.location.href = $location.path();
}
});
I have no ngView, no template, nothing.
It works like a charm.
Please note the line where I actually force a page load in case the url is not intended to be managed by the controller appCtrl.
I was forced to do that because once I define my route to catch '$routeChangeSuccess' all links in the page when clicked are catched by angular and no page load occurs even if the link doesn't have the format defined with 'when'.
I would have like to do it with 'otherwise' but I could not understand how to, if doable.
Now the problem.
In the page of course I have links like just '/privacy.html', if I click them the page load is correctly triggered and I do see '/privacy.html' but unfortunately once there if I click the back button I can see the url of the browser changing to (let's say) /pneumatici/foo/bar but no page load is triggered.
Please note in the privacy.html page I have no angular routing defined, there's no .config no .when; there's an anagular app defined, with a controller, but no injection of '$routeProvider' anywhere, no definition of any route.
What is happening? What I am doing wrong?
Thanks for any help!
Update.
I found a viable solution adding:
angular.element("a").prop("target", "_self");
Angular routing is ignored for all 'a' elements with 'target' set to "_self", didn't know that.
Still if I look at this strategy as a whole doesn't sound very elegant to me and I would love to improve it. What I don't like is since I am defining the route in .config I should be able to tell angular to skip any url which do not match the format/path I defined there.
But I don't know if that is doable or not, does anyone know out there?
By turning on html5mode your app should be acting like it should intercept everything on the site by default (from '/'.)
From that perspective, it seems like $location.path() should work in your explicit override, but it isn't really correct ($location.url() would be) and the browser already has the correct URL, so maybe you can't force a reload with location.href = location.href in your specific browser.
Rather than going down that path, I would do the following to make it DRY:
If you add a base href:
<base href="/pneumatici/"></base>
and replace /pneumatici/ with / in your when clause(s):
$routeProvider.
when('/:chunka', {}).
when('/:chunka/:chunkb', {});
then you should just need this:
$scope.$on('$routeChangeSuccess', function (current,previous) {
$scope.chunka = $route.current.params.chunka;
$scope.chunkb = $route.current.params.chunkb;
/** do my stuff with chunka and chunkb **/
});
I think you should let Angular manage all your routes like this:
var app = angular.module('app', []).config(function($routeProvider, $locationProvider) {
$locationProvider.html5Mode(true).hashPrefix('!');
$routeProvider
.when('/',
{
controller: 'HomeController',
templateUrl: '/partials/home.html'
})
.when('/about',
{
controller: 'AboutController',
templateUrl: '/partials/about.html'
})
.when('/privacy',
{
controller: 'PrivacyController',
templateUrl: '/partials/privacy.html'
})
.when('/404',
{
controller: 'NotFoundController',
templateUrl: '/partials/404.html',
})
.otherwise({
redirectTo: '/404'
});
});
Notice the otherwise above. That tells angular to load the 404 page when a route is not recognized.