Unfortunately, as awesome as our example app is, it's kinda messy. You probably don't want
this much stuff in one place. Therefore, we're going to move out displaying the name into
its own component, show-name
.
Toss this in lib/show_name.dart
:
import 'package:vue/vue.dart';
@VueComponent(template: '<p>Your name is: Bob</p>')
class ShowName extends VueComponentBase {
}
Also, change index.dart
:
import 'package:vuedart_example/show_name.dart'; // <-- This imports your component
@VueApp(el: '#app', components: [ShowName]) // <-- Use the component here
class App // ...
and index.html
:
<body>
<div id="app">
<input v-model="name">
<show-name></show-name>
</div>
</body>
Now refresh your browser page. You should see it now say, Your name is Bob.
Let's take a few steps back. What exactly is going on here?
@VueApp
, just like components:
does in Vue.That being said, putting templates in the annotation string is just nasty. In vanilla Vue, you can use webpack for stuff like this. Does VueDart have an alternative? Yup!
Change the VueComponent annotation in show_name.dart
to read:
@VueComponent(template: '<<show_name.html')
and create lib/show_name.html
containing the following:
<template vuedart>
<p>Your name is: Bob</p>
</template>
The <<show_name.html
syntax means read show_name.html and use the template inside here.
VueDart will read the file, extract the contents of the template element, and use that
for your template. However, this gets even better! Since your template is almost always
the same name as your Dart code (e.g. show_name.dart
and show_name.html
), VueDart
uses that as the default. Therefore, you can abbreviate all this to:
@VueComponent(template: '<<')
and VueDart will automatically use the template in show_name.html
.
Well, this component is a bit of a downgrade. Before, we could display any name; now we're limited to Bob's. Let's try using some properties:
@VueComponent(template: '<<')
class ShowName extends VueComponentBase {
@prop
String name;
}
<template vuedart>
<p>Your name is: {{name}}</p>
</template>
Props in VueDart are declared via the prop annotation, and the syntax is just like normal
Dart. You can also give a default value by doing String name = "default name"
.
Now modify index.html
:
<body vuedart>
<div id="app">
<input v-model="name">
<show-name :name="name"></show-name>
</div>
</body>
Voila! We're back to the same code, but now it's better organized. Yaaay!!
(Yes, I left out the capitalization; that's an exercise for the reader!)
VueDart also supports scoped styles via scopify. Here's how they work:
@VueComponent(template: '<<')
class ShowName extends VueComponentBase {
@prop
String name;
}
<template vuedart>
<p>Your name is: {{name}}</p>
</template>
<style scoped>
p { color: purple; }
</style>
The syntax for scoped styles closely resembles Vue's own single-file components.
If you want scoped styles to "bleed" into v-html
elements other similar items (e.g.
jQuery plugins), you can add the bleeds attribute to your style element:
<template vuedart>
<div v-html="'<p>Inside v-html!</p>'"></div>
</template>
<style scoped bleeds>
p { color: purple; }
</style>
Mixins are easy to declare in VueDart. Take this example:
@VueMixin()
abstract class TodoMixin implements VueMixinRequirements {
@method
String capitalize(String thing) => thing.toUpperCase();
}
There are three important things about this:
@VueMixin()
annotation defines a mixin. You can also use components: [...]
just like
on other VueDart annotations.
mixin, otherwise you'll get incredibly bizarre analyzer errors.abstract
.implements VueMixinRequirements
.Now you can use your mixin:
@VueComponent(template: '<<')
class ShowName extends VueComponentBase with TodoMixin {
// ...
}
This looks exactly like a normal Dart mixin, except the @VueMixin()
annotation from earlier
makes it a Vue mixin.
Lifecycle callbacks are dead simple in VueDart! They're just simple method overrides:
@VueComponent(template: '<pHello!</p>')
class MyComponent extends VueComponentBase {
@override
void lifecycleCreated() => print("created!");
@override
void lifecycleMounted() => print("mounted!");
@override
void lifecycleDestroyed() => print("destroyed!");
}
and so forth for all the other lifecycle callbacks.
Take the following component:
@VueComponent(template: '<div ref="text">Hello!</div>')
class MyComponent extends VueComponentBase {
@override
void lifecycleMounted() {
// How to access the div element?
}
}
For this, you can use the @ref
annotation:
import 'dart:html'; // to get DivElement
@VueComponent(template: '<div ref="text">Hello!</div>')
class MyComponent extends VueComponentBase {
@ref
DivElement text;
@override
void lifecycleMounted() => print(text.text);
}
Piece of cake, right? It's like declaring a normal attribute.
Accessing component refs is just as simple:
@VueComponent(template: '<div>Hello!</div>')
class FirstComponent extends VueComponentBase {
FirstComponent(context): super(context);
@computed
String get stuff => 'Hello!';
}
@VueComponent(template: '<first-component ref="first"></first-component>',
components: [FirstComponent])
class SecondComponent extends VueComponentBase {
@ref
FirstComponent first;
@override
void lifecycleMounted() => print(first.stuff); // Hello!
}
If you need to get a ref by name instead of declaring it ahead-of-type, use the $ref
function:
void lifecycleMounted() {
print($ref('first') as FirstComponent);
}
Vue 2.2's model property is also supported, except it
instead uses the @model
annotation.
@VueComponent(template: '<<')
class MyComponent extends VueComponentBase {
@model(event: 'my-event')
@prop
bool myProp = false;
}
If you omit event: ...
, it will default to input just like Vue itself.