Navigating Through an Array Of Elements - javascript

In my code, I'm trying to navigate through an array of strings. I'm also using observable and I try to map it through next and previous buttons. I can see the first element of the array on the input field but I want to navigate to the next elements and see them in the input field using next button. My code is below, how can I achieve this?
HTML:
<div>
<input type="input" value="{{ slider$ | async }}" />
{{ slider$ | async }}
</div>
<div>
<button mat-button (click)="previous()">Previous</button>
<button mat-button (click)="next()">Next</button>
</div>
TS:
slides: string[] = ['Slide 1', 'Slide 2', 'Slide 3', 'Slide 4'];
slider$: Observable<any> = of(this.slides).pipe(
tap(console.log),
map((arr: string[]) => {
return arr[0];
}),
tap(console.log)
);
previous() {
this.slider$.pipe(
map((arr: string[]) => {
return arr[0];
}),
tap(console.log)
);
}
next() {
this.slider$.pipe(
map((arr: string[]) => {
return arr[1];
}),
tap(console.log)
);
}

You should subscribe only once to slider$. You could navigate through the array like so:
template HTML
<ng-container *ngIf="current$ | async as current">
<input type="input" [value]="current" /> <!-- use property binding -->
<p>{{ current }}</p>
</ng-container>
<div>
<button mat-button (click)="navigate(false)">Previous</button>
<button mat-button (click)="navigate(true)">Next</button>
</div>
component
#Component({...})
export class TheComponent {
index = 0;
slides: string[] = ['Slide 1', 'Slide 2', 'Slide 3', 'Slide 4'];
slider$ = of(this.slides)
current$!: Observable<string>;
ngOnInit(): void {
this.current$ = this.slider$.pipe(map(arr => arr[this.index]));
}
navigate(add: boolean): void {
this.index = add ? ++this.index : --this.index;
console.log('index', this.index)
this.current$ = this.slider$.pipe(map(arr => arr[this.index]));
}
}

Related

Manipulation HTML DOM of Vue3 List Item

i use vue 3 and i want to manipulate a single list item by button click
this is my html:
<socialDiv v-for="(follower, i) in followerList" :key="follower.id" :ref="el => { followerDiv[i] = el }">
<div class="text-md text-gray-800">{{ follower.name }}</div>
<div class="text-sm text-gray-500">{{ follower.username }}</div>
<button #click="handleBtnClick" id="fbtn">{{ follower.btn }}</button>
this is my script:
<script setup>
const followerDiv = ref({})
function handleBtnClick() {
console.log(followerDiv.value)
}
</script>
this is my output:
i'm able to access followerDiv.value[0] but i cannot manipulate the item itself as a DOM element.
how can i access the child items as DOM elements?
Update:
To access the list items i have to add "$el" after value.
I can access the values via:
followerDiv.value[i].$el.style.background = "red"
If I understood you correctly, you can pass index to your function :
const { ref } = Vue
const app = Vue.createApp({
setup() {
const followerDiv = ref({})
const followerList = ref([{id: 1, name: 'aa', username: 'aa', btn: 'aa'}, {id: 2, name: 'bb', username: 'bb', btn: 'bb'}])
function handleBtnClick(i) {
followerDiv.value[i].style.color = "red"
}
return {
followerList, followerDiv, handleBtnClick
}
},
})
app.mount('#demo')
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="demo">
<div v-for="(follower, i) in followerList" :key="follower.id" :ref="el => { followerDiv[i] = el }">
<div class="text-md text-gray-800">{{ follower.name }}</div>
<div class="text-sm text-gray-500">{{ follower.username }}</div>
<button #click="handleBtnClick(i)" id="fbtn">{{ follower.btn }}</button>
</div>
</div>

How can I render a component into an Angular modal?

I want to render a component into a Angular dialog. This component is used on on a full page of my application and it has children.
When I add the selector into my modal, I've got an error : ExpressionChangedAfterItHasBeenCheckedError
This error happens on the page which loads the modal.
Is it possible to reuse a component into an Angular modal with his inputs and outputs ?
<!-- modal -->
<ng-container>
<div class="close-button-wrapper">
<button (click)="onCloseClick()" mat-icon-button>
<mat-icon>close</mat-icon>
</button>
</div>
<div class="dialog-content">
<picture-container></picture-container> <!-- not working ExpressionChangedAfterItHasBeenCheckedError -->
</div>
<div class="dialog-footer">
<raised-button-round class="action-button">Cancel</raised-button-round>
<raised-button-round icon="save" color="primary" class="action-button">OK</raised-button-round>
</div>
</ng-container>
<!-- component I want to reuse -->
#Component({
selector: 'picture-container',
template: `
<ng-container *ngIf="data$ | async as data">
<picture-presenter
[images]="data.pictures"
[tagImages]="data.tagImages"
[totalCountItems]="data.totalCountItems"
[tags]="data.tags"
[pageSize]="20"
[isSelectablePicture]="data.isSelectablePicture"
(displayableContent)="onChangeDisplay($event)"
(searchTagSelect)="onSearchTagSelect($event)"
(importPicture)="onImportPicture()"
(pictureAction)="onPictureAction($event)"
(changePage)="pageChange$.next($event)"
></picture-presenter>
</ng-container>
<upload-widget #uploadWidget (upload)="onUpload($event)"></upload-widget>
`,
styles: [``],
})
export class PictureContainerComponent implements OnDestroy, OnInit {
#ViewChild('uploadWidget') uploadWidget?: UploadWidgetComponent
data$: Observable<
Maybe<{
pictures: Maybe<PictureFragment>[]
tagImages: TagImage[]
tags: Maybe<{ name: string; count: number }>[]
totalCountItems: number,
isSelectablePicture: boolean
}>
> = of(null)
destroyed$ = new Subject<boolean>()
pageChange$ = new BehaviorSubject<PaginatorEvent>({})
personalImages$ = new BehaviorSubject<boolean>(true)
searchTags$ = new BehaviorSubject<string[]>([])
constructor(public store: Store) { }
ngOnInit(): void {
combineLatest([
this.store.select(selectStore),
this.store.select(selectGalleries),
this.searchTags$,
this.pageChange$,
this.personalImages$,
this.store.select(selectPictureUpdated),
])
.pipe(takeUntil(this.destroyed$))
.subscribe(
([store, galleries, tags, direction, isPersonal, picUpdated]) => {
if (!galleries) return
// TODO - High - Get right gallery
const galleriesId = galleries.map((gal) => gal?.id || '')
const filters = {
galleries: !isPersonal ? galleriesId : [],
tags: !isPersonal ? tags : [],
owner: store?.id,
}
this.store.dispatch(
loadPictures({
filters,
paginate: {
count: 15,
},
paginatorEvent: direction,
noCache: picUpdated != null ? true : false,
})
)
this.store.dispatch(
loadTags({
search: {
galleries: galleriesId,
owner: store?.id,
},
})
)
}
)
this.data$ = combineLatest([
this.store.select(selectPicturesState),
of(TAG_IMAGES),
this.store.select(selectPagination),
this.store.select(selectTags),
this.store.select(selectIsSelectablePicture),
]).pipe(
takeUntil(this.destroyed$),
map(([picturesState, tagImages, pagination, tags, isSelectablePicture]) => {
return {
pictures: picturesState?.pictures || [],
tagImages,
totalCountItems: pagination?.totalCount || 0,
tags,
isSelectablePicture
}
})
)
}
ngOnDestroy(): void {
this.destroyed$.next(true)
this.destroyed$.complete()
}
onSearchTagSelect(tags: string[]) {
this.pageChange$.next({})
this.searchTags$.next(tags)
}
onImportPicture() {
if (this.uploadWidget) {
this.uploadWidget?.openDialog({ pictureType: 'food' })
}
}
onPictureAction($event: {
picture: Maybe<PictureFragment>
action: 'edit' | 'delete'
}) {
if (!$event.picture) return
const picture = parsePictureFragmentToPictureInput($event.picture)
if (!picture) return
if ($event.action === 'edit') {
this.store.dispatch(
setUpdatePicture({
picture,
})
)
} else {
this.store.dispatch(setDeletePicture({ picture }))
}
}
onUpload($event: any) {
this.store.select(selectStore).subscribe((store) => {
this.store.dispatch(
setUploadPicture({
picture: { ...$event, owner: store?.id },
})
)
})
}
onChangeDisplay($event: 'import' | 'suggestions' | 'search') {
this.personalImages$.next($event !== 'search')
}
Thank you for your help

unable to set the radio button value selected on button click in list group using angular

Here the requirement is I am displaying the radio button as list group in each list we have 2 buttons with click functionality and whenever the user clicks on that button, it should automatically select the radio button too.
Now I am able to place button and radio button selected button I m not able to select the radio button when the respective sub button is clicked, below is code and Stackblitz
<div class="text-center mt-5">
<h4>Selected value is {{radioSel.name}}</h4>
<div>
<ul class="list-group">
<li class="list-group-item" *ngFor="let item of data">
<input type="radio" [(ngModel)]="radioSelected" name="list_name" value="{{item.value}}" (change)="onItemChange(item)" /> {{item.name}}
<span (click)="one(item.name,'A')">A</span>
<span (click)="two(item.name,'B')">B</span>
</li>
</ul>
</div>
<h5>{{radioSelectedString}}</h5>
</div>
TS code
radioSel:any;
radioSelected:string;
radioSelectedString:string;
public data = [
{
name:'Item 1',
value:'item_1'
},
{
name:'Item 2',
value:'item_2'
},
{
name:'Item 3',
value:'item_3'
},
{
name:'Item 4',
value:'item_4'
},
{
name:'Item 5',
value:'item_5'
}
];
constructor() {
this.radioSelected = "item_3";
this.getSelecteditem();
}
getSelecteditem(){
this.radioSel = this.data.find(Item => Item.value === this.radioSelected);
this.radioSelectedString = JSON.stringify(this.radioSel);
}
onItemChange(item){
this.getSelecteditem();
}
one(data,data1,data2){
console.log(data,data1,data2);
}
two(data,data1,data2){
console.log(data,data1,data2);
this.radioSelected = data;
this.data.find(item => item.value === this.radioSelected);
}
Stackblitz URL::--> https://stackblitz.com/edit/angular-e7utfs and if I click on the other external button I need to get the radio button values
I have tried to solve this with the following approach.
Assigned a dynamic id to each radio button id="{{ item.name }}".
A method onButtonClick(item, i) is fired on every button click. Inside the method, I am setting the input element checked flag to true
HTML
<div class="text-center mt-5">
<h4>Selected value is {{radioSel.name}}</h4>
<div>
<ul class="list-group">
<li class="list-group-item" *ngFor="let item of data; let i = index">
<input type="radio" id="{{ item.name }}" [(ngModel)]="radioSelected" name="list_name" value="{{item.value}}" (change)="onItemChange(item)" />
{{item.name}}
<button (click)="onButtonClick(item)">A</button>
<button (click)="two(item.name,'B')">B</button>
</li>
</ul>
</div>
<h5>{{radioSelectedString}}</h5>
</div>
TS Code
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
radioSel:any;
radioSelected:string;
radioSelectedString:string;
public data = [
{
name:'Item 1',
value:'item_1'
},
{
name:'Item 2',
value:'item_2'
},
{
name:'Item 3',
value:'item_3'
},
{
name:'Item 4',
value:'item_4'
},
{
name:'Item 5',
value:'item_5'
}
];
constructor() {
this.radioSelected = "item_3";
this.getSelecteditem();
}
getSelecteditem(){
this.radioSel = this.data.find(Item => Item.value === this.radioSelected);
this.radioSelectedString = JSON.stringify(this.radioSel);
}
onItemChange(item){
this.getSelecteditem();
}
one(data,data1){
console.log(data,data1);
}
two(data,data1){
console.log(data,data1);
this.radioSelected = data;
}
onButtonClick(data) {
const el = document.getElementById(data.name) as HTMLInputElement;
el.checked = true;
}
}
In the given code, you are setting a value with item.name when button B is clicked.
It has to be item.value instead of item.name since input tag is bound with item.value.
HTML:
<span (click)="two(item.value,'B')">B</span>
TS:
two(data, data1) {
console.log(data, data1);
this.radioSelected = data;
}

Passing custom template to stencil component

I'm working on to create a custom component using Stencil to replicate UI-select.
The component will be used like:
let items = [{name:"Abc", age: 10}, {name:"Xyz", age: 10}];
let itemString = JSON.stringify(items);
<dropdown-search placeholder="Select User" item-string='${itemString}'>
</dropdown-search>
Then the component is defined as
import {
Component,
State,
Prop,
Element,
} from '#stencil/core';
#Component({
tag: 'dropdown-search',
})
export class DropdownSearch {
#Element() dropdownEl: HTMLElement;
#State() isOpen: boolean = false;
#State() items: any = [];
#Prop() itemString: string = '';
#Prop() placeholder: string = '';
componentDidLoad() {
try {
this.items = JSON.parse(this.itemString);
} catch(e) {}
}
onClickDropdownHandler = (e: UIEvent) => {
e.preventDefault();
this.toggleDropdown();
}
toggleDropdown = () => {
this.isOpen = !this.isOpen;
if (this.isOpen) {
window.setTimeout(
() => {
this.dropdownEl.querySelector('input').focus();
},
0,
);
}
}
renderOptions = () => {
if (!Array.isArray(this.items) || !this.items.length) {
return null;
}
return this.items.map((item) => (
<li
>
<a href="javascript:" title="{item.name}">
{item.name}
<small class="d-block">age: {item.age}</small>
</a>
</li>
));
}
render() {
let dropdownClassName = (this.isOpen ? 'open' : '');
return (
<form name="myForm">
<div class="form-group">
<div
class={`btn-group dropdown ${dropdownClassName}`}
>
<button
class="btn btn-default dropdown-toggle"
onClick={this.onClickDropdownHandler}
>
{this.placeholder}
</button>
<ul class="dropdown-menu" role="menu">
<li>
<div class="input-group input-group-search">
<input
class="form-control"
type="search"
/>
</div>
</li>
{this.renderOptions()}
</ul>
</div>
</div>
</form>
);
}
}
The items are rendering fine. As as the user can pass a custom array of objects, so I need to customize the options template. So the user can pass it while using the component.
Right now I'm using a static template for the options within the component, like
<a href="javascript:" title="{item.name}">
{item.name}
<small class="d-block">age: {item.age}</small>
</a>
but I need a way to pass this template from where I'm using the template. I can't use slot there as I'm using the same template within all the options that running in a loop.
I am new with stencil but just tried to utilize an #Prop function for this, and it worked:
Component
#Component({
tag: 'dropdown-search',
styleUrl: 'dropdown-search.css',
shadow: true,
})
export class DropdownSearch {
// allow template function to be passed from outside
#Prop template!: (item: SelectItem) => any;
#State items!: SelectItem[] = [{name: 'dog'}, {name: 'cat'}, {name: 'elephant'}];
render() {
return (
<Host>
<p>Before Template</p>
{/* render the custom template here: */}
{this.items.map(item => this.template(item))}
<p>After Template</p>
</Host>
);
}
}
Providing custom template to this component:
class AppComponent {
render() {
// Here we define our custom template to be rendered in MyComponent
const CustomTemplate = (item: SelectItem) => <p key={item.name}>Hi, I am {item.name}</p>;
return (
<div class="app-home">
<dropdown-search template={CustomTemplate} />
</div>
);
}
}
result looks like this:

Using state and button to remove an element from a list

I have a list created like so:
return (this.state.limit).fill().map((_,index) => {
return (
<div key={`${index}`}> Item </div>
)
)
How can I create a button that let's me remove a specific div element as well as reduce the state limit?
In React, you will have to bind almost everything to data. In your example, all those items should be represented by some underlying data. Here's a very basic example showing this:
class Example extends React.Component {
constructor() {
super();
this.state = {
data: ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5'],
};
}
removeItem(item) {
let data = this.state.data.filter((_, i) => i != item);
this.setState({ data });
}
render() {
return (
<div>
{this.state.data.map((item, i) => <div key={i} onClick={() => this.removeItem(i)}>{item}</div>)}
</div>
);
}
}
ReactDOM.render(<Example/>, document.getElementById('View'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="View"></div>
Create a button
<button onClick={removeDiv}>
Remove div
</button>
Add function removeDiv
function removeDiv(){
this.state.limit.shift()
this.setState({limit: this.state.limit})
}
When changing the state, your component will re-render it self with new state.

Categories

Resources