I am using angular2-tree-component and want to show already expanded tree.
Now my tree is not expanded after loading page:
My HTML looks like this:
<div class="sidebartree-ul" [ngStyle]="{'background-color': 'white'}">
<tree-root class="panel-body " [nodes]="Items" [options]="customTemplateStringOptions"
#tree >
<ng-template #loadingTemplate>
Loading..
</ng-template>
<ng-template #treeNodeTemplate let-node>
<tree-node-expander [node]="node"></tree-node-expander>
<span *ngIf="node?.data?.expanded === true " title="{{node.data.subTitle}}">
<b> {{ node.data.name }} {{ childrenCount(node) }} </b></span>
<span *ngIf="!node?.data?.expanded === true" title="{{node.data.subTitle}}">
<b>{{ node.data.name }} {{ childrenCount(node) }} </b></span>
</ng-template>
<ng-template #loadingTemplate>Loading, please hold....</ng-template>
</tree-root>
</div>
I see an example from github and I've set isExpandedField to expanded, however, children fields are not expanded:
customTemplateStringOptions: any = {
isExpandedField: 'expanded',
idField: 'uuid',
nodeHeight: 23
}
public Items: any[];
And my data for TreeView looks like this:
If I add <tree-node-expander [node]="node"></tree-node-expander> and click at it, then it expands, but it is now what I want:
<tree-root class="panel-body " [nodes]="Items"
[options]="customTemplateStringOptions" #tree >
...
<ng-template #treeNodeTemplate let-node>
<tree-node-expander [node]="node"></tree-node-expander>
...
</ng-template>
<ng-template #loadingTemplate>Loading, please hold....</ng-template>
</tree-root>
Does anybody know what I am doing wrong to expand all children immediately?
I ran into the same problem. I used the below hack which i learned it from using ngx-toastr in ngOnInit. I worked for me. Not sure why ngAfterViewInit is not working for me.
ngAfterViewInit() {
setTimeout(() => this.tree.treeModel.expandAll(), 500)
}
I had to adjust the waiting time to make it work. by animation, you can make it for expansion.
I am still new to angular to provide in depth explanation. Let's see if this works for you and someone provide explanation.
The angular lifecycle isn't reliable for expanding the tree. The more reliable way is to use the (initialized) event on the tree root.
Docs: https://angular2-tree.readme.io/docs/events#initialized
In your component html file:
<tree-root (initialized)="onTreeLoad()">
In your component class:
onTreeLoad() {
this.tree.treeModel.expandAll();
}
You just need to call the expandAll method. You can check the demo, here
https://angular2-tree.readme.io/docs
The Expand All button calls the tree.treeModel.expandAll() method.
Related
I am trying to build a To-Do list in Vue.js which has 3 columns: To-Do, Doing, Done.
I would like to be able to move an item between columns by clicking on arrows that are inside the list item.
Right now I have a list of objects that I separate in 3 arrays depending on a "status" attribute. I would like to change that attribute when clicking on left/right arrow then refresh the UI with new arrays.
I haven't found the way to target the element that received the click.
<ul>
<li v-for="todo in todoTodos" v-bind:key="todo._id">
<span v-if="todo.importance == 1" class="bg-success"></span>
<span v-else-if="todo.importance == 2" class="bg-warning"></span>
<span v-else-if="todo.importance == 3" class="bg-alert"></span>
<div>
<h3>{{ todo.title }}</h3>
<p>{{ todo.description }}</p>
</div>
<p class="todo__date">Début: {{ todo.datebegin }} - Fin espérée: {{ todo.dateend }}</p>
<div class="todo__actions">
<i #click="editTodo" class="icofont-edit"></i>
<i #click="moveRight" class="icofont-arrow-right"></i>
<i #click="moveLeft" class="icofont-arrow-left"></i>
</div>
</li>
</ul>
My linter prevents me from using v-for + v-if, but I guess that means I will have to re-calculate each list (todoTodos, doingTodos, doneTodos) after each modification. Is there a better way ?
I tried console.logging this e.target e.currentTarget but
this logs the entire data model
e.target and e.currentTarget logs the element which I can't use to find my way back to the todo item I want to modify
You /usually/ pass the ($event) argument to a method if you want to access an event.
<i #click="moveLeft($event)" class="icofont-arrow-left"></i>
......
methods: {
moveLeft(e){
console.log(e.target)
}
}
I am using the Angular flexlayout which works perfectly, I am trying to bind fxFlex to the child element like below
<div [fxLayout]="fields.layoutConfig.fxLayout + ' wrap'" fxLayoutGap="10px">
<div *ngFor="let field of fields.componentConfig;" [fxFlex]="field?.componentProperty?.fxFlexChildLayout?.fxFlex">
<ng-container reactiveField [field]="field" [group]="form"></ng-container>
</div>
</div>
Whenever the field?.componentProperty?.fxFlexChildLayout?.fxFlex is null or undefined it should not bind fxFlex to the div
I have tried
[fxFlex]="field?.componentProperty?.fxFlexChildLayout?.fxFlex != undefined ? field?.componentProperty?.fxFlexChildLayout?.fxFlex : 'null'"
[attr.fxFlex]="field?.componentProperty?.fxFlexChildLayout?.fxFlex" --> this does nothing it doesn't bind `fxFlex` when it has value as well
But still in the DOM I can see it add the style as
style="flex: 1 1 100%; box-sizing: border-box;"
I don't want to add style if the value of field?.componentProperty?.fxFlexChildLayout?.fxFlex is undefined or null
How can I achieve this ?
You cannot conditionally apply a directive is the issue. You can dynamically set the value of a directive input which is what you are doing above, but all divs will have the fxFlex directive.
So:
<div [fxLayout]="fields.layoutConfig.fxLayout + ' wrap'" fxLayoutGap="10px">
<ng-container *ngIf="field?.componentProperty?.fxFlexChildLayout?.fxFlex; else noFxFlex">
<div *ngFor="let field of fields.componentConfig;" [fxFlex]="field?.componentProperty?.fxFlexChildLayout?.fxFlex">
<ng-container reactiveField [field]="field" [group]="form"></ng-container>
</div>
</ng-container>
<ng-template #noFxFlex>
<div *ngFor="let field of fields.componentConfig;">
<ng-container reactiveField [field]="field" [group]="form"></ng-container>
</div>
</ng-template>
</div>
This isn't the answer you asked for, but as #Randy stated in their answer because you can't conditionally apply a directive, an alternative solution would be to pass "none" to the [fxFlex] directive.
"None" should size the child element according to its width and height properties. It is fully inflexible. It neither shrinks nor grows in relation to the flex container.
That should have the same effect as removing the [fxFlex] directive, however, the DOM element will still have some flex styles on it.
<div [fxLayout]="fields.layoutConfig.fxLayout + ' wrap'" fxLayoutGap="10px">
<div *ngFor="let field of fields.componentConfig;" [fxFlex]="field?.componentProperty?.fxFlexChildLayout?.fxFlex || 'none'">
<ng-container reactiveField [field]="field" [group]="form"></ng-container>
</div>
</div>
See https://github.com/angular/flex-layout/wiki/fxFlex-API#fxflex-options
You just need to declare a variable in your TS and assign
HTML
<div fxFlex="{{fxflexwidth}}">
TS
fxflexwidth: number = 50;
in your function just return when you need to change the attribute:
this.fxflexwidth = 100;
I'm trying to edit JS library that already existed but it consisted of Vue. So I studied Vue a little.
The problem is that I made child component called 'Analysis' and want to anchor function. I made tag and bind 'moveAnchor' method to onclick, also declared 'moveAnchor' on methods part. but it didn't work. How can I fix? I'm sorry for being inexperienced.. :(
it is script.js of analysis.
import { mapActions, mapState } from 'vuex';
export default {
name: 'Analysis',
computed: {
checkName : function(){
var flag = this.$store.state.analysis.name;
if(flag.indexOf("/PCs/") != -1){
console.log(flag);
}
}
},
methods: {
moveAnchor: function (id){
var div = document.getElementById(id).scrollIntoView();
}
it is template.html of analysis.
<div :class="$style.scrollarea">
<div :class="$style.dropdown">
<button :class="$style.dropbtn">Analysess</button>
<div :class="$style.dropContent">
<a v-for="item in analyData" v-bind:key="item.id" #onclick="moveAnchor(item.id)">
{{ item.title }}
</a>
</div>
</div>
<span>
{{ checkName }}
</span>
<div v-for="item in analyData">
<h1 v-bind:id="item.id">{{ item.title }}</h1>
<img v-bind:src="item.src" style="width: 100%; height: auto">
</div>
Welcome to StackExchange!
The correct binding for Vue's click event is v-on:click, or #click for shorthand. So when you write #onclick, Vue will never call that.
Just change #onclick to #click and all should work fine.
I am making an internal live chat system using Firebase. I make this call to get a list of all chat messages:
firebase.database().ref('chatrooms/'+this.roomkey+'/chats').on('value', resp => {
this.chats = [];
this.chats = snapshotToArray(resp);
setTimeout(() => {
if(this.content._scroll) { this.content.scrollToBottom(0); }
}, 1000);
});
In my view I have this HTML to loop through the chats:
<ion-content>
<ion-list>
<ion-item *ngFor="let chat of chats" no-lines>
<div class="chat-status" text-center *ngIf="chat.type==='join'||chat.type==='exit';else message">
<span class="chat-date">{{chat.sendDate | date:'short'}}</span>
<span class="chat-content-center">{{chat.message}}</span>
</div>
<ng-template #message>
<div class="chat-message" text-right *ngIf="chat.user === nickname">
<div class="right-bubble">
<span class="msg-name">Me</span>
<span class="msg-date">{{chat.sendDate | date:'short'}}</span>
<p text-wrap>{{chat.message}}</p>
</div>
</div>
<div class="chat-message" text-left *ngIf="chat.user !== nickname">
<div class="left-bubble">
<span class="msg-name">{{chat.user}}</span>
<span class="msg-date">{{chat.sendDate | date:'short'}}</span>
<p text-wrap>{{chat.message}}</p>
</div>
</div>
</ng-template>
</ion-item>
</ion-list>
</ion-content>
And here is my relevant CSS:
ion-content {
height: 100%;
overflow: hidden;
.item-md, .item-ios {
background: none;
}
}
There are currently 30 chat messages. They are loaded from Firebase and then after a second, when the setTimeout finishes, the user is automatically scrolled to the bottom of the page so they can see the most recent message. This makes the page load a bit odd with the jump down to the bottom of the page after a second.
Is there any way to replace the timeout with something that will achieve the goal of the user initially seeing the most recent messages? Maybe there is some trigger that can be made that detects that chats has changed in the view and then does the scroll? This would seem better than a fixed 1 second pause?
You could scroll to the bottom of the list when changes to the list of chats are detected:
Associate a template reference variable to the ion-item elements (e.g. #chatItems)
Use ViewChildren to get the QueryList of ion-item elements
In ngAfterViewInit, subscribe to the QueryList.changes event
Scroll to the bottom of the list when the list changes
<ion-content>
<ion-list>
<ion-item #chatItems *ngFor="let chat of chats" no-lines>
...
</ion-item>
</ion-list>
</ion-content>
export class MyComponent {
#ViewChildren("chatItems") chatItems: QueryList<any>;
ngAfterViewInit() {
this.scrollContentToBottom();
this.chatItems.changes.subscribe(() => {
this.scrollContentToBottom();
});
}
scrollContentToBottom() {
if(this.content._scroll) {
this.content.scrollToBottom(0);
}
}
...
}
Note: I used QueryList<any> because I don't know the ion-item component type. You can put the appropriate type if you know what it is.
I'm writing angular components for the foundation css framework. I am working on the tabs component, and want to be able to pass some HTML to the <ng-content> of this.
The problem is, I also need to pass html which a user can put bindings on, like this:
PARENT TEMPLATE
<tabs [data]='example'>
<div> Age <br> {{item.age}} </div>`
</tabs>
TABS COMPONENT
<ul class="tabs" #tabs>
<li *ngFor="let item of data | async" (click)="tabClick($event)">
<a>{{item.name}}</a>
</li>
</ul>
<div>
<ng-content></ng-content>
</div>
TABS TYPESCRIPT
#Component({
selector: 'tabs',
templateUrl: './tabs.component.html'
})
export class TabsComponent {
#Input('data') data:any;
#ViewChild('tabs') tabs: ElementRef;
}
Where item is a reference to an object in the example array.
However, I get this error:
Cannot read property 'name' of undefined
as item is being evaluated before it is inserted into the <ng-content> directive.
Is there a way to get around this limitation, or am I going about this the wrong way?
update Angular 5
ngOutletContext was renamed to ngTemplateOutletContext
See also https://github.com/angular/angular/blob/master/CHANGELOG.md#500-beta5-2017-08-29
original
ngTemplateOutlet or ngForTemplate can be used for that use case:
<tabs [data]='example'>
<ng-template let-item>
<div> Age <br> {{item.age}} </div>`
</ng-template>
</tabs>
#Component({
...
template: `
<ul class="tabs" #tabs>
<li *ngFor="let item of data | async" (click)="tabClick($event)">
<a>{{item.name}}</a>
</li>
</ul>
<div>
<ng-template [ngTemplateOutlet]="templateRef" [ngTemplateOutletContext]="{$implicit: (data | async)}"></ng-template>
</div>
`
})
class TabsComponent {
#ContentChild(TemplateRef) templateRef:TemplateRef;
}
See also Angular 2 bind transcluded content to loop variable
You should be using this way instead,
<tabs [data]='example'>
<div> Age <br> {{item.age}} </div>`
</tabs>
Component typescript
#Component({
selector: 'tabs',
templateUrl: './tabs.component.html'
})
export class TabsComponent {
#Input() data:any;
item:any{};
}
In your content projection define a selector as
<div class="tabs-body">
<ng-content select=".tabs-body"> </ng-content>
</div>
As your passing with bindings
<tabs [data]='example'>
<div> Age <br> {{item.age}} </div>`
</tabs>
DEMO
You need to pass the item object to the ng-content component.
<ng-content [item]="selectedTab></ng-content>
I am not certain on what lies behind the tab click event but you can assign that item object to selectedTab which will be passed to the component.
The component that will control the tab view can have the following:
#Input() item: Item;
And this will pass that object when you click. I might be attacking this from the wrong angle but maybe it will help you in some way.