Angular 2 Transclusion with Template Outlet - javascript

I have a Component with view like below:
<div class="dropdown user-select">
<div class="gray-input dropdown-toggle" type="button" id="assigned_to" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
<ng-content select="[selected-item-tmpl]"></ng-content>
<span class="caret"></span>
</div>
<ul class="dropdown-menu" aria-labelledby="assigned_to">
<li *ngFor="let item of items"
(click)="itemClick$.next(item)">
<ng-content select="[list-item-tmpl]"></ng-content>
</li>
</ul>
</div>
According to this and this, I now know that Transclusion with ngFor will not work.
But according to this, if I change my component to following:
<div class="dropdown user-select">
<div class="gray-input dropdown-toggle" type="button" id="assigned_to" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
<ng-content select="[selected-item-tmpl]"></ng-content>
<span class="caret"></span>
</div>
<ul class="dropdown-menu" aria-labelledby="assigned_to">
<li *ngFor="let item of items"
(click)="itemClick$.next(item)">
<template [ngTemplateOutlet]="template"
[ngOutletContext]="{item: item}">
</template>
</li>
</ul>
</div>
For brevity the Component.ts after change:
#ContentChild(TemplateRef) template: TemplateRef<any>;
and use like following:
<ds-combobox [items]="attendeeService.users"
(itemClicked)="applySelectedUser($event)">
<div selected-item-tmpl>
--> <img *ngIf="newTask.assignedTo.photo" [src]="newTask.assignedTo.photo">{{newTask.assignedTo.name}}
<div *ngIf="!newTask.assignedTo.photo && newTask.assignedTo.initials" class="pull-left photo-avatar">
<div>{{newTask.assignedTo.initials}}</div>
</div>
</div>
<div list-item-tmpl>
<template let-item="item"><b>{{item.name}}</b></template>
</div>
</ds-combobox>
Then I do not get any errors and it seems to run but the template for the ngTemplateOutlet seems to take the template from the first slot and ends up being <img src="null">. If I remove the first slot and just leave <template let-item="item"><b>{{item.name}}</b></template> then it seems to work fine. So basically, how do I combine using Transclusion with Template Outlet in the same component?
NOTE:
After thinking for a while, I just reversed the <template> position with <ng-content> and it seems to work now as expected. But the question still remains why though?

Related

Bootstrap Dropdown Button not displaying V-For Elements

I have the following Vue.js (3) component, which uses Bootstrap (5):
<template>
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle btn-link text-decoration-none text-dark" type="button" id="languageDropdown" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ languages[$i18n.locale] }}
</button>
<div class="dropdown-menu" aria-labelledby="languageDropdown" v-for="(value, key) in languages" v-bind:key="key">
<a class="dropdown-item" href="#" #click="changeLanguage(key)">{{ value }}</a>
</div>
<li v-for="(value, key) in languages" v-bind:key="key">
{{ key }}: {{ value }}
</li>
</div>
</template>
<script>
export default {
name: "LanguageSwitcher",
data() {
return {
languages: {
'en': 'English',
'de': 'Deutsch',
}
};
},
methods: {
changeLanguage: function(lang){
this.$i18n.locale = lang;
}
}
};
</script>
Without the v-for everything works fine. But as soon, as I add it, only the first element of the object languages gets rendered.
As you can see, the list is rendered correctly, with both languages.
Only in the dropdown, "Deutsch" (German) is not rendered.
Is this a bug with Bootstrap/Vue or am I missing something?
im able to solve this by moving the v-for syntax to a template
so instead of this:
<div class="dropdown-menu" aria-labelledby="languageDropdown" v-for="(value, key) in languages" v-bind:key="key">
<a class="dropdown-item" href="#" #click="changeLanguage(key)">{{ value }}</a>
</div>
do this:
<div class="dropdown-menu" aria-labelledby="languageDropdown">
<template v-for="(value, key) in languages" v-bind:key="key">
<a class="dropdown-item" href="#" #click="changeLanguage(key)">{{ value }}</a>
</template>
</div>

comment the below div structure the error is dissappearing

After upgrading to Angular 5 I am facing this error:
ExpressionChangedAfterItHasBeenCheckedError: Expression has
changed after it was checked. Previous value: 'ngIf: undefined'.
Current value: 'ngIf: null'.
If I comment the below div structure the error is disappearing
<div *ngIf="sportsServ?.relatedsportssData" class="stars stars-default pinnedSection">
Any idea how to fix this?
<!-- language-all: lang-or-tag-here -->
<div class="playerpobUpBox playerWindow kPopupConfirmationBox" id="greaterAlertHeader">
<div class="row playerPopUpGridCollection playerPopUpContent lineHeightInputs">
<div class="playerContent"></div>
</div>
<div class="clearFloat"></div>
<div class="row playerPopUpFooter textAligncenterImp">
<button class="commonBtn" type="button" id="greaterAlertOKHeader">OK</button>
</div>
<div class="clearFloat"></div>
<div class="clearFloat"></div>
</div>
<progress-circle></progress-circle>
<div class="leftSlider toggleShow">
<div class="fixedLeftSlider">
<div class="leftSliderBox">
<div class="stars-group" id="accordion">
<div class="stars stars-default recentsportss">
<div class="stars-heading active">
<h4 class="stars-title">
<a data-toggle="collapse" href="#recentsportss" aria-expanded="true">
<i class="fa fa-angle-right starsCaret"></i>
{{recentsportss}}
</a>
<span>{{lastTenViewsportss}}</span>
</h4>
</div>
<div id="recentsportss" class="stars-collapse collapse in" aria-expanded="true">
<div class="stars-body">
<ul *ngFor="let item of recentsportssList; let k=index;">
<li class="recentList" (click)="opensports($event,item.sportsNo,item.packageName,item.pinnedFlag,item.originatingNetwork);$event.stopPropagation();" [class.active]="currentsportsId == item.sportsNo" *ngIf="k<20">
<span> {{item.packageName}}
<i id="pinToggle" class="recentsportssRightIcon" (click)="pinUnpin($event,item.sportsNo); $event.stopPropagation();"></i>
</span>
</li>
</ul>
</div>
</div>
</div>
<div class="stars stars-default pinnedSection">
<div class="stars-heading">
<h4 class="stars-title">
<a data-toggle="collapse" href="#pinnedSection" aria-expanded="true">
<i class="fa fa-angle-right starsCaret"></i>
{{pinnedsportss}} & {{titles}}
</a>
</h4>
</div>
<div id="pinnedSection" class="stars-collapse collapse in" aria-expanded="true">
<div class="stars-body pad0Imp">
<label class="pinnedsportsSubTitle">sportss</label>
<ul *ngFor="let item of pinnedsportssList; let i=index;">
<li class="recentList" (click)="opensports($event,item.sportsNo,item.packageName,item.pinnedFlag,item.originatingNetwork);$event.stopPropagation();" [class.active]="currentsportsId == item.sportsNo" *ngIf="i<20">
<span>{{item.packageName}}
<i id="pinToggle1" class="recentsportssRightIcon" (click)="pinUnpin1($event,item.sportsNo); $event.stopPropagation();"></i>
</span>
</li>
</ul>
<!--<ul>
<li>sports Number One<i class="recentsportssRightIcon"></i></li>
<li>sports Two<i class="recentsportssRightIcon"></i></li>
<li>sports Three<i class="recentsportssRightIcon"></i></li>
</ul>-->
<label class="pinnedsportsSubTitle borderTopTitle">Titles</label>
<ul *ngFor="let item of pinnedTitlesList;let k=index;">
<li class="recentList" (click)="navigateTitlePage($event,item.titleNo);$event.stopPropagation();" *ngIf="k<20">
<span>{{item.titleName}}
<i id="pinTitle1" class="recentsportssRightIcon" (click)="pinUnpintitle($event,item.titleNo);$event.stopPropagation();"></i>
</span>
</li>
<!--<li>Title Name Number One<i class="recentsportssRightIcon"></i></li>
<li>Title Name Number two<i class="recentsportssRightIcon"></i></li>
<li>Title Number 3<i class="recentsportssRightIcon"></i></li>-->
</ul>
</div>
</div>
</div>
<div *ngIf="sportsServ?.relatedsportssData" class="stars stars-default pinnedSection">
<div class="stars-heading">
<h4 class="stars-title">
<a data-toggle="collapse" href="#relatedsportss" aria-expanded="true">
<i class="fa fa-angle-right starsCaret"></i>
{{sportsServ?.relatedsportssHeading}}
</a>
</h4>
</div>
<div id="relatedsportss" class="stars-collapse collapse in" aria-expanded="true">
<div class="stars-body">
<ul>
<li *ngFor="let item of sportsServ?.relatedsportssData" (click)="selectedsports($event,item.sportsId,item.sportsName);$event.stopPropagation();" [class.active]="currentsportsId == item.sportsId">{{item.sportsName}}</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="showHideLeftSliderBtn"></div>
</div>
</div>
<router-outlet></router-outlet>
<!--<div><sportsSearch style="display:none;" (sportsSearchEmitter)="sportsSearchEmitter($event)"></sportsSearch></div>-->
Probably you're getting this value asynchronously, you may try to:
Use changeDetection: ChangeDetectionStrategy.OnPush on your #Component (before selector), in your .ts file, don't forget to import changeDetection from "#angular/core";
Try to not get the value asynchronously, using setTimeout() function to get the value;
Inject cdr: ChangeDetectorRef on your module and import it from your "#angular/core", after that, on your implemented ngAfterViewChecked just put this.cdr.detectchanges.
To provide a better answer, you need to post your .ts code, and remove some lines to make the code cleaner. Hope it helps!

Vue for loop for objects not working

I am trying to create a top menu with dropdown buttons when iterating over an object that looks like this:
links: {
store: 'store',
language: [{'target': '/english'},{'target': '/norwegian'}],
login: 'user/login'
}
I have tried to achieve that by making a loop that looks like this:
<div v-for="(value, key) in links" :key="key" class="control">
<div v-if='Array.isArray(value)'>
<dropdown-menu :menu="value"></dropdown-menu>
</div>
<div v-else>
<a :href="value">
{{ key }}
</a>
</div>
</div>
But, I get errors:
Property or method "value" is not defined on the instance but
referenced during render. Make sure to declare reactive data
properties in the data option.
Even if I declare the value and key with initial values of null, it is not rendering anything:
data() {
return {
data: null,
value: null,
key: null,
};
},
This is the dropdown menu component:
<template>
<div class="dropdown is-active">
<div class="dropdown-trigger">
<button class="button" aria-haspopup="true" aria-controls="dropdown-menu">
<span>Dropdown button</span>
<span class="icon is-small">
<i class="fa fa-angle-down" aria-hidden="true"></i>
</span>
</button>
</div>
<div class="dropdown-menu" id="dropdown-menu" role="menu">
<div class="dropdown-content">
<a href="#" class="dropdown-item">
Dropdown item
</a>
<a class="dropdown-item">
Other dropdown item
</a>
<a href="#" class="dropdown-item is-active">
Active dropdown item
</a>
<a href="#" class="dropdown-item">
Other dropdown item
</a>
<hr class="dropdown-divider">
<a href="#" class="dropdown-item">
With a divider
</a>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'dropdown-menu',
props: ['menu'],
data() {
return {};
},
};
</script>
But, the dropdown menu is not even shown in the vue dev tools when I go to the top-bar component.
Update
For some reason if I removed the divs, and changed the template to this:
<dropdown-menu v-if='Array.isArray(value)' :menu="link"></dropdown-menu>
<a :href="value" v-else>
{{ key }}
</a>
If somebody could explain it, that would be great.

add CSS element using an angular directive

Im using angular and ng-repeat to populate a list of studies. This list is dynamic so you can toggle into child elements of each element. So basically I have a accordion style toggle list that can go three levels deep on each list item. I have a jQuery issue that I think should be solved with some angular directive or something. Basically I have an arrow (glyphicon) that should switch to up or down depending on if your looking at a child or a parent element in this list. I have this working with pure jQuery just adding and removing a css class from each list item. However, it only works on the first item in the list because ng-repeat creates multiple id’s but jQuery will only work on the first element with that id tag.
This is the HTML from the page.
<!-- User Studies List -->
<div ng-controller="StudiesController" id="before">
<h3 class="center"> User Studies </h3>
<div ng-repeat="study in studies" arrow>
<div class="panel-group" style="margin-bottom:0">
<div class="panel panel-default">
<div class="panel-heading" data-toggle="collapse" href="#collapse{{$index}}">
<h3 class="panel-title">{{ study.study }} <span class="glyphicon glyphicon-menu-down pull-right"></span></h3>
<p></p>
</div>
<div id="collapse{{$index}}" class="panel-collapse collapse">
<ul class="list-group">
<li class="list-group-item" data-toggle="collapse" href="#collapse{{study.id}}" style="margin-left:1%" id="sampleID{{$index}}">
<i>Sample: </i>{{ study.sample }}
<span id="glyph-switch" class="glyphicon glyphicon-menu-down pull-right"></span>
<span id="glyph-up" class="glyphicon glyphicon-menu-up pull-right"></span>
</li>
<div id="collapse{{study.id}}" class="panel-collapse collapse">
<ul class="list-group">
<li class="list-group-item" style="margin-left:3%"><i>Fastq: </i>{{ study.fastq }}
<div class="dropdown center pull-right">
<button class="btn-xs btn-default hvr-shadow" id="dLabel" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Action <span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="dLabel">
<li id="list-item" data-toggle="modal" data-target="#more-metadata-modal" ng-click="showMore(study)">More Data</li>
<li id="list-item" data-toggle="modal" data-target="#edit-metadata-modal" ng-click="showMore(study)">Edit</li>
<li id="list-item" data-toggle="modal" data-target="#delete-metadata-modal" ng-click="showMore(study)">Delete</li>
</ul>
</div>
</li>
</ul>
</div>
</ul>
</div>
</div>
</div>
</div>
</div>
This is the Javascript
var count = 1;
$(document).on("click", "li[id*='sampleID']", function() {
console.log("clicked");
if(count > 0) {
$('#glyph-switch').css('visibility','hidden');
$('#glyph-up').css('visibility', 'visible');
count -= 1;
console.log("count", count);
} else {
$('#glyph-switch').css('visibility','visible');
$('#glyph-up').css('visibility', 'hidden');
count += 1;
console.log("count",count);
}
});
I would just use angular code (no jQuery at all).
Since you already have an object of studies, just handle an onClick="showGlyph($index)" passing the index and then in the code just set the a state for the up/down arrow $scope.GlyphUp = !$scope.GlyphUp.
In the template file have the up/down arrow object bind ng-show="GlyphUp"

Bootstrap dropdown to show grid

I want to show a list of clients in a dropdown, but I want their info separated into columns like FirstName, LastName, Address, City, State, and Zip.
How can I put a grid in the dropdown and have each row be selectable like a regular dropdown? This is what I have so far, but the rows don't look right and only the "Create a new client" row has the correct highlighting when you hover over it.
<div class="dropdown">
<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown" ng-click="loadClientList()">
{{client.DisplayName || "Select a client..."}}
<span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li>
<div class="row" style="width: 650px;">
<div class="col-sm-2"><b>First Name</b></div>
<div class="col-sm-2"><b>Last Name</b></div>
<div class="col-sm-3"><b>Address</b></div>
<div class="col-sm-3"><b>City</b></div>
<div class="col-sm-1"><b>State</b></div>
<div class="col-sm-1"><b>Postal</b></div>
</div>
</li>
<li>
<a ng-click="newClient()">[Create a new client]</a>
</li>
<li ng-repeat="c in clients">
<div class="row" style="width: 650px;">
<div class="col-sm-2"><a ng-click="selectClient(c)">{{c.FirstName}}</a></div>
<div class="col-sm-2"><a ng-click="selectClient(c)">{{c.LastName}}</a></div>
<div class="col-sm-3"><a ng-click="selectClient(c)">{{c.Address}}</a></div>
<div class="col-sm-3"><a ng-click="selectClient(c)">{{c.City}}</a></div>
<div class="col-sm-1"><a ng-click="selectClient(c)">{{c.StateCode}}</a></div>
<div class="col-sm-1"><a ng-click="selectClient(c)">{{c.PostalCode}}</a></div>
</div>
</li>
</ul>
</div>
Look at this Mega Menu, it's called Yamm: http://geedmo.github.io/yamm/

Categories

Resources