How can I use *ngIf condition in index.html in Angular2+. I want to load tags based on condition using *ngIf for that I have a value in local storage. Here is my code but it is not working.
<head *ngIf="localStorage.getItem('theme_token') === 'site-layout'"></head
When I used script tag it get the value of theme token in javascript.
<script>
var _val = localStorage.getItem('theme_token'); /// This part is working returning me the value.
</script>
Note: I don't want to hide. I have to render it using *ngIf
condition.
*ngIf wouldn't do the trick for you. *ngIf will work inside angular. Try this example may this helps. It worked with angular 8. In your app component.
constructor() {
if (localStorage.getItem('theme_token') === 'site-layout') {
const temp = document.createElement('link');
temp.innerHTML = '<link id="default-css" href="assets/somestyle.css" rel="stylesheet" media="all">'
;
const head = document.head;
while (temp.firstChild) {
head.appendChild(temp.firstChild);
}
}
}
You can load according to your condition.
You can't use *ngIf outside . We have multiple ways to do the same in pure javascript. Go with vanila js for no side-effetcs.
As far as I am aware Angular application (most of them) has entry point of <app-root></app-root> where the application is bootstrapped, means from this point angular will come into the picture(memory) and then you have your routes, other components, angular stuff the way you have structured the application.
Now you want to use *ngIf in index.html, so point is where you will bind this index.html i.e. with which component to supply your *ngIf variable and even if you will just add *ngIf='true' it will be rendered as it is without any effect or side-effect
What you can do (possibly)
Try to do with plain JS via <scrip></script> access DOM, change it the way you want
If you want to do with Angular only, then in app.component.ts in ngOnInit access the DOM and do it.
Browser doesn't know what it has to with *ngIf, It is the angular that knows the meaning of *ngIf and this particular point angular not loaded yet
Related
TL;TR; is below
I'm dealing with a huge application where everything is made super-generic to easily expend. One of the components is the dialog. Now before your answer is, use ngInclude or angular templates, let me explain how these work and why we would like to stick to them.
The flow of creating this dialog:
From somewhere in javascript a javascript function is called.
That constructs the container for the dialog. Position, widths, heights, gray background, etc.
Once that is present, a loading indicator will show up, while a GET request takes place to the back-end.
At the back-end Action, a view name is provided and a model.
This view (a .cshtml file) gets loaded into a string builder. To give you an idea of what happens, here is a piece of code where the view gets loaded.
var sb = new StringBuilder(1);
using (StringWriter sw = new StringWriter(sb))
{
.....
var helper = new HtmlHelper(viewContext, viewDataContainer);
using (helper.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
helper.RenderPartial(viewName, model);
}
return sb.ToString();
}
Then the string is returned, in javascript it is eventually set as html: diag.html(jsonResponse.data);
Now the view that I load, contains angular elements, like ng-controller and brackets to show something from that controller. Here is the .cshtml:
#model int
<div ng-controller="dialogGridColumnSelectionController as dgc" ng-init="dgc.init(#Model)">
<table>
<thead>
...
<tbody>
<tr ng-repeat="col in dgc.tableColumns">
<td>
<input type="checkbox" name="some" ng-value="dgc.hide"/>
</td>
<td>{{col.headerName}}</td>
<td>{{col.Description}}</td>
</tr>
</tbody>
</table>
</div>
A few statements:
ng-app was tried later, it should not be needed here because the <body> element already has this.
The dialog is within the <body> element.
No errors, no angular warnings.
{{col.headerName}} shows exactly as how it is shown, so angular is not working.
The controller and all the other required javascript files are already loaded on the page from where this dialog is opened. Also tried to add them here, no difference. I also tried to load angular there again, that does give me a warning that I tried to load it more than once. So the scripts are there.
Now I kind of doubted that this would work from the beginning, but I just want to make sure before we start a major rework of the dialogs.
So my question is, is it possible? Can a view that is initiated from .html() (in javascript) have angular? How do I "start" Angular? Or why is it not working with generated html?
TL;TR and EDIT:
After some digging, I eventually did this:
dialog.html(jsonResponse.data);//dialog is a created jquery element, jsonResponse.data contains the `.cshtml` content
if (options.angularCompile && options.angularScope) {//I've set them in options
options.angularCompile(dialog)(options.angularScope);//= $compile(dialog)($scope) or $compile(dialog.contents())($scope)
}
From the html above, it does fire the init function. That function correctly loads data (from other injected factories).
var self = this;
this.tablePageID = 0;
this.tableColumns = [];
$scope.hello = "hii";
this.init = function (tablePageID) {
self.tablePageID = tablePageID;
self.tableColumns = gridTableFactory.getTableColumns(tablePageID);
}
But once completed and the dialog is shown, it still has no angular working.
Worth noting is that the ng-repeat has done it job at the beginning, there are no items. Looks like it compiles and forgets.
Try executing $scope.$apply(); after using the $compile. You may need to inform angular to update its bindings. Also try to avoid using jQuery directly and use angular.element("#....") instead.
How can I use ng-include in such way that it's content will be loaded only once?
Here is what I have:
<div data-ng-if="%condition-1%" data-ng-include="%url-1%"></div>
<div data-ng-if="%condition-2%" data-ng-include="%url-2%"></div>
<div data-ng-if="%condition-3%" data-ng-include="%url-3%"></div>
...
In my case only one condition is true at some moment of time.
And any condition can change its value many times during page lifetime.
So ng-include will load the same content again and again.
How can I tell Angular to process ng-include only once - when the appropriate condition becomes true for the first time?
Loading them all at once will kill the page because every template is large and heavy.
Also there is no strict sequence of condition changes, for example, condition-3 may never become true during page lifetime - I'd like not to load url-3 content at all in this case.
Thanks!
UPDATE
Yes, template is already on cache. But it has a complicated internal structure like references to external images, iframes and so on - all this things are reloading each time when I'm using ng-include.
You have many solutions but only 2 come to my mind at the moment
1° Replace the ng-if for a ng-show, as the ng-if deletes the dom and all children scopes available, forcing the framework to make the request once again, while if you were using ng-show, the dom would only be hidden and the request would have only be made once.
2° If you do need to use ng-if and the content from the server is static, you could cache it on the javascript layer by manually accesing the $templateCache service provided by angular, or if the content you wish to load is html, you could either use the $templateCache service on the javascript layer or use the ng-template tag to preload that data.
Example:
<script id="url/you/want.html" type="text/ng-template">
<div>I am preloaded dom that responds to the url/you/want.html
requests made by this application
</div>
</script>
Cheers
How about using only one ng-include and using some logic in the controller to switch which source to use using a binding? This way only one will ever be loaded at a time.
Controller
function($scope) {
$scope.activeTemplate = null; //some default or even null
$scope.$watch('condition', function(newvalue) {
//whatever logic you need to switch template
if (newvalue == 'condition1') {
$scope.activeTemplate = '/path/to/condition1.html';
} else if (newvalue == 'condition2') {
$scope.activeTemplate = '/path/to/condition2.html';
} else {
$scope.activeTemplate = '/path/to/default.html';
}
});
}
This way only one template will ever be loaded at a time, and you've reduced the number of bindings from 3 to 1. (however you have added a watch so effectively from 3 to 2 maybe)
I've hooked up a lazy loader in Angular. It pulls in full templates and extracts key information from that full template in order to populate a partial. This full page template has script tags which load in and then register with the existing app. All of this works fine. My problem is that I'd like to remove the only use of jQuery in this approach.
The root issue is that the JS inside of something.js doesn't execute when using $element.html(), but it does execute when using $.html(), despite the script tag being placed in the DOM in both approaches.
Working code, including lazy loader and post-bootstrap registration of lazy-loaded JS:
$http.get("/path/to/file.html").success(function(response) {
// response is a full HTML page including <doctype>
var partial = getOnlyWhatWeNeed(response);
// partial is now something like: '<script type="text/javascript" src="/path/to/something.js"></script><div ng-controller="somethingCtrl">{{something}}</div>'
// i'd like the following to not rely on full jQuery.
$("#stage").html(partial);
$("#stage").html($compile(partial)($scope)); // it is necessary to do it once before compile so that the <script> tags get dropped in and executed prior to compilation.
});
I've tried what seems like the logical translation:
$element.html($compile(partial)($scope));
and the DOM is created properly, but the JS inside of the loaded <script> tag doesn't actually execute. My research suggested this was an $sce issue, so I tried:
$element.html($compile($sce.trustAsHtml(partial)($scope));
but i get the same result. the DOM is fine, but the JS doesn't actually execute and so I get undefined controller issues.
I've tried playing with $sce.JS and $sce.RESOURCE_URL but the docs didnt elaborate much so I'm not sure I know whether or not what I'm trying is even right.
I've also tried $element[0].innerHTML but I get the same result as $element.html().
Preemptive disclaimer: I can trust the incoming HTML/JS. I know it's inadvisable. This isn't my baby and it is much more complicated than I explained so please try to stay on topic so other people in this position may not have as hard of a time as I am :)
The $http.get happens in a provider, and the $element.html happens in a directive. I consolidated them to remove noise from the problem.
Jquery will find any script tags and evaluate them (either a direct eval or appending them to the head for linked scripts) when calling html(), see this answer. I'm assuming angular's jquery lite doesn't do this. You would need to effectively replicate what jquery is doing and look for script tags in the html you are appending.
Something like this (although I haven't tested it):
$http.get("/path/to/file.html").success(function(response) {
// response is a full HTML page including <doctype>
var partial = getOnlyWhatWeNeed(response);
// partial is now something like: '<script type="text/javascript" src="/path/to/something.js"></script><div ng-controller="somethingCtrl">{{something}}</div>'
var d = document.createElement('div');
d.innerHTML = partial;
var scripts = d.getElementsByTagName('script');
for (var i = 0; i < scripts.length; i++) {
document.head.appendChild(scripts[0]);
}
$("#stage").html($compile(partial)($scope)); // it is necessary to do it once before compile so that the <script> tags get dropped in and executed prior to compilation.
});
This is far from an ideal solution as it gives you no guarantee of when things are loaded and doesn't really handle dependencies across scripts. If you can control the templates it would be simpler to remove the scripts from them and load them independently.
I have a AngularJS application where I am loading data from a REST service.
Now what sometimes happens is that the brackets {{}} used to access values from scope are rendered and after that replaced by the real values. Now what I d like to do is add a ng-switch to the top DIV of the application and check whether a global var (e.g. pageLoaded (true|false)) is true or false. If its true, I d like to load the normal page, if its false, I d like to print something like "loading...". So my problem is how can I get notified (e.g. through a Angular Event) if all the data is ready, and is added to scope? Because after that I dlike to set pageLoaded to true.
Is there a way to do this in a generic way? I don't like to implement this per page.
Thanks a lot in advance.
Greets
Marc
You should use ng-cloak for that - http://docs.angularjs.org/api/ng.directive:ngCloak
For showing a loading panel, you can do something like:
<div ng-hide="true">Loading...</div>
So when angular finishes loading, ng-hide will occur and hide the loading panel.
Use ng-cloak to get rid of this sort of problems. And make sure you apply the ng-cloak directive to the body of the page so that this doesn't show up till the data is loaded properly. And use the following styling in your CSS file.
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
display: none;
}
Note: you can even create some other element or div, thats something like a popup or notification bar, which shows "please wait till the data is comnpletely loaded". Set this div to display:none initially and in the Ajax call, change the display property to block/inline as needed and finally make it dispay:none after the success call back.. :)
One of the solutions is you can use ng-bind instead of using {{}} which will show ugly {{}} when the value is not rendered.
<div ng-bind="value">Loading ...</div>
For anyone who is having a problem more to do with the actual question than OP's specific scenario:
I had a fragment that was getting loaded-in after/by the main partial that came in via routing.
I needed to run a function after that subpartial loaded and I didn't want to write a new directive and figured out you could use a cheeky ngIf
Controller of parent partial:
$scope.subIsLoaded = function() { /*do stuff*/; return true; };
HTML of subpartial
<element ng-if="subIsLoaded()"><!-- more html --></element>
I have some elements I want to only show the author of the document.
I can do something like this:
<div ui-show="currentUser == doc.user">Edit</div>
<div ui-show="currentUser == doc.user">Review</div>
Which is fine but because in my production code the ui-show is much longer than this example I don't want to copy and paste it everywhere that I need it.
I want to set a single variable that'll dynamically update as users log in and out or as the document gets updated with new / different users.
<div ui-show="isUser">Edit</div>
<div ui-show="isUser">Review</div>
I found that I could make isUser into a function.
<div ui-show="isUser()">Edit</div>
<div ui-show="isUser()">Review</div>
And write the conditions in the controller.
You have a couple of options to solve this problem:
1) Introduce a "global" or "parent" controller to your application. This will contain your isUser scope variable that you can basically set from any controller beneath this controller. Meaning that you can have a LogInController which will handle log off/in and can set that variable via $scope.isUser = false.
Here is a fiddle with an example of what this might look like: http://jsfiddle.net/digitalzebra/MrQrX/
2) Load different templates or includes based on whether or not the user is logged in/off. when using <ng-include src="partialTemplate"> the src attribute is actually an expression. So, you can toggle what template is actually loaded based on the value of that expression. You can then set the value in your controller and dynamically change which template is loaded: $scope.partialTemplate = "loggedOff.html"