mat-accordion with-in component - angular 5 - angular5

I have a question component with mat-accordion to display question. I'm calling my question component to question-list component.
Everything works great until this point, the issue arises when I'm clicking on question component it is getting open and remain in that state if I'm opening another one also. As per requirement, only one should open at a time.
I see an issue is with the accordion is reside in question component and from question-list, we are looping it with *ngFor so every accordion is having one expension-pannel in it.
My code was working until I had break mine code to two-component from one question-list(parent) and question(chile)
Sample Code:-
question-list.component:
<ng-template ngFor let-data [ngForOf]="questionList" let-i="index" >
<app-question [data]="data"></app-question>
</ng-template>
question.component:-
<mat-accodion>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-description style="float:left;">
{{data.description}}
</mat-panel-description>
</mat-expansion-panel-header>
<form>
<div class="row text-left options">
<div class="col-md-6" *ngFor="let option of data.options;">
<mat-checkbox>{{option.description}}</mat-checkbox>
</div>
</div>
</form>
</mat-expansion-panel>
</mat-accodion>

A question is represented by a single expansion panel. A list of questions is represented by an accordion. So your question component should be an expansion panel only, not an accordion. Then you make the accordion part of the question list with the for loop inside the accordion.
Question List:
<mat-accordion>
<ng-container *ngFor="let data of questionList; let i = index">
<app-question [data]="data"></app-question>
</ng-container>
</mat-accordion>
Question:
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-description style="float:left;">
{{data.description}}
</mat-panel-description>
</mat-expansion-panel-header>
<form>
<div class="row text-left options">
<div class="col-md-6" *ngFor="let option of data.options;">
<mat-checkbox>{{option.description}}</mat-checkbox>
</div>
</div>
</form>
</mat-expansion-panel>

#G. Tranter thanks, for your answer, actually this(your suggestion) I already tried and did not got any success, after that I tried the traditional way of doing collapse and expand with self-declared property in to every question object "expanded" by default this will be false to every question and we have used it as ngModel in expansion control, on change the state of one we handle other "expanded" property.
sample here which is working now,
<mat-expansion-panel [(ngModel)]="data.expanded"
name="fieldName" ngDefaultControl (opened)="panelOpenState(data._id)"
[expanded]="data.expanded">
<mat-expansion-panel-header>
<mat-panel-description style="float:left;">
{{data.description}}
</mat-panel-description>
</mat-expansion-panel-header>
<form>
<div class="row text-left options">
<div class="col-md-6" *ngFor="let option of data.options;">
<mat-checkbox>
{{option.description}}
</mat-checkbox>
</div>
</div>
</form>

Related

Modal memory leak as more modals added

I am developing an Angular 4 application using ngx-bootstrap modals heavily. I am currently using the template + modalService way of opening up modals. During a click event, this line of code is called:
#ViewChild() worklistTemplate;
// ...
this.modalRef = this.modalService.show(this.worklistTemplate, this.config);
And the worklist template looks like this:
<ng-template #worklistTemplate>
<app-action-view
[leftButtons]="leftButtons"
[labelHeader]="modalHeader"
[labelIcon]="modalIcon"
[modalRef]="modalRef">
<div class="row modal-info-panel">
<div class="col-xs-4 modal-user-info">
<fa name="mars" class="gender-icon"></fa>
<div class="field-panel">
<input type="text"
[ngModel]="rowInfo.lastName"
[ngClass]="{ 'modal-editable-field': modalFieldsEditable }"
[disabled]="!modalFieldsEditable">
<input type="text"
[ngModel]="rowInfo.firstName"
[ngClass]="{ 'modal-editable-field': modalFieldsEditable }"
[disabled]="!modalFieldsEditable">
<div>
<label for="modal-mrn">MRN --</label>
<input id="modal-mrn" type="text"
[ngModel]="rowInfo.mrn"
[ngClass]="{ 'modal-editable-field': modalFieldsEditable }"
[disabled]="!modalFieldsEditable">
</div>
<div>
<label for="modal-dob">DOB --</label>
<input id="modal-dob" type="text"
[ngModel]="rowInfo.birthDate"
[ngClass]="{ 'modal-editable-field': modalFieldsEditable }"
[disabled]="!modalFieldsEditable">
</div>
<div class="edit-panel">
<fa *ngIf=modalFieldsEditable class="worklist-edit-buttons green-hover link" name="floppy-o" tooltip="Save" (click)=saveChanges()></fa>
<fa *ngIf=modalFieldsEditable class="worklist-edit-buttons red-hover link" name="times" tooltip="Cancel" (click)=toggleModalFields()></fa>
</div>
</div>
</div>
<div class="col-xs-8 modal-id-info">
Associated ID
<div class="full-width">
<div class="row" *ngFor="let i of rowInfo.associatedIDs">
<div class="col-xs-6 cell">{{i.id}}</div><div class="col-xs-6 cell">{{i.source}}</div>
</div>
</div>
</div>
</div>
<div class="row" id="modal-table">
<app-table-view
[columns]="objDetailsCols"
[tableData]="objDetailsData"
[expandTemplate]="rowImageContainer"
[expandColStyle]="expandColStyle">
</app-table-view>
</div>
<div *ngIf="resolvePanelVisible" class="resolve-panel"
[#slideRight]>
<div class="resolve-label">
<fa class="lt-icon" name="wrench"></fa>
Resolve QA Issue
</div>
<div class="resolve-body">
Hello, World!
</div>
<div class="resolve-footer">
<a>Validate</a>
<a>Resolve</a>
<a>Reload</a>
<a (click)="toggleResolvePanel()">Close</a>
</div>
</div>
</app-action-view>
</ng-template>
The modal can be closed by clicking outside of the modal boundaries or there is a button inside the ActionViewComponent that calls modalRef.hide().
Memory usage increases drastically as more and more modals are opened. In fact, if I open the modal around 5 times, the application becomes sluggish and almost unusable. This is especially apparent if there are many rows in our TableViewComponent.
Is there a problem with the way I am using the modals, or is this an issue related to ngx-bootstrap modals? OR, is the issue related to the browser and unavoidable? I am developing on Chrome 62 right now.
I have verified that onDestroy is never called on the TableViewComponent inside the modal. It is not even called if I navigate to a different page component, though another table (not in the template) does have onDestroy called when navigating from the page.
Any feedback is appreciated greatly- I have been stuck for way too long on this.
This is an issue of ngx-bootstrap, unfortunately. I created a pull request (https://github.com/valor-software/ngx-bootstrap/pull/3179) so this will be fixed as soon as my PR is merged and new version is released.

preventing Vue from aggresively reusing dom-elements

Condider the following snippet:
<template v-if="tryIsMobile" >
<div class='device device-mobile-portrait' :class="deviceClass">
<div class="device-scroller-container">
<div class='device-scroller'>
<img id='tryit-img-mobile' :src="srcUrlMobile" v-on:load="onImgLoad" v-on:error="onImgError"/>
</div>
</div>
</div>
</template>
<template v-else>
<div class='device device-tablet-landscape' :class="deviceClass" >
<div class="device-scroller-container">
<div class='device-scroller'>
<img id='tryit-img-tablet' :src="srcUrlTablet" v-on:load="onImgLoad" v-on:error="onImgError"/>
</div>
</div>
</div>
</template>
This code conditionally renders one of the two images. Some user action results in the actual shown image to be toggled.
What I'm seeing is the following: When toggling from say, tryit-img-mobile to tryit-img-tablet, the image loaded as part of tryit-img-mobile will get displayed with different dimensions instantly. However, during the time the image loads it's new source :src="srcUrlTablet", the image with src :src="srcUrlMobile" still displays.
This is probably due to Vue using the same img-tag for both the templates. How can I prevent Vue from doing this, and instead use seperate img-tags?
In cases such as this, Vue uses a special key attribute that tells it not to reuse the same element. Give each element this attribute with a unique value, and Vue will no longer reuse the same element:
<div v-if="tryIsMobile"
class="device device-mobile-portrait"
:class="deviceClass"
key="mobile"
>
...
</div>
<div v-else
class="device device-tablet-landscape"
:class="deviceClass"
key="tablet"
>
...
</div>

VueJS render once into an element

Is it possible to just render once into an element?
Suppose I have a contenteditable div, and only want to render the first value, then stop rerendering as the model changes. Here only the initial value of variable will be rendered.
<div contenteditable="true"> {{variable}} </div>
Use v-once
<div contenteditable="true" v-once> {{variable}} </div>
You can also wrap it with a <span>:
<div contenteditable="true">
<span v-once> {{variable}} </span>
</div>
refs:
https://v2.vuejs.org/v2/guide/components.html#Cheap-Static-Components-with-v-once
https://v2.vuejs.org/v2/api/#v-once
Or another solution is simply clone the variable and just don't modify it, for example if you call it readOnlyVariable:
<div contenteditable="true"> {{readOnlyVariable}} </div>

Bootstrap element structure (containers)

I'm recently started to work with bootstrap and i really like it, very easy and strcutured.
Although i dont understand one thing, what is the correct structure?
I've read their getting started and they said for example the the first child of row can be column ...
But they also mentioned about div='container', should then also all of the elements be in a container?
for example
<div class="container">
<nav> </navr>
</div>
<div class="container">
<div ....> BODY </div>
</div>
<div class='container">
<footer></footer>
</div>
Or are all of this containers very optional and can be left not used?
Same question about rows, should then all column elements be a part of row or not?
I have following structure:
<nav></nav>
<row>
<div class="col-md-2> </div>
<div class="col-md-10">
<row>
<div class="col-md-6"> </div>
<div class="col-md-6"> </div>
</row>
</div>
</row>
Is this the correct structure?
Since row has negative margins, it should be placed inside container..
According to the Bootstrap docs (http://getbootstrap.com/css/#grid):
"Rows must be placed within a .container for proper alignment and padding."
As you'll see in this demo, the non-contained row causes a horizontal scrollbar:
http://bootply.com/106752
But, container can be used anywhere.. alone, or nested inside other containers / rows.

Dijit: Why am I getting an "Uncaught Error: Invalid Template"?

I have a dijit that looks fine as far as I can tell, but it is raising Uncaught Error: Invalid template every time. I have not been able to figure out why. All variables (e.g. ${variableName} are defined in the widget correctly.
Here is the widget:
<div class="${classPrefix}-wrapper">
<div class="${classPrefix} flair" dojoAttachPoint="flairNode"></div>
<div class="${classPrefix}-count hidden" dojoAttachPoint="countWrapperNode">
<div class="count" dojoAttachPoint="countNode">0</div>
</div>
<div class="${classPrefix} ${secondaryClass} action hidden" dojoAttachPoint="secondaryClickNode" dojoAttachEvent="onclick:_onSecondaryClick">
<div class="${classPrefix}-inner"></div>
<div class="${classPrefix}-icon"></div>
</div>
<div class="${classPrefix} ${primaryClass} action" dojoAttachPoint="primaryClickNode" dojoAttachEvent="onclick:_onPrimaryClick">
<div class="${classPrefix}-inner"></div>
<div class="${classPrefix}-icon"></div>
</div>
<div class="${classPrefix}-message hidden" dojoAttachPoint="messageNode"></div>
</div>
<div class="${actionPromptNodeClass}" dojoAttachPoint="actionPromptMessageNode">
<span dojoAttachPoint="actionPromptMessage">${actionPromptText}</span>
<span dojoAttachPoint="actionCompletedMessage" class="hidden">${actionCompletedText</span>
</div>
Found the answer to my question. It turns out that you can only have one root node in a Dijit. I missed this in the docs, but it is at the bottom of this tutorial:
Common Pitfalls
Be sure to only have one root node in your template
Don’t start or end your template with a comment because that means you technically have two nodes
Avoid a trailing </div> at the end of your template
There may be only one root element in the template. Wrap your template into <div></div> and it should work.