Vue custom directives - javascript

I need a solution to get all inputs in my vue app and then do something with them i.e styling.
I am thinking about using directives for this.
I always use this way when I work on angular project, where I can create a directive, which has a selector i.e
selector : input[type="text"].
export class MyCustomDirective {
constructor(el) {}
.............
}
and then when I start my app I get all inputs of the app without adding the created directive to them one by one.
Unfortunately Vue directives are not able to do this, because I must prefix the created directive with v-
so this wont work
Vue.directive('input', {
bind: function (el) {
// some magic
},
});
any help or new ideas ?

Related

VueJS: Possible to set v- directives within a directive, defined in a module?

I'm creating a master checkbox mixin, which manages the state of a masterCheckbox, and followerCheckboxes. Instead of having to set directives like v-model several times on different checkbox elements, it would be alot nicer to set one directive which takes care of that.
The example below is not working, and instead I have to set these individually on the component that uses the mixin. Am I doing something wrong, or is this just not possible?
// define a mixin object
module.exports = {
directives: {
masterCheckbox: {
// directive definition
bind: function (el, binding, vnode) {
el.setAttribute("v-on:click", "toggleMasterCheckbox");
el.setAttribute("v-model", "masterCheckboxChecked");
el.setAttribute(":indeterminate.prop", "masterCheckboxIndeterminate");
}
},
followerCheckbox: {
// directive definition
bind: function (el, binding, vnode) {
el.setAttribute("v-model", "checkedCheckboxes");
}
}
},
...
}
Directives are not attributes, they are non-HTML markup in the template that Vue processes away before creating HTML. You're setting attributes on DOM elements well after Vue has created them from template. So what you are trying to do cannot be done.
You should probably set up a component – possibly a dynamic component – in whose template you can include all the appropriate directives, and then use multiple places. But I think your click and v-model directives are going to fight, as v-model is going to change the value of masterCheckboxChecked, and so is toggleMasterCheckbox.

Using hasClass with vue.js

I know this setup I have isn't ideal however I need to check the class of an input element. I have just started using Vue.js on a new project however we have a form validation library that has to be used that uses jQuery.
The validation library I am using adds a class to an input if it doesn't fit some validation requirements.
If I was using jQuery I could just use hasClass to check if the input element had a specific class.
In Vue I am unsure how I can check if an element has a class though?
Here is an example method of what I'd like to achieve:
methods: {
nextStep: function() {
const element = this.$el.querySelector("#conversation__tram-1 input");
if (element has the class of is-valid) {
Do something
}
},
},
As you can see I'd like to check whether or not the input field has a class of is-valid. Can you think of any way I can do this with Vue.js? Thanks.
You can use the element's classList property:
if (element.classList.contains('is-valid')) {
// Do something
}
Most of the native vue.js functions involve manipulating the data behind the DOM, not the DOM itself.
There are multiple ways you can achieve this,
You can use Vue class binding, which is two-way. So you once you initialize binding then you can build an API to expose that.
Have you tried this?
if (this.$els.elementNameHere.className.match(/\bmyclass\b/)) {
//
}
If jQuery is already involved, just use its hasClass the way you already know how.
nextStep: function() {
const element = this.$el.querySelector("#conversation__tram-1 input");
if ($(element).hasClass('is-valid')) {
Do something
}
},
Vue does not provide any special DOM manipulation routines, because in Vue, you aren't generally supposed to be manipulating the DOM. There are some carved-out exceptions, but this isn't really one. :) The key to mixing jQuery and Vue is that you must be careful to separate what jQuery controls from what Vue controls so they don't step on each other's toes. Not much harm here in simply reading the DOM as long as you don't expect Vue to be updating it.

Update dom after component rendered the view, best practice in Angular2?

I'm seeking some advice how I should handle elements when working with Angular2.
I have stored some elements id's in the localstorage, and want to set a selected class name on some specific elements.
For now I'm using this way:
ngAfterViewChecked() {
// Check if seats has been selected before
var selectedSeats: elementInterface = JSON.parse(localStorage.getItem('SelectedSeats'));
if (selectedSeats != null) {
var outboundElement = document.getElementById(selectedSeats.outboundSelectedElement);
var returnElement = document.getElementById(selectedSeats.returnSelectedElement);
this.autoSelectSeats(outboundElement);
this.autoSelectSeats(returnElement);
}
}
Method:
private autoSelectSeats(element: Element) {
// Set selected class on element
element.classList.add('selected');
}
I see two issues here. The first is the ngAfterViewChecked hook that fires more than once after the view is created. Is there something I can do so it only fires once?
Second: Is there a better way to get the element when you know the id and set a class attribute on it after the content has loaded?
I can't seem to find the Angular2 way of doing it, besides using this hook.
Any idea? Also, please don't post Jquery posts, as I don't want to implement that when using Angular :)
How about adding a custom directive to each of your "seat" elements and let that directive add the CSS class?
In your template, the directive would be used as follows (I'm guessing, since you didn't show your template):
<div *ngFor="let seat of seats" [highlight]="seat.id">
...
</div>
You need to pass some information to the directive to identify which seat it is working on. It seems better to pass an id directly (e.g. seat.id) rather than to rely on HTML ids (although in your case they might be one and the same).
Now the code for the directive:
#Directive({
selector: '[highlight]'
})
export class HighlightDirective {
#Input() highlight: string; // This will contain a seat.id
constructor(el: ElementRef, ss: SeatService) {
const selectedSeats = ss.getSelectedSeats();
// If current seat found in selectedSeats, mark it as selected.
if (selectedSeats.indexOf(this.highlight) !== -1) {
this.el.nativeElement.classList.add('selected');
}
}
}
The reason I'm using an external service SeatService to get the data from localStorage is that Angular will create an instance of HighlightDirective for every match it finds in your template. You don't want to refetch the selected seats in every instance (the service lets you cache the seats and return the same data).
Angular way has pretty good documentation, classes are toggled using the following syntax: [class.selected]="selected"

<require> inside a globalResource component called with enhance in Aurelia

So, I'm trying to setup Aurelia in my Angular 1 web application so I can slowly upgrade. I need to do that since the application is too big and migrating everything at once would be impossible.
So, in my Aurelia folder I created a component folder with two components (aurelia-component.js and another-component.js with their views aurelia-component.html and another-component.html), I won't put the javascript as they are just two classes with one property, the html for both is the same, the only thing that changes is the text property value so I can differentiate them:
<template>
<div>${text}</div>
</template>
My entry point main.js looks like this:
export function configure(aurelia) {
aurelia.use
.basicConfiguration()
.developmentLogging()
.globalResources('components/aurelia-component')
.globalResources('components/another-component');
//window.aurelia = aurelia;
aurelia.start()
.then(a => {
window.aurelia = a;
});
}
As you can see, this puts Aurelia in the window object so I can access it from my Angular app, I'll improve this later.
In my angular app I have this directive:
'use strict';
function AureliaContainer() {
function Link($scope, element, attrs) {
window.aurelia.enhance(element[0]);
}
//
return {
restrict: 'A',
link: Link
};
}
module.exports = AureliaContainer;
I set this up in my app root with:
app.directive('aureliaContainer', require('./directives/aurelia.container'));
And in my Angular View I have these divs with my directive that calls the enhance function from Aurelia:
<div aurelia-container>
<aurelia-component></aurelia-component>
</div>
<div aurelia-container>
<another-component></another-component>
</div>
The reason I have two aurelia-container in the html is that I know I'll have to have more than one when I'm migrating the application.
And this works fine, both components load normally in the screen.
The problem is when I try to call another component from within one of those components.
What I did was, I created a new component called test-component.js with its view test-component.html. The html for this is just:
<template>
<h1>Header</h1>
</template>
And then, from the aurelia-component.html I called it using:
<template>
<require from="./test-component"></require>
<div>${text}</div>
<test-component></test-component>
</template>
Now, when I load the page, the test-component actually loads but the <div>${text}</div part of aurelia-component doesn't and I get this error in the console:
Uncaught (in promise) TypeError: Cannot read property 'behaviorInstructions' of undefined
I really don't understand why this error is happening, I should be able to load a custom element from within another one normally, shouldn't I. Or is there a limitation when you use enhance?
I also tried to use setRoot in both divs with no success, just one of them is loaded.
Maybe there's a better approach for this?
Again, I can't migrate my entire application at once, it's just no feasible.
Thanks in advance for the help.
First off, I know nothing about progressive enhancement in Aurelia. And cannot comment about its suitability for your scenario.
But I am wondering if maybe you missed some Au dependencies (like binding or templating?)
http://aurelia.io/hub.html#/doc/article/aurelia/framework/latest/app-configuration-and-startup/8
aurelia.use
.defaultBindingLanguage()
.defaultResources()
.developmentLogging()
.globalResources('resources/my-component');
That might explain why it fails when you want it to render a template?

Angular 1.5 Component Host Element Attributes

I'd like to be able to set custom attributes on the host element for Angular 1.5 components.
Why?
I want to add a class to a component so I can style it. For example, if a component's parent has display: flex set, I'll likely want to set the flex property on the component.
It's often useful to conditionally apply a class depending on a component's state.
In certain circumstances, I'd like to use ARIA attributes to make a component more accessible.
Here's a simplified example of what I'd like to do (it obviously doesn't work, but I'm looking for something similar):
angular.module("app").component('hello', {
attributes() {
return {
class: "hello " + (this.greeting ? "hello--visibile" : ""),
data-greeting: this.greeting
}
},
bindings: {
greeting: "<",
}
})
It looks like Angular 2.0 supports this feature, but I don't see anything in the docs about supporting it in 1.5. Is there a way to accomplish this for those of us still stuck using .component?
In the past, I would have simply used replace: true to solve this problem, but that's been deprecated and isn't even available for .component.
As you mention, it is not directly possible as you describe. An attributes property would be usefull, but does not exist as of yet.
A possible work-around would be to use $element inside your controller. This passes a jQuery element as a dependency, so you can add attributes as needed.
angular
.module('myComponent', [])
.component('myComponent', {
bindings: {
greeting: '<'
},
controller: function($element) {
$element.addClass('hello-world');
$element.attr('aria-hidden', true);
$element.data('my-greeting', this.greeting);
},
template: 'Define your template here.'
});
The <myComponent> element would now have a hello-world class and a aria-hidden attribute. It is even possible to use bindings, as described above with greeting.
The angular component definition is just a wrapper around normal directives.

Categories

Resources