Vue - pass element Object without an event - vue.js

How can i pass a target element to a method, without using an event?
isVideoRender($this) will not pass any element objects.
I know it works with events, but can i pass current Objects also through Methods?
<button v-html="isVideoRender($this)?icons('videorender'):'create Video'"></button>
isVideoRender(el) {
if (this.video.rendering == true) {
$(el.target).prop('disabled', true);
return true;
}
}

Related

How to reset all states when property value is changed from javascript?

I am using stencil framework. In my component I am using different states to trigger different events. I am also updating the property value of component from javascript.
I would like to reset all states value and reload the component with updated property value.
New property value is responsible for many actions like calling api, generating the cache key etc.
Can anyone suggest me the best approach to fulfill my requirement. Currently I am reset all the states in watcher method of property and call the componentWillLoad event but I am facing many issue in this approach.
Sample code
#Prop() symbol!: string;
#Watch('symbol')
symbolChanged(newSymbol: string, prevSymbol: string) {
if (newSymbol && newSymbol !== prevSymbol) {
this.resetStates();
}
}
resetStates() {
//Reset all state values here
this.componentWillLoad();
}
By setting key property on root element of render method would solve my issue like below code snippet.
uniqKeyId = uniqid.get();
#Prop() symbol!: string;
#Watch('symbol')
sysmbolWatcher(newSymbol: string, prevSysmbol: string) {
if (newSymbol != prevSysmbol) {
//update key attribute each switch of exchange
this.uniqKeyId = uniqid.get();
//Set default values based on properties as to consider this as fresh request.
this.setDefaultValues();
}
}
And in render method like below
render() {
return (
<section class="cid-minichart" key={this.uniqKeyId}>
//Render markup
</section>
);
}

Vue Keys do not delete from Object

I'm trying to delete a key from an object in a parent component. A child component emits an event (with an item value) back to the parent method that triggers the delete in the parent's data object.
Parent component:
data() {
return {
savedNews: Object
}
},
methods: {
containsKey(obj, key) {
var result = Object.keys(obj).includes(key)
return result
},
handleSaveNews(item) {
if (!this.containsKey(this.savedNews, item.url)) {
this.savedNews = {
[item.url]: item,
...this.savedNews
}
} else {
console.log(this.containsKey(this.savedNews, item.url))
var res = delete(this.savedNews, item.url)
console.log(res)
console.log(this.containsKey(this.savedNews, item.url))
}
}
}
All of the console.logs in the last else statement return true. It's saying that the delete was successful yet the key is still there. How do I delete this key?
From the docs:
Vue cannot detect property addition or deletion
Use this.$delete:
this.$delete(this.savedNews, item.url)
or this.$set (which also should be used for property changes):
this.$set(this.savedNews, item.url, undefined);
Extra info: The $ is a naming convention Vue uses for its built-in methods that are available on each component instance. There are some plugins which opt to follow this pattern too. You can also use built-ins inside other modules if you import Vue and use Vue.delete, for example. You could add your own methods like Vue.prototype.$mymethod = ....

How to make preventDefault for change on Element UI radio group

I'm using Element UI radio button group and I want to use preventDefault() when trigering change event:
<el-radio-group #change="changeHandler" v-model="radio1">
<el-radio-button label="New York"></el-radio-button>
<el-radio-button label="Washington"></el-radio-button>
<el-radio-button label="Los Angeles"></el-radio-button>
<el-radio-button label="Chicago"></el-radio-button>
</el-radio-group>
Script:
methods: {
changeHandler(value, e) {
// e is undefined
// here should be preventDefault
}
}
I tried set second parameter to change function, but it is undefined.
Element UI works a bit differently. #change just returns the value chosen, nothing else. We need to use native to access the Event object. But #change.native won't work, as the change has already happened (if you want to actually prevent the change). As a side note, I would use disabled attribute instead (like presented in documentation). In my opinion for the UX it's weird that a user cannot choose a radio button, but putting that aside.... Use #click.native instead if you want to prevent a choice:
#click.native="changeHandler"
Then you have access to the Event object:
changeHandler(e) {
console.log(e.target.value)
if(e.target.value == "Washington") {
e.preventDefault();
}
}
CODEPEN
You're having the parameters in the method wrong. The are 2 ways this can go using the #change event handler:
A
defining a change handler without arguments, like
<el-radio-group #change="changeHandler" v-model="radio1" ></el-radio-group>
changeHandler(e) {
// e will be the Event object
console.log(e)
}
B
defining a change handler with arguments, put in $event to still include the event object
<el-radio-group #change="changeHandler($event, 'string')" v-model="radio1" ></el-radio-group>
changeHandler(e, value) {
// e will be the Event object
console.log(e);
// value will be 'string'
console.log(value);
}

Call a custom attribute method from the view model

I have a custom attribute with a method to show and hide some HTML content, I've attached the attribute to an element in a view model.
How can I call a method defined in the custom attribute from the view model?
To access the custom attribute's view-model, just put the custom attribute on the element a second time, but this time put .ref="viewModelPropertyName" on to the attribute. Then, in the parent view-model, you can access methods on the attribute using viewModelPropertyName (or whatever name you gave it). You can see an example of this here: https://gist.run/?id=9819e9bf73f6bb43b07af355c5e166ad
app.html
<template>
<require from="./has-method"></require>
<div has-method="hello" has-method.ref="theAttribute"></div>
<button click.trigger="callMethod()">Call method</button>
</template>
app.js
export class App {
callMethod() {
const result = this.theAttribute.someMethod('blah');
}
}
has-method.js
export class HasMethodCustomAttribute {
someMethod(foo) {
console.log('someMethod called with foo = ' + foo + ', this.value = ' + this.value);
return `Hello ${foo}`;
}
}
There are some ways to do it, but I believe the ideal would be binding a property from your custom-attribute to your view-model. For example:
MyCustomAttribute {
#bindable showOrHide; //use this to show or hide your element
}
MyViewModel {
visible = false;
}
Usage:
<div my-custom-attribute="showOrHide.bind: visible"></div>
So, whenever you change visible you will also change showOrHide.
Nevertheless, is good to remember that Aurelia already has a show and if custom-attributes:
<div show.bind="visible" my-custom-attribute></div>
<div if.bind="visible" my-custom-attribute></div>
Make sure if you really need to create this behaviour in your custom-attribute.
This can be done without the need for a ref. Here is an example that shows how.
It calls a showNotification method on the custom attribute from the custom element using the custom attribute.
In the custom attribute:
#bindable({ defaultBindingMode: bindingMode.twoWay }) showNotificationCallback: ()=> void;
bind() {
this.showNotificationCallback = this.showNotification.bind(this);
}
showNotification() {
// Your code here
}
In the custom element view (Note the absence of parens in the value of this binding):
<div notification="show-notification-callback.bind: showSaveSuccessNotification;></div>
In the custom element view-model:
// Show the save success view to the user.
if (typeof this.showSaveSuccessNotification=== 'function') {
this.showSaveSuccessNotification();
}

How to tear down an enhanced fragment

I am working on an existing SPA where we replace components with Aurelia components step by step. We use the enhance API of the TemplatingEngine. That works pretty well but we also need to tear down those enhanced fragments (remove event listeners, ...) when moving to another part of the application (no page reload).
My idea is to keep the aurelia instance in the page and reuse it.
Currently I enhance fragments like this:
function enhanceFragment(targetElement) {
function proceed() {
let aurelia = window.DFAurelia;
let engine = aurelia.container.get(TemplatingEngine);
engine.enhance({
container: aurelia.container,
element: targetElement,
resources: aurelia.resources
});
}
if (!window.DFAurelia) {
bootstrap(async aurelia => {
aurelia.use
.defaultBindingLanguage()
.defaultResources()
.eventAggregator()
.developmentLogging()
.globalResources('app/df-element');
await aurelia.start();
window.DFAurelia = aurelia;
proceed();
});
} else {
proceed();
}
}
The HTML I enhance looks like:
<df-element></df-element>
I tried this in a function of the custom element itself (DfElement::removeMyself()):
let vs: ViewSlot = this.container.get(ViewSlot);
let view: View = this.container.get(View);
vs.remove(view);
vs.detached();
vs.unbind();
but I get an error when getting the view from the container (Cannot read property 'resources' of undefined). I called this function from a click handler.
Main question: how to manually trigger the unbind and detached hooks of the DfElement and its children?
Bonus questions: properties of my aurelia instance (window.DFAurelia) root and host are undefined: is that a bad thing? Do you see any potential issue with this way of enhancing (and un-enhancing) fragments in the page?
Use the View returned from the enhance() method.
The enhance() method returns the enhanced View object. It is a good practice to manage the teardown from the same location where you call enhance(), as you may not be able to trust an element to remember to tear itself down. However, you can always register the View instance with the enhance container to access it from within the custom element.
function proceed() {
let aurelia = window.DFAurelia;
let container = aurelia.container;
let engine = container.get(TemplatingEngine);
let view = engine.enhance({
container: container,
element: targetElement,
resources: aurelia.resources
});
container.registerInstance(View, view);
}
This will tell the DI container to respond to calls for View with this View.
import { inject, Aurelia, View } from 'aurelia-framework';
#inject(Aurelia, Element)
export class DFCustomElement {
// element is passed to the constructor
constructor(aurelia, element) {
this.container = aurelia.container;
this.element = element;
}
// but View is only available after attached
attached() {
this.view = this.container.get(View);
}
removeMyself() {
this.element.remove();
this.view.detached();
this.view.unbind();
}
}
Using the created(view) lifecycle method
A much better practice would be to use the created(view) lifecycle method in your custom element.
import { inject } from 'aurelia-framework';
#inject(Element)
export class DFCustomElement {
constructor(element) {
this.element = element;
}
created(view) {
this.view = view;
}
removeMyself() {
this.element.remove();
this.view.detached();
this.view.unbind();
}
}
This is a much more straightforward, best practices way of having a custom element grab its own View. However, when trying to write this answer for you, I tested nesting a custom element in a <compose> element. The result was that the View in my custom element was actually the View for my <compose> element, and removeMyself() removed the <compose> entirely.