*ngIf or [hidden] not working correctly - html

I'm trying to allow for hiding of certain sections of the project I'm working on via user toggle. It saves in the database and gets pulled when the page is loaded in the constructor using the following code
this.http.get(`api/section/get/${this.id}`, this.id).subscribe(res => {
this.section = res.json()[0];
this.sect = res.json();
console.log(this.section);
this.hideIntro = this.sect[0].hideIntro;
this.hideMainVideo = this.sect[0].hideMainVideo;
this.hideHandout = this.sect[0].hideHandout;
this.hideQuiz = this.sect[0].hideQuiz;
console.log("Hide Intro = " + this.hideIntro);
console.log("Hide Main = " + this.hideMainVideo);
console.log("Hide Handout = " + this.hideHandout);
console.log("Hide Quiz = " + this.hideQuiz);
});
The HTML is as follows...
<div class="row classMainBackground col-md-12" *ngIf="!hideIntro">
...content...
</div>
For some reason, no matter what I do, whether I change it to *ngIf="hideIntro == false" or even use [hidden]="hideIntro", it is not working.
Even the console logs in the .ts file show up correctly. Is there a reason why this is not working for me? I've used it in other positions and it works fine there...
Does it have something to do with assigning it in the constructor or something?
Thanks in advance!

Angular change detection runs in response to use interaction with the component. If values are updated outside of that event handling (such as after an HTTP request), you need to manually tell the component that it has changed.
constructor(private changeDetector: ChangeDetectorRef){}
this.http.get(`api/section/get/${this.id}`, this.id).subscribe(res => {
[...]
this.changeDetector.markForCheck();
})
More in depth reading: https://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html

I ended up solving the problem by using {{!section.hideIntro}} in the HTML instead of trying to define a new variable to pass that boolean to.
I believe the answer was a combination of what #Vlad274 and #ConnorsFan were mentioning.
the HTML was returning an [object object] for {{hideIntro}} and it seems like there's a delay between the assigning the new value data from the GET response before the DOM actually loads.
Grabbing the data right from the GET respone variable ended up doing the trick.

Related

ngOnChanges only works when it's not the same value

So basically I have a modal component with an input field that tells it which modal should be opened (coz I didn't want to make a component for each modal):
#Input() type!:string
ngOnChanges(changes: SimpleChanges): void {
this.type = changes["type"].currentValue;
this.openModal();
}
that field is binded to one in the app component:
modalType = "auth";
HTML:
<app-modal [type] = modalType></app-modal>
In the beginning it's got the type "auth" (to login or register), but when I click on an icon I want to open a different modal, I do it like so:
<h1 id="options-route"
(click) ="modalType = 'settings'"
>⚙</h1>
but this only works the first time, when modalType already has the value "settings" the event doesn't trigger even though the value has technically changed
I think the problem is that it's the same value because i tried putting a button that does the exact same thing but with the value "auth" again and with that it was clear that the settings button only worked when tha last modal opened was auth and viceversa
any ideas? I want to be able to open the settings modal more than once consecutively possibly keeping onChange because ngDoCheck gets called a whole lot of times and it slows down the app
You need to include the changeDetectorRef, in order to continue in this way.
More about it https://angular.io/api/core/ChangeDetectorRef
Although, a better and a faster alternative is the use of a behavior Subject.
All you have to do is create a service that makes use of a behavior subject to cycle through each and every value exposed and then retrieve that value in as many components as you want. To do that just check for data changes in the ngOnInit of target component.
You may modify this for implementation,
private headerData = new BehaviorSubject(new HeaderData());
headerDataCurrent = this.headerData.asObservable();
changeHeaderData(headerDataNext : HeaderData) {
this.headerData.next(headerDataNext)
console.log("subscription - changeUserData - "+headerDataNext);
}
Explanation:
HeaderData is a class that includes the various values that can be shared with respective data types.
changeHeaderData({obj: value}), is used to update the subject with multiple values.
headerDataCurrent, an observable has to be subscribed to in the target component and data can be retrieved easily.
I mean i'm too l-a-z-y to use your slightly-not-so-much-tbh complicated answers so I just did this:
I added a counter that tops to 9 then gets resetted to 0 and I add it to the value
screwYouOnChangesImTheMasterAndYouShallDoMyBidding = 0;
//gets called onClick
openSettings(){
if(this.screwYouOnChangesImTheMasterAndYouShallDoMyBidding === 9){
this.screwYouOnChangesImTheMasterAndYouShallDoMyBidding = 0;
}
this.screwYouOnChangesImTheMasterAndYouShallDoMyBidding = this.screwYouOnChangesImTheMasterAndYouShallDoMyBidding + 1;
this.modalType = "settings"+this.screwYouOnChangesImTheMasterAndYouShallDoMyBidding;
}
then in the child component I just cut that last character out:
ngOnChanges(changes: SimpleChanges): void {
let change = changes["type"].currentValue as string;
change = change.substring(0, change.length - 1);
this.type = change;
this.openModal();
}
works like a charm 😂

Angular Conditional Views?

I have inherited a mess of a Angular project. I've never really messed with Angular too much but know MVC well enough to feel like I can learn. My question is I have a property of a JSON object that I want to return a different views for. (one is an archived state and one is a non-archived state) As they both have different view templates, how would I return the non-archive template if the json.status == 'archived'
I have the following as my current StateProvider's templateURL property.
templateUrl: appConfig.viewPath + 'non-archived.html?v=' + appConfig.version
should I just return multiple template urls here? Or do I have to create a whole new url path?
Thanks!
I've gone down this road a few times, I don't think I've found the optimal way yet, but I've learned a few things.
It really all depends on when you have access to your json-object. You can pass a function to templateUrl, and send in a service.. (A service that returns your current json-object could be great, but how would you update it? Probably when you change route right? Then you have a egg-hen problem. You can't decide route until you have the json-object, but you don't have the json-object until you change route.)
But IF you have access to the json-object you could do something like this:
templateUrl: function(){
var tpl = (json.status == 'archived') ? 'archived.html?v=' : 'non-archived.html?v=';
return appConfig.viewPath + tpl + appConfig.version
}
But my guess is that you don't have access to the json-object until after the route has loaded.
Then I'd say the easiest way (but maybe not so pretty) is to have just one template. $scope.json = json in the controller:
<div ng-if="json.status == 'archived'">
<h1>ARCHIVED</h1>
...
</div>
<div ng-if="json.status != 'archived'">
<h1>NOT ARCHIVED</h1>
...
</div>
Or if you think that is too cheap, declare two routes. The whole "create a whole new url path" is not as painful as you might think. It'll be considerably less complex than trying to wedge out a value from a route before it has loaded.
1: Try this. send json.status in $stateParams and apply condition inside stateProvider :
$stateProvider.state('home', {
templateProvider: ['$stateParams', 'restService' , function ($stateParams, restService) {
restService.getJson().then(function(json) {
if (status.status == 'archived') {
return '<div ng-include="first.html"></div>';
} else {
return '<div ng-include="second.html"></div>';
}
})
}]
});
2 : or simply in view you can try this:
<div ng-if="json.status == 'archived'">
<h1>ARCHIVED</h1>
...
</div>
<div ng-if="json.status != 'archived'">
<h1>NOT ARCHIVED</h1>
...
</div>

Show different default page depending on stored value

I am trying to make an windows store app where the default page (first page that comes up when app loads) changes depending on stored value.
I have following files
- js
|- default.js
- default.html
- page_A.html
- page_B.html
default.js has the following code:
if (localStorage["value"] == undefined || localStorage["value"] == "pageA") {
localStorage["value"] = "pageA";
//WinJS.Navigation.navigate("page_A.html");
window.location.assign = "page_A.html";
} else {
localStorage["value"] = "pageB";
//WinJS.Navigation.navigate("page_B.html");
window.location.assign = "page_B.html";
}
WinJS.Navigation code does not work at all. So I tried using window.location and what's happening is instead of loading the actual page, it loads an empty page as shown below.
I tried using both href and assign for windows.location object. What's interesting is that it seems like href and assign loads the page because if I have page_A/B.js associated with pageA/B.html and have a simple console.log statement, then the log statement does get logged, but it does not render the page.
Any ideas? I've been stuck for a while.
Try putting your default.js at the root of your project, next to page_A.html and page_B.html, or, and I don't know if this works, you can try calling those pages with ..\page_X.html.
Also, you can add an error handler function to your navigate in case there's something else going on that you're not seeing.
WinJS.Navigation.navigate('page_A.html', {}).then(function () {
// it worked!
}, function (err){
// something went wrong
});

Outer container 'null' not found.

used jssor slider , i have some pages with same jssor slider , some pages are working fine , but some pages comes Outer container 'null' not found. bug , can any one help on this ?
I had a similar problem, so did some digging to see what the issue was.
The setup starts with the initial call, here's the snippet from the demo site
http://www.jssor.com/development/index.html
var jssor_slider1 = new $JssorSlider$("slider1_container", options);
which, among setting up all kinds of utility functions- more importantly does this
function JssorSlider(elmt, options) {
var _SelfSlider = this;
...
// bunch of other functions
...
$JssorDebug$.$Execute(function () {
var outerContainerElmt = $Jssor$.$GetElement(elmt);
if (!outerContainerElmt)
$JssorDebug$.$Fail("Outer container '" + elmt + "' not found.");
});
}
so at this point, it's trying to collect the string you passed, which is the elmt variable- which is for what? Well let's take a look at that $GetElement function in jssor.js
_This.$GetElement = function (elmt) {
if (_This.$IsString(elmt)) {
elmt = document.getElementById(elmt);
}
return elmt;
};
So, really, what it comes down to is this line for finding the element.
elmt = document.getElementById(elmt);
So the base of this error is
"We tried to use your string to find a matching ID tag on the page and it didn't give us a valid value"
This could be a typo, or another line of code modifying/removing the DOM.
Note that there are some scripts try to remove or modify element in your page.
Please right click on your page and click 'Inspect Element' menu item in the context menu.
Check if the 'outer container' is still there in the document. And check if there is another element with the same id.
Check if "Slider1_Container" is present or Used.
In my case, I didn't have it in my html, but still I had added the js.
Removing js resolved my issue.

Spotify List objects created from localStorage data come up blank

I'm working on a Spotify app and trying to create a views.List object from some stored information in our database. On initial load, a POST is made to get the necessary info. I store this in localstorage so each subsequent request can avoid hitting the database and retrieve the object locally. What's happening though is the List objects I create from localstorage data come up blank, while the POST requests work just fine.
Here is the snippet I'm using to create the list:
var temp_playlist = models.Playlist.fromURI(playlist.uri);
var tempList = new views.List(temp_playlist, function (track) {
return new views.Track(track, views.Track.FIELD.STAR |
views.Track.FIELD.NAME |
views.Track.FIELD.ARTIST |
views.Track.FIELD.DURATION);
});
document.getElementById("tracklist").appendChild(tempList.node);
playlist.uri in the first line is what I'm retrieving either from a POST or from localstorage. The resulting views.List object (tempList) looks identical in both cases except for tempList.node. The one retrieved from localstorage shows these values for innerHTML, innerText, outerHTML, and outerText in console.log:
innerHTML: "<div style="height: 400px; "></div>"
innerText: ""
outerHTML: "<div style="height: 400px; "></div>"
outerText: ""
Whereas the one retrieved via POST has the full data:
innerHTML: "<div style="height: 400px; "><a href="spotify:track:07CnMloaACYeFpwgZ9ihfg" class="sp-item sp-track sp-track-availability-0" title="Boss On The Boat by Tosca" data-itemindex="0" data-viewindex="0" style="-webkit-transform: translateY(0px); ">....
innerText: "3Boss On The BoatTosca6:082....
and so forth..
Any help would be greatly appreciated
Solved this.
I am using hide() and show() to render the tabs in my app. I was constructing the tracklist and then show()ing the div which led to a blank tracklist. If I simply show() the div and then construct the tracklist it works fine.
The reason (I think) it was working for POSTs is because the tracklist was retrieved from the database and the slightly longer loading time probably meant the tracklist was constructed after the div's show() executed. With localStorage I guess the tracklist was constructed before the div was even shown, leading to the error.
Using, the local storage, I did it this way :
sp = getSpotifyApi(1);
var m = sp.require("sp://import/scripts/api/models");
var v = sp.require("sp://import/scripts/api/views");
var pl;
pl = m.Playlist.fromURI(uri);
var player = new v.Player();
player.track = pl.get(0);
player.context = pl;
var list = new v.List(pl);
XXXXX.append($(list.node));
Hope, it will help, as it's working for me
I think I've actually managed to solve this and I think it's bulletproof.
Basically I was trying to solve this by trying to convince the API that it needed to redraw the playlist by hiding things/scrolling things/moving things which worked occasionally but never consistently. It never occurred to me to change the playlist itself. Or at least make the API think the playlist has changed.
You can do so by firing an event on the Playlist object.
var models = sp.require('$api/models');
...
// playlist is your Playlist object. Usually retrieved from models.Playlist.fromURI
playlist.notify(models.EVENT.CHANGE, playlist);
These are just standard Spotify functions and the list updates because it thinks something has changed in the playlist. Hope this helps someone!