Vue.js - What is the self modifier - events

I am learning Vue.js. Currently, I'm trying to understand eventing. In Vue, there are Event Modifiers. I understand the purpose of all of them except for the self modifier. What is the self modifier? It seems like the stop modifier does the same thing as self. When would I use self?
Thank you for any help. I feel like i have to be misunderstanding something. I just don't see the purpose of self.

If a parent node and child node are registered with same type of event then when that type event dispatches then handlers of parent and child are called.
Here is an example fiddle.
Try removing self from click event modifier of the parent div then click on the child div.
First the child handler is called.
Parent handler is called.
<div class="parent" v-on:click="log('from parent')">
Parent
<div class="child" v-on:click="log('from child')">
Child
</div>
</div>
If you put back self modifier and clicking on the child div doesn't call the parent handler.

Let's i explain difference.
<div id="app">
<div class="root" v-on:click="log('root')">root
<div class="parent" v-on:click.self="log('parent')">Parent
<div class="child" v-on:click="log('child')">Child
</div>
</div>
</div>
</div>
Do you know about event phases?
Capture (event.eventPhase = 1)
Target (event.eventPhase = 2)
Bubbling (event.eventPhase = 3)
By default if you add event listener for element it will work in bubbling mode.
If you click on child in example, you will get output:
child
root
Modificator self say: 'If user click on me (my area or borders), please trigger handler'
If you remove self:
<div class="parent" v-on:click="log('parent')">
and click on child, you will get output:
child
parent
root
If you add stop:
<div class="parent" v-on:click.stop="log('parent')">
and click on child, you will get output:
child
parent
You will not get root, because stop cancel event bubbling (event.stopPropagation).
If you will use self it doesn't stop bubbling!

As the docs says:
only trigger handler if event.target is the element itself
i.e. not from a child element
So if you want to capture an event, which is not being triggered from anywhere in child component, but when clicking on the whole component, you can use this modifier.

Related

How to capture current set of classes used on an element

I have two elements on the page: button1 and button2. One of them has class active set on an initial load.
I want to grab button1 and check if active is set. If set, then for my tests I want to use button2, if not I stick with button1.
For my test, I need to test the switch action between two buttons, hence why I need to use the button which is not an active one.
Each button does have a text, i.e. <div class="active" data-cy="button1">Button 1</div>, so I was also thinking that maybe a different option would be to grab a button1 with class active set and check if it exists? Not sure if that is possible...
How can the above be achieved?
You want to avoid conditional processing in the test, try to use a selector that targets what you want.
Given
<button class="active">Button 1</button>
<button>Button 2</button>
use a :not() selector
cy.get('button:not(.active)')
.should('have.text', 'Button 2')
If there's other button, also not active but not wanted,
<button class="active">Button 1</button>
<button>Button 2</button>
<button>Ignore me</button>
you can add a filter
cy.get('button:not(.active)')
.filter((index, button) => ['Button 1', 'Button 2'].includes(button.innerText))
.should('have.text', 'Button 2')
If you really meant the active attribute,
<button active>Button 1</button>
<button>Button 2</button>
cy.get('button:not([active])')
.should('have.text', 'Button 2')

How to destroy livewire component on click?

I've created a livewire component with some input fields, added a for loop to the parent component with the child component inside.
#for($i = 0; $i < $count; $i++)
#livewire('dashboard.profile.garage-service-list-item', ['garage' => $garage], key($i))
#endfor
Each child component has a delete button. How can the method for this button look like?
Two ways spring to mind. One being that you display an empty view if the item gets deleted, or you fire an event to refresh all the components that are rendered within that loop.
1: Refreshing the parent
This assumes that the parent component is a Livewire component.
In your parent, you can listen for an event to refresh itself. Simply declare a listener in your parent component (the component that has your #for loop), by adding the following.
protected $listeners = ['refreshParentComponent' => '$refresh'];
refreshParentComponent is the name of the event, you could rename it to something more suitable (that name is probably not a good name), and $refresh is the action -- and in Livewire, the $refresh action is a magical action that simply refreshes the component in its entirety, basically re-rendering it. This would mean that you will get a fresh set of data after deleting the item, and render it all with that data.
To make the event trigger, in your Profile\GarageServiceListItem class, where you delete the item, you can fire, or emit, that particular event - and you can emit it upwards. You can do that by calling the emitUp method.
$this->emitUp('refreshParentComponent');
2: Rendering an empty HTML block
You can add a boolean property to your Livewire-component, for example public $show = true;. Then in the base of your garage-service-list-item view, you start by checking that property.
<div>
#if ($show)
<!-- The rest of your component here -->
#endif
</div>
Then in the method where you delete the component, simply set $this->show = false; and you're done! The component will render a empty <div> block.
Livewire events
Livewire properties

Ui-tabset and bootstrap-daterangepicker not working together?

I have an application where I am using ui-bootstrap-tabs. documentation here. With the ng-bs-daterangepicker.
The behavior I am observing is that whenever I put the daterangepicker inside the ui-tab. It is not able to catch the events attached to it.
But when I move that input tag outside the ui-tabs, it's able to catch the events associated with it.
I have created a working plunker to highlight my issue.
<body ng-app="myApp">
<div ng-controller="testController">
<uib-tabset>
<uib-tab index="0" heading="Drivers"></uib-tab>
<uib-tab index="1" heading="Charts">
<input class="btn btn-danger" type="daterange" id="daterange1" ng-model="dates" format="DD MMM" ranges="ranges" />
</uib-tab>
</uib-tabset>
<!-- <input class="btn btn-danger" type="daterange" id="daterange1" ng-model="dates" format="DD MMM" ranges="ranges" /> -->
</div>
</body>
Here there are two input tags. One inside the uib-tab and other outside it.
$scope.dates = {
startDate: moment().startOf('day'),
endDate: moment().endOf('day')
};
$scope.ranges = {
'Today': [moment(), moment()],
'Yesterday': [moment().subtract('days', 1), moment().subtract('days', 1)],
'Last 7 days': [moment().subtract('days', 7), moment()],
'Last 30 days': [moment().subtract('days', 30), moment()],
'This month': [moment().startOf('month'), moment().endOf('month')]
};
$('#daterange1').on('apply.daterangepicker', function(ev, picker) {
console.log(picker.startDate);
console.log(picker.endDate);
});
The event apply.daterangepicker is not called when I activate the inside input button but is called when I activate the outside one.
My approach
I am guessing it's not a scope issue as highlighted in some posts. Because if it was that, then how come even the date is being populated.
Another thing could be the Known issue column which says
To use clickable elements within the tab, you have override the tab
template to use div elements instead of anchor elements, and replicate
the desired styles from Bootstrap's CSS. This is due to browsers
interpreting anchor elements as the target of any click event, which
triggers routing when certain elements such as buttons are nested
inside the anchor element.
maybe somehow this is stopping the event propagation. I am stuck at this point and can't think of a solution on how to fix it. Hoping the community would help here...
In case of $('#daterange1').on the object to which the event if getting attached must exist at the moment when .on() is invoked.
When daterangepicker is initialized inside Tabs component you could attach event like this:
$("body").on("apply.daterangepicker", "#daterange1", function(e,picker) {
console.log(picker.startDate);
console.log(picker.endDate);
});
Modified plunker

Scripts not working on partial view after Ajax call

I have called scripts on _Layout.cshtml page and my Index.cshtml page has partial view into it. So on page load, SignalR scripts working perfect on partial view, on page end I make another ajax request and load the partial view with another data filled in that and embed under already displayed data, and then the SignalR does not work on the newly embedded record.
This is my index page code:
<div class="col-md-6">
<div class="profile-body">
<div class="row infinite-scroll">
#Html.Partial("_AlbumRow", Model)
</div>
</div>
</div>
This is my partial View Code:
#model IEnumerable<SmartKids.Lib.Core.ViewModels.FileMediaAlbumsVM>
#foreach (var item in Model)
{
<div class="widget">
<div class="block rounded">
<img src="#Url.Content(item.ImageUrl)" alt="#item.Title">
<input type="button" data-image-id="#item.imageId" class="btn btn-sm btn-default">Like</input>
</div>
</div>
}
Kindly help me how to resolve this issue that after making an ajax request I am not able to get those SignalR working. Here is more to say when I put the SignalR scripts on PartialView that works but it also sucks that on each ajax request there is again SignalR loaded on the page and when I click on LIke button it makes many calls to the function behind it.
Kindly help me to resolve this issue, I am stuck at this point since 1 week.
Here is signalR Code:
$(".btn.btn-sm.btn-default").on("click", function () {
var imageId = $(this).attr("data-image-id");
albumClient.server.like(imageId);
});
Problem: You are binding event to elements directly, So when you remove this element and replace it with a different one the events are also removed along with that element, This is something like strongly coupled.
Solution: Use Jquery event delegation. This will make sure the events will be triggered on the current elements and also all the elements that can come in future.
syntax is as below.
$(document).on("click", ".btn.btn-sm.btn-default",function () {
var imageId = $(this).attr("data-image-id");
albumClient.server.like(iamgeId);
});
NOTE: This was never a singlaR issue, it was Jquery issue.
Efficient Way: The problem in using $(document).on("click"... is that when ever there is a click happening on the entire page the Jquery framework will bubble the events from the clicked element upwards(its parent, and its parent and so on..) unless the element specified in the selector arrives, So its kind of performance hit as we don't want this check's to run if we are clicking outside the required area ( button .btn.btn-sm.btn-default in this example).
So best practice is to bind this event delegation to the closest parent possible which will not be removed, <div class="row infinite-scroll"> in this question. So that only when the click happens within this element the event bubbling will happen and also will be stopped once it reaches the parent element,it acts kind of a boundary for event bubbling.
$('.row.infinite-scroll').on("click", ".btn.btn-sm.btn-default",function () {
var imageId = $(this).attr("data-image-id");
albumClient.server.like(iamgeId);
});

How to determine view is backed in Kendo Mobile?

Is there any way to know that view is open by back?
For example
<div data-role="view" id="view-test" data-show="show">
<!-- View content -->
</div>
<script>
var show = function(e){
if(e.view.isBack())
{
console.log("Back")
// do something
}
}
</script>
Is there any method or property like e.view.isBack() ?
There are many ways to handle this, maybe you can use a global variable where you keep the last visited page or even you can add a back button handler and get the view from which the back button was pressed. Another solution would be to pass a parameter along with page navigation when going back, for example:
<a data-role="button" href="#foo?back=true">Link to FOO with back parameter set to true</a>
And on the visited page on show event you can get the parameter like this:
function fooShow(e) {
e.view.params // {back: "true"}
}
Now depending on what the parameter value is you can detect if the back button was pressed or not before reaching the page.

Resources