Vuejs v-bind class not applying the classes dynamically - vue.js

<button #click="startEffect"> Start Effect</button>
<br/>
<div id="effect" class="highlight" :class="{highlight: enableHighlight}"></div>
I just need the highlight class to be applied based on the data property enableHighlight but for some reason It doesn't apply the class when the startEffect function is called.
<script>
export default {
data() {
return {
enableHighlight: false
}
},
methods: {
startEffect: function () {
this.enableHighlight = !this.enableHighlight;
}
}
}
</script>
I have debugged and confirmed that value of enableHighlight is switched when clicking the button and that the CSS classes are present. However upon clicking the button the class is not applied to the div.

You really mess with Vue when having a "normal" class attribute and one dynamic. Remove the normal one.
<div id="effect" :class="{highlight: enableHighlight}"></div>
To make it work, you need to remove the function in startEffect's definition:
startEffect() {
this.enableHighlight = !this.enableHighlight;
}
Why? because this isn't the same in the different ways to define the function. Learn more about this here.

Related

Add custom CSS class to element `.ck-content` in CKEdtior5 and Vue2

How to add custom CSS class to element .ck-content, by other words, to editable formatted text container in CKEditor5 and Vue2?
The ck-content is the input field; we must differ it from the toolbar above, the other part of CKEditor. So if we want to apply some class affecting only to inputted content, it must not affect to toolbar.
Seems to be obvious solution:
<ckeditor class="CustomClass" :editor="editor" v-model="editorData" :config="editorConfig"></ckeditor>
will not work because CustomClass it will not be applied to .ck-content and even not to root element.
Below solution will not work too, because when CKEditorWrapper mounted, the CKEditor mounting has not complete yet.
import CK_EditorVueAdaptation from "#ckeditor/ckeditor5-vue2";
#Component({
components: {
ckeditor: (CK_EditorVueAdaptation as { component: VueClass<unknown>; }).component
}
})
class CKEditorWrapper extends Vue {
private mounted(): void {
this.$el.getElementsByClassName("ck-content").item(0) // null
}
}
Vue CKeditor component emits ready event when it's really mounted and rendered. So an easy solution: catch it and add the class to the wanted node, having in mind that this.$el is the sibling of the real container of the editor.
<ckeditor
...
#ready="onEditorReady"
/>
...
,
methods: {
onEditorReady () {
const myClass = 'anyCustomClass'
const addCustomClass = () => this.$el.nextSibling
.querySelector('.ck-content')
.classList
.add(myClass)
this.$nextTick(addCustomClass)
}
}

Add focus to first input on load and next input after success using vue

I am creating a fill in the blank using vue. I have three components, a parent component where I build the question, an input component where I validate and the text component. I have stripped out a lot of the code to try and keep the question relevant, anything commented out is unsuccessful. Hope I am not out in left field on this one but have never attempted this so thought I would try here.
First issue:
Some of the questions have two inputs and I want to auto provide focus to the first input, (using a custom directive ) I am able to gain focus on the last created input. I am not really sure how to access first child I guess. Works well with single input questions though. I have tried doing so by using $refs and $nextTick(()) but no luck.
Second issue:
Once one input gets isCorrect I want to auto focus to the next available non correct element. I figured I would have be able to access the child input from the parent component and have been trying using this link but I have been unsuccessful.
Any help or insights is much appreciated. thanks
What it looks like for visual
What I am after
Parent Component
import ivInput from "../inline-components/iv-input.vue";
import ivText from "../inline-components/iv-text.vue";
<component
:is="buildQuestion(index)"
ref="ivWrap"
v-for="(item, index) in questionParts"
:key="index">
</component>
export default() {
components:{
ivInput,
ivText
},
mounted(){
// console.log(this.$refs.ivWrap)
// console.log(this.$refs.iv)
},
methods: {
buildQuestion: function (index) {
if(this.questionParts[index].includes('*_*')){
return ivInput
}else{
return ivText
}
},
//focus: function (){
// this.$refs.iv.focus()
// console.log(this.$refs.iv)
// }
}
}
Input Component
<div :class="'iv-input-wrap'">
<input
ref="iv"
v-focus
type="text"
v-model="userInput"
:class="[{'is-correct':isCorrect, 'is-error':isError}, 'iv-input']"
:disabled="isCorrect">
</div>
export default{
// directives:{
// directive definition
// inserted: function (el) {
// el.focus()
// },
}
computed{
isCorrect: function () {
if(this.isType == true && this.isMatch == true){
// this.$refs.iv.focus()
// this.$nextTick(() => this.$refs.iv.focus)
return true
}
}
}
}

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();
}

Binding method result to v-model with Vue.js

How do you bind a method result to a v-model with Vue.js?
example :
<someTag v-model="method_name(data_attribute)"></someTag>
I can't make it work for some reason.
Thank you.
Years later, with more experience, I found out that is it easier to bind :value instead of using v-model. Then you can handle the update by catching #change.
Edit (per request):
<input :value="myValue" #change="updateMyValue">
...
methods: {
updateMyValue (event) {
myValue = event.target.value.trim() // Formatting example
}
}
And in a child component:
// ChildComponent.vue
<template>
<button
v-for="i in [1,2,3]">
#click="$emit('change', i) />
</template>
// ParentComponent.vue
<template>
<child-component #change="updateMyValue" />
</template>
<script>
import ChildComponent from './child-component'
export default {
components: {
ChildComponent
},
data () {
return {
myvalue: 0
}
},
methods: {
updateMyValue (newValue) {
this.myvalue = newValue
}
}
}
</script>
v-model expressions must have a get and set function. For most variables this is pretty straight forward but you can also use a computed property to define them yourself like so:
data:function(){
return { value: 5 }
},
computed: {
doubleValue: {
get(){
//this function will determine what is displayed in the input
return this.value*2;
},
set(newVal){
//this function will run whenever the input changes
this.value = newVal/2;
}
}
}
Then you can use <input v-model="doubleValue"></input>
if you just want the tag to display a method result, use <tag>{{method_name(data_attribute)}}</tag>
Agree with the :value and #change combination greenymaster.
Even when we split the computed property in get/set, which is help, it seems very complicated to make it work if you require a parameter when you call for get().
My example is a medium sized dynamic object list, that populates a complex list of inputs, so:
I can't put a watch easily on a child element, unless I watch the entire parent list with deep, but it would require more complex function to determine which of the innter props and/or lists changed and do what fromthere
I can't use directly a method with v-model, since, it works for providing a 'get(param)' method (so to speak), but it does not have a 'set()' one
And the splitting of a computed property, have the same problem but inverse, having a 'set()' but not a 'get(param)'

Getting element height

I was curious if I can get element properties form component template.
So I have made simple div with class and I've made this class:
export class viewApp{
elementView: any;
viewHeight: number;
myDOM: Object;
constructor() {
this.myDOM = new BrowserDomAdapter();
}
clickMe(){
this.elementView = this.myDOM.query('div.view-main-screen');
this.viewHeight = this.myDOM.getStyle(this.elementView, 'height');
}
}
getStyle(), query() are from BrowserDomAdapter.
My problem is when I try to get height it is null, but when I set some height by setStyle() and then I get it by getStyle() it returns proper value.
After checking DOM and styles in browser I discovered that is because of two CSS elements. One is: .view-main-screen[_ngcontent-aer-1]{} and second one is element{}.
.view-main-screen has some stylings, but element is empty. When I add styles by setStyle() it appears in element{}. Why is that? How can I get element properties by using Angular2?
The correct way is to use #ViewChild() decorator:
https://angular.io/docs/ts/latest/api/core/index/ViewChild-decorator.html
Template:
<div class="view-main-screen" #mainScreen></div>
Component:
import { ElementRef, ViewChild } from '#angular/core';
export class viewApp{
#ViewChild('mainScreen') elementView: ElementRef;
viewHeight: number;
constructor() {
}
clickMe(){
this.viewHeight = this.elementView.nativeElement.offsetHeight;
}
}
That should do it but obviously you need to add your Component decorator.
Edit:
For Angular 8 or later you need to provide the 2nd parameter in ViewChild
#ViewChild('mainScreen', {read: ElementRef, static:false}) elementView: ElementRef;
update2
constructor(private elementRef:ElementRef) {}
someMethod() {
console.log(this.elementRef.nativeElement.offsetHeight);
}
Accessing nativeElement directly is discouraged but current Angular doesn't provide other ways as far as I know.
update
https://github.com/angular/angular/pull/8452#issuecomment-220460761
mhevery commented 12 days ago
We have decided to remove Ruler service, and so it is not part of the public API.
original
As far as I know the Ruler class should provide that functionality
https://github.com/angular/angular/blob/master/modules/angular2/src/platform/browser/ruler.ts if this isn't enought you probably need to access elementRef.nativeElement and use direct DOM access and functions provided by the elements.
new Ruler(DOM).measure(this.elRef).then((rect: any) => {
});
Rules service is safe in WebWorker.
See also the comments on https://github.com/angular/angular/issues/6515#issuecomment-173353649
<div #getElementHeight>
Height
</div>
Height of element is {{ getElementHeight.offsetHeight }}
<div *ngFor="let item of items" (click)="itemClick($event.currentTarget)"></div>
itemClick(dom){
var height=dom.clientHeight;
// ...
}