How to change color of button text in popover in ionic 4 - ionic4

I am trying to change the text color on the button when the user has made the selection from a popover, so that when the user opens the popover again the previous selection is highlighted...It's for a filter list, user selects filter values desc or asc or by name, etc.
in my html file I have 3 buttons:
<ion-button class="btn1" (click)="sortList('Desc', 'secondary')" fill="clear" [color]="(colorSelect==='secondary')"? 'secondary':'tertiary'">Desc</ion-button>
<ion-button class="btn2" (click)="sortList('Desc', 'secondary')" fill="clear" [color]="(colorSelect==='secondary')"? 'secondary':'tertiary'">Desc</ion-button>
<ion-button class="btn3" (click)="sortList('Desc', 'secondary')" fill="clear" [color]="(colorSelect==='secondary')"? 'secondary':'tertiary'">Desc</ion-button>
in my Component file:
colorSelect: string = '';
public sortList(sortType, item: string){
this.popoverController.dismiss(sortType);
this.colorSelect = item;
}
How would you keep that item selected after the popover has closed so that when the popover is opened again, the item would be hightlighted?

Since popover uses a component that gets destroyed after popover is closed you need to leverage a service that popover component can import and use as source of truth for the button colors.
Very roughly:
https://stackblitz.com/edit/ionic-angular-v5-xfe357
At a high level:
service.ts:
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root',
})
export class SharedService {
public filters;
constructor() {
this.filters = ["secondary","secondary","secondary"]
}
}
popover.ts:
import { Component } from '#angular/core';
import { PopoverController } from '#ionic/angular';
import { SharedService } from './shared.service';
#Component({
selector: 'hello',
template: `<h1>Hello!</h1>
<ion-list>
<ion-button class="btn1" (click)="sortList(0, 'Desc', 'secondary')" fill="clear" [color]="sharedService.filters[0]">Desc</ion-button>
<ion-button class="btn2" (click)="sortList(1, 'Desc', 'secondary')" fill="clear" [color]="sharedService.filters[1]">Desc</ion-button>
<ion-button class="btn3" (click)="sortList(2, 'Desc', 'secondary')" fill="clear" [color]="sharedService.filters[2]">Desc</ion-button>
</ion-list>
`
})
export class HelloComponent {
constructor(private sharedService: SharedService, private popoverController: PopoverController) {
}
sortList(buttonIndex, sortType, item: string){
if (this.sharedService.filters[buttonIndex]==="secondary") {
this.sharedService.filters[buttonIndex]="tertiary";
} else {
this.sharedService.filters[buttonIndex]="secondary";
}
this.popoverController.dismiss(sortType);
}
}
So your main component from which you call popover should also import such service (and inject it) and leverage data from it as single source of truth.

Related

Bootstrap 5 tooltip || popover

I have a little problem with use tooltip or popover in vue app from bootstrap.
<template>
...
<i :title="person.jobTitle" class="fa fa-tag" data-bs-placement="left" data-bs-toggle="tooltip" #click="showDetails"></i>
...
</template>
method:
<script>
import {Popover} from "bootstrap";
...
methods: {
showDetails(event) {
new Popover(event.target)
}
}
...
</script>
Im 100% sure im doing something wrong :).
I have to click twice to showup popover, and it's not closing.
While I create example:
<button id="tolek" ref="tolek" title="aha" class="btn btn-secondary">
Popover
</button>
and in mounted:
new Popover(this.$refs.tolek)
it's all work beautiful.
Edit:
looks like create directive do the job:
<i title="Job Title" class="fa fa-tag" v-popover:click="person.jobTitle"></i>
directive:
directives: {
popover: {
beforeMount(el, binding) {
new Popover(el, {content: binding.value})
}
}
},
Don't mix the BS data-bs- attributes with Vue. Make sure to use the show() method after the popover is instantiated...
methods: {
showDetails(event) {
this.bsPopover = new Popover(event.target, {placement: 'left', trigger: 'click'})
this.bsPopover.show()
}
}
Demo
For Vue3 and Bootstrap 5 you could use Directives
My answer is inspired on the answer from Sergio Azócar and I used TypeScript instead of JavaScript.
Create file bootstrap.ts
import { Tooltip, Popover } from 'bootstrap'
export const tooltip = {
mounted(el: HTMLElement) {
const tooltip = new Tooltip(el)
}
}
export const popover = {
mounted(el: HTMLElement) {
const popover = new Popover(el)
}
}
In main.ts add
import { tooltip } from 'your_path/tooltip'
app
.directive('tooltip', tooltip)
.directive('popover', popover)
.mount("#app");
And use it
<button v-tooltip title="Hello world from a button!">
I am a button
</button>
<div v-popover title="Hello world from a div!" data-bs-content="And with some amazing content">
I am a div
</div>

Angular undefined value of #input variable

I'm new to Angular and I have some issues , hope you'll help me.
so I'm trying to share a value of a variable from a ProjectComponent to an AcceuilComponent , the value of this variable is displaying correctly into my acceuil.component.html but when I try to use it into my acceuil.component.ts it's undefined !
project.component.html (the parent component)
<app-header-in></app-header-in>
<ng-sidebar-container>
<ng-sidebar [opened]="opened">
<p> Sidebar </p>
<button (click)="Sidebar()">
Close Sidebar
</button>
<ul class="menu">
<li class="hh"
*ngFor="let project of projects"
[class.selected]="project === selectedProject"
(click)="onSelect(project)">
{{project.nomprojet}}</li>
</ul>
</ng-sidebar>
<div ng-sidebar-content >
<br><br><br><br>
<button (click)="Sidebar()">
Open Sidebar
</button>
<app-acceuil [message]="idProject"></app-acceuil>
</div>
</ng-sidebar-container>
project.component.ts
import { Component, OnInit } from '#angular/core';
import { ApiService } from '../api.service';
import {ProjectService} from '../project.service';
import {PartService} from '../part.service';
#Component({
selector: 'app-project',
templateUrl: './project.component.html',
styleUrls: ['./project.component.css']
})
export class ProjectComponent implements OnInit {
opened=true;
projects:any;
idProject;
selectedProject;
constructor(private projectService:ProjectService) { }
ngOnInit(): void {
this.projectService.getProjects().subscribe((result)=>{console.log("result",result)
this.projects=result})
}
Sidebar(){
this.opened=!this.opened;
}
onSelect(pro): void {
this.idProject = pro.id;
}
}
acceuil.component.html (my child component)
<p>{{message}}</p>
<ul >
<li class="hh"
*ngFor="let part of parts">
{{part.nomparti}}
</li>
</ul>
acceuil.component.ts
import { Component, OnInit,Input } from '#angular/core';
import { ApiService } from '../api.service';
import {PartService} from '../part.service';
#Component({
selector: 'app-acceuil',
templateUrl: './acceuil.component.html',
styleUrls: ['./acceuil.component.css']
})
export class AcceuilComponent implements OnInit {
#Input() message;
parts:any;
constructor(private partService:PartService) {
}
ngOnInit(): void{
console.log("id",this.message);
this.partService.getPartsFromIdProject(this.message).subscribe((result)=>{console.log("result",result)
this.parts=result})
}
ngOnChanges() {
if(this.message) {
console.log(this.message)
}
}
}
I'm using the message to call a service and displaying data .
in the acceuil.component.html <p>{{message}}</p> is displaying correctly but console.log("id",this.message); in acceuil.component.ts displays undefined
As message is an input property, you need to get its value in ngOnchanges life cycle.
First time, when it is in ngOnChanges, input value will be undefined. So for the safe side, better to add a check for not undefiled condition like below
ngOnChanges(changes: SimpleChanges) {
if (changes.message) {
// Should be defined now
console.log(this.message);
}
}

Conditioan Appearance of Ionic Icons

can you please describe me how to change Ionic icon on conditioanal basis I want 'man' icon if gender is Male and 'woman' icon if gender is femal for example:
So ionic icons have icons like that:
<ion-icon name="man"></ion-icon>
<ion-icon name="woman"></ion-icon>
Source: https://ionicframework.com/docs/ionicons/
to make this conditional you need to create a binding from your template to a variable that defines if its "man" or "woman":
<ion-icon [name]="variableThatDefinesGender"></ion-icon>
Then in your ts file for the template you can assign relevant value (man or woman). Pseudo code below:
import { Component } from '#angular/core';
#Component({
selector: 'page-some',
templateUrl: 'some.html'
})
export class SomePage {
variableThatDefinesGender: string;
constructor(
) {
// initial value:
this.variableThatDefinesGender = "woman";
}
someMethod(condition) {
if (condition === "woman") {
this.variableThatDefinesGender = "woman"
} else {
this.variableThatDefinesGender = "man"
}
}
}

Unable to dynamically reload component

I am trying to dynamically load a tab component based on screen resolution. I have a service with an observable with values: xl, md, sm, xs. The component initially loads on xs and then unloads on screen resizes. The problem is when you resize the screen back to under 768 (xs) the component is not fully materialized. I can see that the component is injected into the DOM on the second load, but it appears the directives are not rendered.
Plnkr - Try sizing the display full width, then resize back to smallest size to reload tab component
import {Component, bootstrap, DynamicComponentLoader, ElementRef, ComponentRef} from 'angular2/core'
import {UiTabs, UiPane} from './ui_tabs'
import {TabbedLayout} from './tabbed_layout'
import {ResizeSvc} from './resize_svc';
#Component({
selector: 'my-app',
template: `
<div>
<h2>Hello {{name}}</h2>
<div #location></div>
</div>
`,
providers: [ResizeSvc]
})
export class App implements OnInit{
private resizeSvc: ResizeSvc;
private _children:ComponentRef;
constructor(private _dcl: DynamicComponentLoader, private _e: ElementRef, pResizeSvc:ResizeSvc) {
this.name = 'Angular2'
this.resizeSvc = pResizeSvc;
}
ngOnInit() {
console.log('initialized app.ts');
this.resizeSvc.layout$.subscribe(
value => this.setLayout(value)
);
}
setLayout(pSize:string) {
this.removeAll();
if(pSize === 'xs') {
console.log('loading layout ' + pSize);
//this._dcl.loadIntoLocation(TabbedLayout, this._e, 'location').then((ref) => {
this._dcl.loadNextToLocation(TabbedLayout, this._e).then((ref) => {
ref.instance._ref = ref;
this._children = ref;
});
} else {
}
}
removeAll() {
if(this._children != null) {
console.log('Disposing layout...');
this._children.dispose();
this._children = null;
}
}
}
Here is a picture of the DOM when initially loaded
And here is a picture of the DOM on the second load with missing tabs
The order of <script> imports matters. angular2-polyfills.js has to be after system-polyfills.js. Here is your plunker working
I just moved the import below to the top of the list.
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.20/system-polyfills.js"></script>

Aurelia - Watch Dependency Value for Change

Suppose you have a class you are injecting into a another class or component. Is there a way to watch for changes on an attributed of the dependency you are injecting and act upon it?
For example, say you have the following app:
app.html
<template>
<input type="text" value.bind="item">
<button click.trigger="addToList()">Add</button>
<h3>Modded</h3>
<ul>
<li repeat.for="it of modded">${it}</li>
</ul>
<h3>Original</h3>
<ul>
<li repeat.for="it of dep.items">${it}</li>
</ul>
</template>
app.js
import {bindable, inject} from 'aurelia-framework';
import {Dep} from './dep';
#inject(Dep)
export class App {
constructor(dep) {
this.dep = dep;
}
attached() {
this.modifyItems();
}
addToList() {
this.dep.addItem(this.item);
}
modifyItems() {
this.modded = [];
for (let item of this.dep.items) {
this.modded.push(item.toUpperCase());
}
}
}
dep.js
export class Dep {
constructor() {
this.items = ['one', 'two', 'three'];
}
addItem(item) {
this.items.push(item);
}
}
Now, let's say that some other component modifies Dep.items. Is there a way to watch for changes in app.js on this.dep.items and then call modifyItems()?
Assume modifyItems() is more complex than this example so maybe a value converter is not the best option. (unless it is the only option I guess)
Here is working plunker with the above example: http://plnkr.co/edit/rEs9UM?p=preview
Someone pointed me to the BindingEngine.collectionObserver and it appears that is what I needed.
app.js:
import {inject} from 'aurelia-framework';
import {BindingEngine} from 'aurelia-binding';
import {Dep} from './dep';
#inject(Dep, BindingEngine)
export class App {
constructor(dep, bindingEngine) {
this.dep = dep;
let subscription = bindingEngine.collectionObserver(this.dep.items)
.subscribe((newVal, oldVal) => {
console.debug(newVal, oldVal);
this.modifyItems();
});
}
attached() {
this.modifyItems();
}
addToList() {
this.dep.addItem(this.item);
this.item = '';
}
modifyItems() {
this.modded = [];
for (let item of this.dep.items) {
this.modded.push(item.toUpperCase());
}
}
}
Here is the working pluker: http://plnkr.co/edit/Pcyxrh?p=preview