Can you access component slot data from parent - javascript

I am trying to get the rangeText data into div outside of the component block for https://innologica.github.io/vue2-daterange-picker/advanced/#slots-demo but it only seems to appear when used in a slot.
I am trying to update the selectDateButtonText text after user selects dates.
Many thanks
<template>
<div v-click-outside="hideCalendarDropdown" class="select-dates">
<button #click="filterCalendarIsActive = !filterCalendarIsActive" :class="{ 'is-active': filterCalendarIsActive }" class="select-dates__button fw-lt-sm">{{ selectDateButtonText }}</button>
<div :class="{ 'is-active': filterCalendarIsActive }" class="calendar-wrapper">
<date-range-picker #finish-selection="datesSelected()"
v-model="dateRange"
:minDate="minDate"
:maxDate="null"
:singleDatePicker="singleDatePicker"
:opens="opens"
:showDropdowns="showDropdowns"
:autoApply="autoApply"
:ranges="ranges"
>
<div slot="footer" slot-scope="data" class="date-range-picker-footer">
{{ data.rangeText }}
<button class="clear-dates" #click="clearDate()" type="button"> Clear </button>
</div>
</date-range-picker>
</div>
</div>
</template>

Got it.
Had to $refs the component
datesSelected() {
this.selectDateButtonText = this.$refs['picker'].rangeText;
}

Related

How to add classes to an element outside of AlpineJS element

I have a simple question but it's driving me mad to work out.
I'm using AlpineJS to show and hide content.
Normally with alpine, the button and content will be in the same element, but here I have it outside of it. This works.
But I cannot add classes to both the button and content div when the content is open or closed.
Can anyone guide me please.
<!-- button -->
<div class="mt-4" x-data="{id: 1}">
<button
#click="$dispatch('open-dropdown',{id})"
type="button"
class="bg-white hover:bg-gray-50"
:class="{ 'bg-green-400': open, 'bg-gray-200': !(open) }">
I'm the button
</button>
</div>
<!-- popup 1 -->
<div x-data="{ open: false }"
x-show="open"
#open-dropdown.window="if ($event.detail.id == 1) open = true"
#click.away="open = false">
<div
class="bg-white hover:bg-gray-50"
:class="{ 'bg-green-400': open, 'bg-gray-200': !(open) }">
I'm the content
</div>
</div>
For this you can move the open state inside the global Alpine.js' $store object that is accessible for all component on the page.
<!-- button -->
<div class="mt-4" x-data="{id: 1}">
<button #click="$dispatch('open-dropdown',{id})"
type="button"
class="bg-white hover:bg-gray-50"
:class="{ 'bg-green-400': $store.open, 'bg-gray-200': !($store.open) }">
I'm the button
</button>
</div>
<!-- popup 1 -->
<div x-data
x-show="$store.open"
#open-dropdown.window="if ($event.detail.id == 1) $store.open = true"
#click.away="$store.open = false">
<div class="bg-white hover:bg-gray-50"
:class="{ 'bg-green-400': $store.open, 'bg-gray-200': !($store.open) }">
I'm the content
</div>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.store('open', false)
})
</script>
Since our open state in the store is a simple value, we can just replace every open with $store.open. Note that the popup 1 component still requires an empty x-data attribute.

How to loop through unknow number of nested json object in Angular with ngFor?

I'm trying to display all the reply from this json :
https://www.reddit.com/r/AskReddit/comments/k8w43d/people_with_the_last_name_pepper_who_have.json
So, depending of the post im loading and the reply, my number of children will differ. How can I loop throught it until I reach the last reply with children (body[2].data.children) ?
Like this :
<div class="replies-box" *ngFor="let reply of comments.body[1].data.children">
<div class="reply-header">
<p class="reply-author"><b>From</b> : {{ reply.data.author }}</p>
<p class="reply-send"><b>Send</b> : {{ this.getDateReply(reply.data.created_utc) }}</p>
</div>
<div class="text-first-reply" [innerHTML]="this.getHtmlDecode(reply.data.body_html)">
</div>
</div>
I have only the first level of reply, is there a way to simply loop through them all ?
Thanks in advance.
I would use a recursion type of approach.
Develop a app-comment component and if comment has children, loop over the children and display the app-comment. That way it will loop over the comments until no more children
<div *ngIf='comment'>
<span *ngIf='comment.kind; else showComment'>Kind: {{ comment.kind }}</span>
<ng-template #showComment>
<span>{{ comment }}</span>
</ng-template>
<div>
<app-comment *ngFor='let child of comment.data?.children' [comment]='child'> </app-comment>
</div>
</div>
See this sample illustration
Simply use ngFor inside ngFor with help of ng-container (will not create extra dom elements)
<div class="replies-box">
<ng-container *ngFor="let commentBody of comments.body">
<ng-container *ngFor="let reply of commentBody.data.children">
<div class="reply-header">
<p class="reply-author">
<b>From</b> : {{ reply.data.author }}
</p>
<p class="reply-send">
<b>Send</b> : {{ this.getDateReply(reply.data.created_utc) }}
</p>
</div>
<div class="text-first-reply" [innerHTML]="this.getHtmlDecode(reply.data.body_html)">
</div>
</ng-container>
</ng-container>
</div>

how to add :key in v-for with multiple divs

I want to make v-for loop without any html element so I decided to use <template as parent. I don't know to assign :key for this loop. I can't assign it to template and to every div inside loop. Any ideas?
<template
v-for="{ id, text, option, percentage, value } in reports"
>
<div class="table-row__index">
{{ id }}
</div>
<div class="table-row__title">
<p>{{ text }} - <strong>{{ option }}</strong></p>
</div>
<div class="table-row__info">
{{ percentage }}%
</div>
<div class="table-row__info">
{{ value }}
</div>
</template>
As a good practice we should always have a parent element inside. But due to your constraints, it's okay to use for loop on template as given in official docs
https://v2.vuejs.org/v2/guide/list.html#v-for-on-a-lt-template-gt
In this case, any keys have to be added to child elements/components and this is what officially recommended.
See this example and please add keys to your div's inside template.
new Vue({
el: '#app'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<template v-for="n in 5">
<span :key="'number' + n">{{ n }}</span>
<span :key="'dot' + n">. </span>
</template>
</div>
you just can't have the for loop in your template. The for loop directive is only allowed to be inside the first child component ( or root component) inside your template.
Here is an example of how you can render your loop:
<template>
<div class="cant-have-for-loop-this-is-the-root-component">
<div v-for="{ id, text, option, percentage, value } in reports" :key="{id}">
<div class="table-row__index">
{{ id }}
</div>
<div class="table-row__title">
<p> {{ text }} - <strong>{{ option }}</strong></p>
</div>
<div class="table-row__info">
{{ percentage }}%
</div>
<div class="table-row__info">
{{ value }}
</div>
</div>
</div>
</template>
This runs soomthly, and with no hesitations. And renders with no styling as this screenshots depicts:
Hope this answers what you want to achieve.

Why doesn't this div trigger the assigned onclick() function?

I have a Django project in which I have this HTML code in my template:
<div class="upper_bar">
<div></div>
<div class="stats_bar">{{ stage1_total }} recibidos, {{ stage2_total }} en preparaciĆ³n, {{ stage3_total }} en camino y {{ stage4_total }} entregados</div>
<div id="createButton" onclick="myFunction()"><i class="fas fa-plus-circle" style="font-size:48px;"></i></div>
</div>
In my scripts tag, I have this:
function myFunction()
{
alert('Function...');
}
I previously had this code instead in my scripts:
document.getElementById('createButton').addEventListener('click',
function()
{
alert('message...');
document.querySelector('.bg-modal').style.display = 'flex';
}
);
But it didn't work either. Does someone know why is the div called 'createButton' not working? I don't want to use a button since I'd like to only see the icon, or is there another way to just see the icon in a way that works and calls the function?
Your div element that is the target is empty - not even blank space. Adding some content seems to work fine: https://jsfiddle.net/0fkyp5nj/
HTML:
<div class="upper_bar">
<div></div>
<div class="stats_bar">{{ stage1_total }} recibidos, {{ stage2_total }} en preparaciĆ³n, {{ stage3_total }} en camino y {{ stage4_total }} entregados</div>
<div id="createButton" onclick="myFunction()"><i class="fas fa-plus-circle" style="font-size:48px;"></i>test 123</div>
</div>
JavaScript:
function myFunction()
{
alert('Function...');
}

ng-click to affect only the element it is inside

I am using ng-repeat to generate some elements...
<div class="form-block" ng-repeat="form in formblock | filter:dateFilter">
<div ng-click="showResults()" ng-if="repeat == true" class="drop">{{ form.form_name }} <span class="caret"></span></div>
<div ng-show="results" class="formURL">{{ form.url }}</div>
<div ng-show="results" class="formCount">{{ form.count }}</div>
<div ng-show="results" class="formSubmit">{{ form.submit }}</div>
</div>
As you can see, ng-click="showResults()" toggles the display of the other elements. The problem is, I only want the ng-click to toggle the elements inside the same container, not toggle all elements.
In short, I only want the click event to affect the elements in the same container that the function is called, how can I do this?
this is showResults in my controller...
$scope.showResults = function(){
return ($scope.results ? $scope.results=false : $scope.results=true)
}
ng-repeat provides you with a special variable (unless you already have an identfier): $index.
Using this, you can store (instead of a single boolean value) an object $index => toggleState in your angular code:
$scope.hiddenHeroes = {};
$scope.toggleHero = function (idx) {
$scope.hiddenHeroes[idx] = !$scope.hiddenHeroes[idx];
}
And in your HTML:
<div ng-repeat="hero in heroes">
<div class="hero" ng-hide="hiddenHeroes[$index]">
<h1>
{{hero}}
</h1>
All you want to know about {{hero}}!
<br />
</div>
<a ng-click="toggleHero($index)">Toggle {{hero}}</a>
</div>
See it live on jsfiddle.net!
You can use $index to index item/containers and show the corresponding results:
<div ng-click="showResults($index)" ng-if="repeat == true" class="drop">{{ form.form_name }} <span class="caret"></span></div>
<div ng-show="results[$index]" class="formURL">{{ form.url }}</div>
<div ng-show="results[$index]" class="formCount">{{ form.count }}</div>
<div ng-show="results[$index]" class="formSubmit">{{ form.submit }}</div>
And your function
$scope.showResults = function(index){
return ($scope.results[index] ? $scope.results[index]=false : $scope.results[index]=true)
}

Categories

Resources