I'm trying to integrate translate partial loading into my app, but there is a flicker on initial load of the translation file (when changing states). I'm using translate-cloak as suggested but I still get the same effect.
Note The flicker only happens when when changing the partial, not the language.
Here is my setup
Config
app.config(['$translateProvider', function($translateProvider){
$translateProvider.useLoader('$translatePartialLoader', {
urlTemplate: 'dist/locales/{lang}/{part}.json'
});
$translateProvider.preferredLanguage('en-US');
}])
Controller
app.controller('appCtrl', function($translate, $translatePartialLoader){
$translatePartialLoader.addPart('app');
$translate.refresh()
});
Template
<div translate="HEADLINE"></div>
Layout
<html ng-app="app" >
<head>
<title>SportProphecy</title>
<script src="/dist/js/vendors.min.js"></script>
<script src="/dist/js/app.min.js"></script>
</head>
<body ui-view ng-cloak translate-cloak>
</body>
</html>
Am I missing something? I have read through all the documentation out there regarding translate-cloak.
PS: I tried creating a fiddle but for some reason the json files aren't being requested.
This is called "Flash of untranslated content" from the angular translate docs. You might want to check it out here.
Basically, you need to define atleast one language to be loaded synchronously. So go ahead and add translations for your preferred language ('en' in your case) in the app.config
app.config(['$translateProvider', function($translateProvider){
$translateProvider.useLoader('$translatePartialLoader', {
urlTemplate: 'dist/locales/{lang}/{part}.json'
});
$translateProvider.preferredLanguage('en-US');
$translateProvider.translations('en-US', {
'HEADLINE': 'My Fancy App!'
});
}])
svikasg is completly right. Though as a workarround you can do the following inside your controller:
$translatePartialLoader.addPart('my-part'); //this should be already in your controller
var vm = this;
function updateCloak () {
vm.cloak = $translate.instant('some_translation_id') === 'some_translation_id';
}
$rootScope.$on('$translateLoadingEnd', function () {
$timeout(updateCloak);
});
updateCloak();
And then in your page container where you're using the translations add an ng-if="ctrl.cloak". You can also use ng-class if you prefer.
Of course you need to inject $rootScope, $translate, $timeout in your controller also.
This code will set a cloak scope property to true as long as an expected translation is not available, allowing you to hide the parts using the translations in the same file than the one you're checking.
Note: if you're not using controllerAs syntax replace all the vm references with scope and remove the ctrl. from the ng-if.
Related
It's my first time using routeProvider with angular js. The project has two pages so far: home and user. Both pages load completely and interact with their controllers correctly. However, the text "Loading ..." appears at the bottom of both pages.
[NOTE: I previously reported that the "Loading ..." text was only appearing on one of the pages. But it actually appears on both pages.]
Of course, having "Loading ..." on the page is completely unacceptable, but I have no idea why it's there. Here's the HTML that angular has inserted into the page:
<div block-ui-container="" class="block-ui-container ng-scope">
<div class="block-ui-overlay"></div>
<div class="block-ui-message-container" aria-live="assertive" aria-atomic="true">
<div class="block-ui-message ng-binding" ng-class="$_blockUiMessageClass">Loading ...</div>
</div>
</div>
And here is the routeProviderCode:
var projectApp = angular.module("mainProjectModule",
['ngRoute', 'blockUI', 'ngSanitize', 'ui.bootstrap']);
function resolver(path) {
return {
resolver: ['$q','$rootScope',
function($q, $rootScope) {
var deferred = $q.defer();
require([path], function() {
$rootScope.$apply( function() {
deferred.resolve();
});
});
return deferred.promise;
}
]
}
}
projectApp.config(['$routeProvider',
function($routeProvider) {
var pageNames = [ 'home', 'user' ];
pageNames.forEach( function(pageName) {
$routeProvider
.when('/'+pageName, {
templateUrl: '/project/ajs/'+pageName+'.html',
resolve: resolver(pageName+'Controller')
});
});
$routeProvider.otherwise({redirectTo: '/home'});
}
]);
// Bootstrap Angular when DOM is ready
angularAMD.bootstrap(projectApp);
Is there some kind of signalling mechanism that my controller should be using to clear this string?
Answering my own question here, and it's more of a work-around than a real answer. I would still appreciate knowing the correct way to resolve this issue.
I started the project with a template that included angular-block-ui (see https://github.com/McNull/angular-block-ui). It appears this block-ui is being invoked behind-the-scene when the route provider loads the template (see the original route provider code above). I'm not 100% certain because the "Loading ..." message is not actually blocking anything, but it is the sort of default blocking message I might expect to see while loading a template. The invocation is outside my control (as far as I can tell), so without debug-stepping into the angular-js code (no thanks!) it's not clear what is happening or why.
The angular-block-ui web site says that you can stop the blocking with this call:
blockUI.stop();
I added this code to my controller and it had no effect.
So the workaround is that I've added a directive to the project's css file to hide the message like this:
.block-ui-overlay + .block-ui-message-container {
display:none;
}
Obviously this is a hack, but it's simple and effective.
I'm writing an AngularJS application, but I'm quite new to it, so in the code below, please point out any issues that you have.
I do have my AngularJS application defines like that:
var OfficeUIModule = angular.module('OfficeUI', ['ngSanitize']);
Then, I do have a couple of services that loads data from various JSon files.
But i won't post the code here, because I do believe that irrelevant.
Then I do have my controller which basically looks like this:
OfficeUIModule.controller('OfficeUIController', function($scope) { });
Now, I want the controller to execute some logic on startup and show a DIV element on it.
Here's the case what I want to do:
Show a Loading DIV element during initial setup.
Show a Displaying DIV element when the initialization has been done.
Let's say that in my controller I do have method to initialize the application:
OfficeUIModule.controller('OfficeUIController', function($scope) {
function Init() {
// I'll load all the data from the services here.
}
});
In order to call this method on startup, I just define it in my controller like so:
OfficeUIModule.controller('OfficeUIController', function($scope) {
Init();
function Init() {
// I'll load all the data from the services here.
}
});
And the HTML which I would like to use is the following:
<body>
<div id="loading">LOADING</div>
<div id="displaying">DISPLAYING</div>
</body>
When the page is initially loaded, I would like to show the element 'Loading'.
When the page has been loaded, I would like to show the element 'Displaying'.
In order to make this testable, I've changed my controller method Init to include a timeout of 5 seconds, just like below:
OfficeUIModule.controller('OfficeUIController', function($scope) {
Init();
function Init() {
setTimeout(function() {
alert('Page has been loaded. When you click OK the displaying DIV should be showed.');
}, 5000);
}
});
So, in this particular case, I would like to make sure that the page is being loaded, displaying the LOADING div and after 5 seconds, display the DISPLAYING div.
I would also like not to see any flickering, and therefore ng-cloak can be used I've read, but I can't manage to configure it correctly.
Here's what I've tried already:
LOADING
DISPLAYING
However, that setup doesn't work quite well.
I've included a Plunker so that you can test the solution you provide.
It happens because you are using setTimeout with anonymous function and angularjs isn't aware of model change
http://plnkr.co/edit/FJ2lnrmtG7P3HXZuxRkH?p=preview
if you use angularjs $timeout it works
$timeout(function() {
$scope.initialized = true;
}, 5000);
you could also use $scope.$digest() or $scope.$apply to make it work
You can use the angular version of window load event.
angular.element($window).bind('load', function(e) {
// code to execute
});
In an Angular (1.3) app, I am displaying list of records using ng-repeat. There is a directive with a template inside the ng-repeat. Within the template I'm using ShareThis controls which are activated after the DOM is loaded.
On initial load of the app, the ShareThis Javascript works correctly and activates the buttons. On route change it does not activate. I've found to references to activate the controls manually via stButtons.makeButtons() or stButtons.locateElements();, but I'm unsure where to call this function in the directive or page cycle. I've tried within:
the directive link function - using $timeout or scope.$watch
the template <script>stButtons.locateElements();</script> - activates before model binding
the controller after binding - activates before DOM rendered
My understanding is the function to activate needs to be called after binding and after DOM rendering, but Angular does not know when the DOM is ready. There is a method to dynamically render the ShareThis controls using only Javascript, but I want the HTML defined in the template not Javascript for this case.
I've seen several questions out there related, but none of the answers seem to work 100% for my scenario (and many are broken as of Angular 1.3).
item-list.html (view)
<div ng-repeat="item in vm.itemList">
<item-directive item="item"></item-directive>
</div>
item-list.cs (controller)
{ ... vm.itemList = getItems(...) ... }
item-directive.js (directive)
(function () {
angular.module('app');
function itemDirective() {
var directive = { templateUrl: 'item.html', link: linkFunc, controller: ItemDirective };
return directive;
function linkFunc(scope, element, attr, ctrl) { var item = scope.item }
}
ItemDirective.$inject = ['$scope'];
function ItemDirective($scope) { ... }
}
item.html (directive template)
...
<div class="item-share-section">
<span class='st_sharethis_large' st_url="{{vm.item.url}}" st_title="{{vm.item.name}}"></span>
</div>
...
if I understood well, you want to call the function after the dom is completely render, right? Try this inside the postLink of your directive:
$scope.$watch('viewContentLoaded', stButtons.locateElements())
While my solution is a little hackish, I still prefer it over using $watch, since that is inefficient. Instead, I initialize the function which loads the buttons when the particular view you want to load the buttons with is rendered. The technique is as follows:
Here is the function which you should put in your controller:
$scope.loadShareThis = function() {
stButtons.makeButtons();
}
You'd then add to your item-list.html as such:
<div ng-repeat="item in vm.itemList" ng-init="loadShareThis()">
<item-directive item="item"></item-directive>
</div>
The dynamic URL's might give you additional problems, but that's another issue all together.
New to ngView, I wrote some code a little bit different from sample code about ngView. Usually the URL to manipulate ngView is outside of it, whereas in my code the link is within the ngView template. The problem is when I click on the ShowOrder link below, I get a TypeError, function $scope.show() is not defined. I printed the $scope object both before and after I click the link. My suspect is confirmed. Before the click $scope has function Show() defined, afterward $scope becomes naked as is just born by angular, without any customized properties.
Please help me to find out where I did wrong, maybe a little bit inside on how the ngView is loaded, destroyed within a scope. Thanks.
Here is the main html code:
<body ng-app="sampleApp" ng-controller="rootCtrl">
<div ng-view></div>
</body>
App.js:
var sampleApp = angular.module('sampleApp', ['ngRoute']);
sampleApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/ShowOrder', {
templateUrl: 'templates/show_order.html',
controller: 'ShowOrderController'
});
}]);
sampleApp.controller('ShowOrderController', function($scope) {
$scope.show = function {console.log("here")}
});
template code:
<h2>Show Orders</h2>
Show Order
{{show()}}
Yours says
$scope.show = function {console.log("here")}`
should be
$scope.show = function() {console.log("here");}
I kind of stumbled on a solution to my problem. Every time when I click a href to change route, the corresponding controller get re-created. It seems Angular create a new $scope.prototype first, then tries to do the binding with directives in HTML. When it meets Show(), Angular proclaims it to be undefined. Finally I take the advice of #Lizzie to use ng-click to make my life easier.
I know I can apply to the template/templateURL, but currently the content will load from another place in my application.
JSbin: http://jsbin.com/imuseb/1/
HTML
<html ng-app="app">
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.min.js"></script>
</head>
<body>
<div alert>here</div>
</body>
</html>
JS code:
var app = angular.module('app', []);
app.directive('alert', function (){
return {
link: function (scope, element, attrs) {
element.on("click", function (){
alert("here");
});
}
};
});
$(function (){
$("body").append("<div alert>here too</div>");
});
The new DOM must be accessible to the angular app in order to be compiled correctly. Here is one way to do this (not the only way). For applying the new DOM to the app's $rootScope, change this:
$(function (){
$("body").append("<div alert>here too</div>");
});
to:
app.run(function($rootScope){
$rootScope.$apply($("body").append("<div editable>here too</div>"));
});
According to the Angular docs:
$apply() is used to execute an expression in angular from outside of the angular framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
It would be best if you could load your extra content from within the angular framework. Check out ng-include.
If that can't be used to do what you need, you'll have to manually call the angular compile service on the element, to compile it and link it onto the scope yourself using $compile.
Compiling elements touches the DOM, and therefore should be done within a custom directive if possible.