Hi I am looking for a way to probarly add new VM objects in my current VM
Can anyone help me how to do this?
The idea is that this should output
Hi!
my name is jhonny
Code i have so far
import {foo} from 'dist/foo';
export class Main{
constructor(){
this.heading = "hi!"
this.fooVar = new Foo("jhonny");
}
}
<template>
<section>
<h2>${heading}</h2>
<import from="./foo"></import>
<foo item.bind="fooVar"></foo>
</section>
</template>
export class Foo{
static Behavior(){
return Behavior
.customElement('foo');
}
constructor(name){
this.name = name;
}
}
<template>
<section>
<h2>my name is ${name}</h2>
</section>
</template>
Also tried
<foo item.bind="fooVar"></foo>
<foo bind="fooVar"></foo>
<foo model.bind="fooVar"></foo>
<compose
model.bind="fooVar"
view-model="foo">
</compose>
They all give "my name is" without the variable name. So i guess its not binding
I think you don't need to instantiate the Foo.
and it probably should be:
<foo name.bind="fooVar"></foo>
and
Behaviour.withProperty('name');
Your stated goal is: Add VM object in current VM. Here's the code for that (it removes the "custom element" you were adding, and uses instead a property in the main VM that is itself an object):
export class Main{
constructor(){
this.heading = "hi!"
this.fooVar = new Foo("jhonny");
}
}
<template>
<section>
<h2>${heading}</h2>
<h2>my name is ${fooVar.name}</h2>
</section>
</template>
export class Foo{
constructor(name){
this.name = name;
}
}
I created a sandbox code project to illustrate the above (it is written in TypeScript)
If you want to try out the sandbox code project, here are the instructions
I added an Aurelia TypeScript Wizard Navigation Sample to show an alternative to the approach you asked about.
Related
Is there anyway I can get a reference to all the viewmodels of the child components in my lanes property on the BoardComponent viewmodel? I need to have a reference of al <boardlane></boardlane> in the viewmodel of the <board></board> component. Is this possible?
App.ts
export class App {
public groups: any[] = [
{
title: 'first group'
},
{
title: 'second group'
}
]
}
<template>
<board>
<boardlane repeat.for="group of groups" title.bind="group.title"></boardlane>
</board>
</template>
Board Component
#customElement('board')
export class BoardComponent {
public lanes: BoardLaneComponent[];
}
<template>
<div class="board">
<slot></slot>
</div>
</template>
BoardLane Component
#customElement('boardlane')
export class BoardLaneComponent { }
<template>
<div class="boardlane">
I am a board lane
</div>
</template>
You can try the #children decorator:
Board Component
import {children} from 'aurelia-framework';
#customElement('board')
#children({ name: 'lanes', selector: 'boardlane' })
export class BoardComponent {
public lanes: BoardLaneComponent[];
lanesChanged() {
// Handle any mutations to the array here
}
}
<template>
<div class="board">
<slot></slot>
</div>
</template>
In theory this should maintain a list of child VMs on BoardComponent by using the selector to gather the elements within the view.
If the elements are non-aurelia backed elements they will be represented by Element instances in the specified array, otherwise they will be a reference to the actual Aurelia VM backing the element.
Also, it will by default create a function called <x>Changed where <x> is the name of the backing array. You can use this to be notified of any mutations happening to the tracked elements.
The only issue may be nesting - I believe the original implementation deep-selected into descendants but that was removed later. I'm not sure if it was re-introduced but the details are here:
https://github.com/aurelia/templating/issues/451
Assuming you don't need to go to grandchildren this should work.
Disclaimer: not done any Aurelia dev for a little while :(
Note: I don't think the docs clearly list the API for children and the selectorOrConfig parameter it takes
In the source it looks like this:
constructor(config) {
this.name = config.name;
this.changeHandler = config.changeHandler || this.name + 'Changed';
this.selector = config.selector;
this.all = config.all;
}
So it looks like the object can have those properties - not sure what all does though but interesting that you can change the name of the change handler that's fired when the array contents mutate.
I want to call a child component's function from its parent. I have a way to do it, but I want to know if I'm missing a better way.
From Ashley Grant's blog post about accessing a custom element's viewModel from a custom attribute, I see that Aurelia adds au to the element and you can access the viewModel through that. So, if I add a nested component with a ref, like this:
<template>
<nested-element ref="childElement"></nested-element>
</template>
I can call a function on it like this:
this.childElement.au.controller.viewModel.someFunction();
This feels roundabout. I was hoping I would be able to access a nested element's viewModel through the parameters to a hook that the parent implements, such as created(owningView, myView) but I can't find a path to it.
Have I missed a better way?
Edit: I forgot to add that I need a return value from the function I'm calling, so having access to the viewmodel itself is preferable
ref gives you the element. view-model.ref gives you the element's view model.
<template>
<nested-element view-model.ref="childViewModel"></nested-element>
</template>
Call it like this in the parent view-model:
this.childViewModel.someFunction();
If you only have one instance of the nested-element or don't care if multiple nested-elements respond to the event. Then you could use standard Javascript event functionality for this:
bar.html
<template>
<h1>${value}</h1>
<input type="text" value.bind="value"></input>
<foo>text</foo>
</template>
bar.ts
import {bindable} from 'aurelia-framework';
export class Bar {
#bindable value;
public valueChanged(newValue, oldValue) {
var event = new CustomEvent("some-event-name", { "detail": { message: "Hello from Bar", oldValue, newValue } });
document.dispatchEvent(event);
}
}
foo.html
<template>
<h1>${value}</h1>
</template>
foo.ts
import {bindable} from 'aurelia-framework';
export class Foo {
constructor() {
document.addEventListener("some-event-name", (e) => {
console.log('hello here is Foo, recieved event from Bar : ', e);
}, true);
}
}
Almost give up on Aurelia, I'm struggling with adding custom elements dynamic in Aurelia,
lets say we have a custom tag:
view my-element.html:
<template> My Element ${name} </template>
viewmodel: my-element.js:
export class MyElement {
#bindable name = '';
}
so I try to manually add this tag, in another view:
<template>
<button type="button" click.delegate="createMyElement()">Remove</button>
</template>
another viewmodel:
export class App {
createMyElement() {
//how to do it in here to create element
//<my-element name='name1'></my-element>
}
}
I looked this link https://gist.run/?id=762c00133d5d5be624f9, but it needs a container reference
<div ref="container"></div>
I dont want to specify a container, instead I want it to be append to current view.
I also tried using aurelia-compiler from https://github.com/gooy/aurelia-compiler, when I try to import it, it was able to locate file'gooy/aurelia-compiler', but I got this error:
Error invoking Compiler. Are you trying to inject/register something that doesn't exist with DI?
Can someone please help? thanks.
You could inject the view's html element and use it as a "container". Like this:
import {inject} from 'aurelia-framework';
import {ViewFactory} from './view-factory';
#inject(Element, ViewFactory)
export class App {
//...
constructor(element, viewFactory) {
this.element = element;
this.viewFactory = viewFactory
}
}
Then, use this.element in the insert method:
this.dispose = this.viewFactory.insert(this.element, this.viewHtml, viewModel);
Running example:
https://gist.run/?id=9d5e7a60cd02e55618829a304df00621
Hope this helps!
Rather than trying to manually inject views through your viewModel (controller), try creating new viewModels from which to generate views. So, like this:
home.html
<template>
<my-element repeat.for="name of names" name.bind="name"></my-element>
<button click.delegate="addName()">Create My Element</button>
</template>
home.js
export class HomeViewModel {
constructor() {
this.names = []
}
addName() {
this.names.push('Jim');
}
}
I am totally new to this framework. Going through all Docs I have successfully configure Aurelia framework using visual studio and type script.
I want to know how to compose a view inside another view and and initialize its view model from its parent.
For example:
Here in navigation skeleton we have one view as welcome displaying first name and second name with submit button.
Now I created one Route name it as MyApp inside this I want to compose welcome view and i want to pass first name and second name to its view model.
Please let me know how to do that?
This is how my Html MyApp looks like:
<template>
<import from='welcome'></import>
<section class="au-animate">
<compose view-model="welcome"></compose>
</section>
</template>
This is How View Model is :
import {inject} from 'aurelia-framework';
import refWelcome = require("welcome");
#inject(refWelcome)
export class myApp {
vm;
title: string;
constructor(refWelcome) {
this.title = "My App Demo";
this.vm = refWelcome;
console.log(this.vm);
}
}
This is view model for welcome view:
import {computedFrom} from 'aurelia-framework';
export class Welcome{
heading = 'Welcome to the Aurelia Navigation App!';
firstName = 'John';
lastName = 'Doe';
previousValue = this.fullName;
constructor(fname: string, lname: string) {
if (fname != null || lname != null) {
this.firstName = fname;
this.lastName = lname;
}
}
}
I think you have a few issues in your code -
You need to fix the syntax for your myApp's title property.
Conventionally you should name your classes PascalCase.
You can't use a standard HTML import at the moment, you need to use require if you are requiring a custom element
To compose the view you can use two methods -
Compose custom element (require not required)
<template>
<section class="au-animate">
<compose view-model="./welcome"></compose>
</section>
</template>
No idea why that won't display without the backticks
As a custom element
<template>
<require from='welcome'></require>
<section class="au-animate">
<welcome></welcome>
</section>
</template>
I've created a plunkr to illustrate the problem I'm having.
I'm in the middle of creating a dashboard which at the moment contains four different items. Each of these items I'm creating as Custom Elements then wrapping them in a Custom Element called Widget to give them a frame, title and styling. Here's what that snippet looks like:
<widget title="A Widget" icon="fa-question">
<template replace-part="item-template">
<child-element text.bind="$parent.$parent.someText"></child-element>
</template>
</widget>
For reference the widget view looks like this:
<template>
<require from="./widget.css!"></require>
<div class="widget">
<div class="widget-header">
<i class="fa ${icon}"></i>
<h3>${title}</h3>
</div>
<div class="widget-content">
<template replaceable part="item-template"></template>
</div>
</div>
</template>
and the view model is:
import {bindable} from "aurelia-framework";
export class WidgetCustomElement {
#bindable title;
#bindable icon;
#bindable show; // This is something I want the child element
// to be able to bind to and control but haven't
// got there yet!!
}
But notice I'm trying to bind data from the ViewModel into the child-element where the child-element looks like this:
import {bindable} from "aurelia-framework";
export class ChildElementCustomElement {
#bindable text;
}
and the view:
<template>
<p>The widget passed us : ${text}</p>
</template>
The problem is no matter what expression I use (and here I'm currently trying $parent.$parent.someText) I can't get the binding to work.
Should this work? I've also tried defining the variable someText in the main ViewModel as `#bindable someText' but that throws the following exception:
Unhandled promise rejection TypeError: Cannot read property 'some-text' of null
at BindableProperty.initialize (https://cdn.rawgit.com/jdanyow/aurelia-plunker/v0.4.0/jspm_packages/github/aurelia/templating#0.14.4/aurelia-templating.js:2448:33)
at new BehaviorInstance (https://cdn.rawgit.com/jdanyow/aurelia-plunker/v0.4.0/jspm_packages/github/aurelia/templating#0.14.4/aurelia-templating.js:2199:23)
at HtmlBehaviorResource.create (https://cdn.rawgit.com/jdanyow/aurelia-plunker/v0.4.0/jspm_packages/github/aurelia/templating#0.14.4/aurelia-templating.js:2821:30)
at https://cdn.rawgit.com/jdanyow/aurelia-plunker/v0.4.0/jspm_packages/github/aurelia/templating#0.14.4/aurelia-templating.js:3385:27
at f (https://cdn.rawgit.com/jdanyow/aurelia-plunker/v0.4.0/jspm_packages/npm/core-js#0.9.18/client/shim.min.js:1415:56)
at https://cdn.rawgit.com/jdanyow/aurelia-plunker/v0.4.0/jspm_packages/npm/core-js#0.9.18/client/shim.min.js:1423:13
at b.exports (https://cdn.rawgit.com/jdanyow/aurelia-plunker/v0.4.0/jspm_packages/npm/core-js#0.9.18/client/shim.min.js:453:24)
at b.(anonymous function) (https://cdn.rawgit.com/jdanyow/aurelia-plunker/v0.4.0/jspm_packages/npm/core-js#0.9.18/client/shim.min.js:1625:11)
at Number.f (https://cdn.rawgit.com/jdanyow/aurelia-plunker/v0.4.0/jspm_packages/npm/core-js#0.9.18/client/shim.min.js:1596:24)
at q (https://cdn.rawgit.com/jdanyow/aurelia-plunker/v0.4.0/jspm_packages/npm/core-js#0.9.18/client/shim.min.js:1600:11)
I am not sure whether you are looking for this or not, however, introducing a #bindable text property in WidgetCustomElement makes this a whole lot easier. I worked on your plunk and introduced a #bindable wtext; in WidgetCustomElement, used this property to bind someText in app view like below:
<widget title="A Widget" icon="fa-question" wtext.bind="someText">
<template replace-part="item-template">
<child-element text.bind="wtext"></child-element>
<!--<child-element text.bind="$parent.$parent.someText"></child-element>-->
</template>
</widget>
And this works. However as I said I am not sure whether this approach works for you or not. What I have assumed is your widget will mostly contain one single child element and if this assumption is correct then this should work, however in case one-to-many mapping (many child element in single widget), something else is needed.
Hope this helps.
EDIT: I forked your plunk to make the changes and here is the link to that.
I have since stumbled across something in the Aurelia document detailing Template Parts. In example.js a bind() method is defined which assigns the bindingContext to a local member this.$parent.
This means all I have to do is defined the following in the view model for my widget:
bind(bindingContext) {
this.$parent = bindingContext;
}
Then the child-element is bound with the following:
<child-element text.bind="$parent.someText"></child-element>
I've forked my original plunkr to demonstrate it working.
Given repeat-for provides it's own $parent I had mistakenly believed there was one automatically defined here!!