Reference dynamically created component in Aurelia - aurelia

I know I can create a reference to my component in my view model like this:
.html:
<template>
<mdfield view-model.ref="ref"></mdfield>
</template>
.ts:
export class Vm {
ref: any;
test(){
console.log(this.ref);
}
}
This works, but what is the syntax if I'm creating the components dynamically? Like this:
<template>
<div repeat.for="field of fields">
<mdfield view-model.ref="<what goes here?>"></mdfield>
</div>
</template>
I guess I want to add them to an array in my viewmodel for later reference, but how?

$index gives you the current index of the repeat.for. So, if you want to add the view-model references to an array:
<div repeat.for="field of fields">
<mdfield view-model.ref="refArray[$index]"></mdfield>
</div>

Related

VueJS conditional class in template

I have this template that renders cards of each individual projects. These projects are being pulled from Firebase and stored in an array
<template>
<div class="projects-wrapper">
<div class="individual-project"
v-bind:key="project.id"
v-for="project in projectsCollection"
:title="project.title"
>
<Project :project="project"
v-on:update-likes="updateLikes"
v-on:expand-card="expandCard"
v-on:close-card="closeCard" />
</div>
</div>
</template>
<script>
import Project from './Project'
export default {
name: 'Projects',
data() {
return {
projectsCollection: [],
}
},
I need to implement a functionality of resizing the card (in this example it is "individual-project" div) based on project key's value.
Now, I cannot just use :class="expanded-class, isExpanded" where isExpanded is a key in the project because I need to iterate through array (projectsCollection) and then iterate through an object to get this value.
Hence template does not see this key and fails to apply the class.
How would one approach this task? I am pretty new to Vue, so trying to figure things on the fly.
You don't need to iterate the collection as you have access to each individual project.
You can use the property directly on your div:
<template>
<div class="projects-wrapper">
<div class="individual-project"
v-bind:key="project.id"
v-for="project in projectsCollection"
:title="project.title"
:class="{'expanded-class': project.isExpanded}"
>
<Project :project="project"
v-on:update-likes="updateLikes"
v-on:expand-card="expandCard"
v-on:close-card="closeCard" />
</div>
</div>
</template>

Why isn't the value of the object properties inserted here?

I started learning vue yesterday and I'm now fiddling around on the CLI3.
Currently I'm trying out the different approaches to inserting data into my markup.
Here, I basically want to make a "list of Lists".
This here is list1:
<template>
<div>
<ul v-for="item in items">
<li :text="item"></li>
</ul>
</div>
</template>
<script>
export default{
name:"list1",
data() {
return {
items: {
item1 : "itemA",
item2 : "itemB",
item3 : "itemC"
}
}
}
}
</script>
This is the list of lists:
<template>
<div>
<h1>All my stuff in a biiig list!</h1>
<listOfLists />
</div>
</template>
<script>
import listOfLists from '#/components/listOfLists.vue'
export default {
name: 'myComplexView.vue',
components: {
listOfLists
}
}
And this is inserted into myComplexView.vue inside views (im working with routing as well, though it doesnt work perfectly yet as you will see on the screenshot), which you can see here:
<template>
<div>
<h1>All my stuff in a biiig list!</h1>
<listOfLists />
</div>
</template>
<script>
import listOfLists from '#/components/listOfLists.vue'
export default {
name: 'myComplexView.vue',
components: {
listOfLists
}
}
</script>
This is the result Im getting:
https://imgur.com/H8BaR2X
Since routing doesnt work correctly yet, I had to enter the url into the browser manually. Fortunately, the site at least loaded that way as well, so I can tackle these problems bit by bit ^^
As you can see, the data was iterated over correctly by the v-for.
However, the data wasn't inserted in the text attribute of the li elements.
I'm a bit clueless about the cause though.
Maybe I'm not binding to the correct attribute? Vue is using its own naming conventions, based off standard html and jquery as far as I understood.
You've got this in your template:
<li :text="item"></li>
This will bind the text attribute to the value, outputting, e.g.:
<li text="itemA"></li>
You should be able to see this in the developer tools. In the picture you posted you hadn't expanded the relevant elements so the attributes can't be seen.
I assume what you want is to set the content. For that you'd either use v-text:
<li v-text="item"></li>
or more likely:
<li>{{ item }}</li>
Either of these will output:
<li>itemA</li>
On an unrelated note, I would add that this line will create multiple lists:
<ul v-for="item in items">
It's unclear if that's what you want. You're going to create 3 <ul> elements, each with a single <li> child. If you want to create a single <ul> then move the v-for onto the <li>.

control over inherited attributes by vue component

Is there a way to have control over attributes provided through the component tag?
For example:
<my-component class="myClass" style="myStyle"></my-component>
My component:
<template>
<div>
<div>
</div>
<div>
</div>
</div>
</template>
At render Vue applies given attributes on the root:
<div class="myClass" style="myStyle">
<div>
</div>
<div>
</div>
</div>
I want to control where those attributes are applied like so:
<div>
<div>
</div>
<div class="myClass" style="myStyle">
</div>
</div>
#Boussadjra Brahim answer is definitely one way to handle it, however this will require you to pass in all of the class attributes you want everytime you define the component.
This question is answered in this SO post already as well.How to style a nested component from its parent component in Vuejs?
If you want a bit more flexibility I would suggested using interpolation and properties as below. This will let you define some default classes and pass in whatever else in addition.
<app-header :headerclass="parent-header-class"> </app-header>
Inside of your child component, you can use these properties and v-bind the class inside the HTML, as shown in the example below:
<template>
<div :class=`${headerClass} internal-class-example button`> </div>
</template>
Note: This does not allow you to use any scoped parent CSS to pass to the child. The classes you pass down must be global. Otherwise, the child component will not know what it is.

ViewModel-less compose with model.bind in aurelia

I am having a structure where the main view composes a partial view that composes another partial view in a repeater.
Example:
main view
<template>
<h1>${factory.name}</h1>
<div class="column">
<compose view="./cars.html"></compose>
</div>
</template>
cars view
<template repeat.for="car of factory.cars">
<compose view="./specifications.html model.bind="{test: 'abc}"></compose>
</template>
specifications view
<template repeat.for="car of factory.cars">
<h1>${$parent.$parent.factory.name} - ${car.name}</h1>
${test}
</template>
The problem I am facing is that the model.bind in compose doesn't work. I tried it with the test above, but what I'd actually want to pass there is $parent.$parent.factory so I can output $parent.$parent.factory.name in the specifications view.
(I know I can print it like this, but the scenario gets way more complicated so
the binding is necessary)
Worth to mention that both specifications and cars view are viewmodel-less. Only themain view has a viewmodel where factory and cars are coming from.
According to this page, what I am trying to do is possible, but I can't wrap my head about what I'm doing wrong.
When composing with just an html file, the view-model for the referenced html file is the same as where the compose element is placed. In other words, it inherits the view-model of the parent. So you don't need to supply the model.
main view
<template>
<h1>${factory.name}</h1>
<div>
<compose view="./cars.html"></compose>
</div>
</template>
cars.html
<template>
<div repeat.for="car of factory.cars">
<compose view="./specifications.html"></compose>
</div>
</template>
specifications.html
<template>
<h1>${factory.name} - ${car.name}</h1>
</template>
Take a look at this GistRun example.
What #Jeff G said is correct, but for my specific use-case what solved my issue was creating a simple view-model that I could use for all compositions. It's looking like:
export class Main {
public parentFactory;
public activate(data) {
this.parentFactory= data;
}
}
And in the view
<compose
view="./car.html"
view-model="../view-models/main"
model.bind="$parent.$parent.factory">
</compose>

Providing the model for a component as a slot

Consider the following two custom elements in Aurelia (list & row):
row.html
<template>
<span>${name}</span>
</template>
row.js
export class Row
{
name = "Marry";
}
list.html
<template>
The List
<ol>
<li repeat.for="r of rows">
<slot name="rowItem" model.bind="r"></slot>
</li>
</ol>
</template>
list.js
import { bindable } from 'aurelia-framework';
export class List
{
#bindable
rows = [{name: "John"}];
}
The app will tie them together:
app.html
<template>
<require from="./list"></require>
<require from="./row"></require>
<list rows.bind="users">
<row slot="rowItem"></row>
</list>
</template>
app.js
export class App
{
users = [{name: "Joe"}, {name: "Jack"}, {name: "Jill"}];
}
The problem is that the model for the row is not set correctly. All I get as the output is the following:
The List
1.
2.
3.
So the question is; how can I provide the model for a slot in Aurelia?
Here's a Gist to demonstrate the problem in action.
Slots aren't going to work for what you want to do. It's a known limitation of slots in Aurelia. Slots can't be dynamically generated (such as inside a repeater).
Luckily, there's another option to accomplish what you want: template parts.
Template parts aren't well documented (my fault, I should have written the docs for them). But we have some docs in our cheat sheet. I've modified your gist to show how to use them: https://gist.run/?id=1c4c93f0d472729490e2934b06e14b50
Basically, you'll have a template element in your custom element's HTML that has the replaceable attribute on it along with a part="something" attribute (where something is replaced with the template part's name. Then, when you use the custom element, you'll have another template element that has the replace-part="something" attribute (again, where something is replaced with the template part's name). It looks like this:
list.html
<template>
The List
<ol>
<li repeat.for="row of rows">
<template replaceable part="row-template">
${row}
</template>
</li>
</ol>
</template>
app.html
<template>
<require from="./list"></require>
<require from="./row"></require>
<list rows.bind="users">
<template replace-part="row-template">
<row name.bind="row.name"></row>
</template>
</list>
</template>