Aurelia: Update the custom element on changes to the bound object - aurelia

I have a custom element called summary-bar with summary property:
export class SummaryBarCustomElement {
#bindable summary;
---
In another component test-website, I uses the summary-bar element and bind its data as below:
<summary-bar summary.bind="testWebsiteSummary"></summary-bar>
And here testWebsiteSummary is defined in the test-website.js ViewModel:
export class TestWebsiteCustomElement {
testWebsiteSummary = {
passed_result_count: 0,
failed_result_count: 0,
incomplete_result_count: 0,
unknown_result_count: 0
}
---
There are several functions in TestWebsiteCustomElement class that modify the values of testWebsiteSummary.passed_result_count, testWebsiteSummary.failed_result_count, testWebsiteSummary.incomplete_result_count and testWebsiteSummary.unknown_result_count. However, the summary-bar element is not reloaded with the new values of testWebsiteSummary. Is there a way to achieve that? What I mean is every time the properties of testWebsiteSummary is updated, is it possible to update the summary-bar with the new values? Thank you.
Example of a function which changes the properties:
changeWebsiteSummary(status) {
switch (status) {
case "SUCCESS":
this.testWebsiteSummary.passed_result_count++;
this.testWebsiteSummary.incomplete_result_count--;
break;
case "INCOMPLETE":
this.testWebsiteSummary.incomplete_result_count++;
this.testWebsiteSummary.passed_result_count--;
break;
default:
}
}

When you bind an object into your Custom Element it will update its values automatically. Whenever your TestWebsiteCustomElement changes any of the properties in testWebsiteSummary, those changes will be automatically reflected in your SummaryBarCustomElement. That is, if you are for example displaying testWebsiteSummary.passed_result_count in the SummaryBarCustomElement view, then it will be automatically updated in the ui.
Now, if what you want is to know when those changes occur to do something else, then you need to use a propertyObserver.
Aurelia by default support adding methods such as summaryChanged(newValue, oldValue) to custom elements. This works just fine for primitive values, but for Objects (or arrays) this method will not be triggered if any of the internal properties changes, only if the object itself has been reassigned.
To work around this you can use the binding engine to observe specific properties inside your summary object. Here is what it would look like:
import {bindable, BindingEngine, inject} from 'aurelia-framework';
#inject(BindingEngine)
export class SummaryBarCustomElement {
#bindable summary;
constructor(bindingEngine){
this.bindingEngine = bindingEngine;
}
bind(){
this.subscription = this.bindingEngine.propertyObserver(this.summary, 'passed_result_count')
.subscribe(newValue, oldValue => this.passedResultCountChanged(newValue, oldValue))
}
detached(){
this.subscription.dispose();
}
passedResultCountChanged(newValue, oldValue){
//Do something
}
}

You can use the signal binding behaviour
<summary-bar summary.bind="testWebsiteSummary & signal:'your-signal'"></summary-bar>
And the class:
import {BindingSignaler} from 'aurelia-templating-resources';
export class TestWebsiteCustomElement {
constructor(signaler: BindingSignaler) {
this.signaler = signaler;
}
functionThatChangesValues(){
this.signaler.signal('your-signal');
}
}

Related

Vue: replacing data object entierly?

I know that if I want to add a property to a data object, I use Vue.set(). I did this in my created() lifecycle method ... a new field will be added to the myObject. But what about if I wanted to make an api call and completely replace the existing data object of myObject? Can I do what I am doing in updateMore() method or is there another way to handle this?
data() {
return {
myObject: {}
}
},
async created() {
let apiData = await axios.get('http://myurl.com');
this.$set(this.myObject, 'name', apiData.name);
},
methods: {
updateMore() {
let moreAPIData = await axios.get('http://myurl.com');
// will this completely override `myObject` with `moreAPIData` and still be reactive?
this.myObject = moreAPIData;
}
}
TL;DR:
This assignment of a new object is fine. Vue reactivity issues occurs when you add a new key to an object, because the object is the same, what changed was its content. Javascript compares objects by its address, so if I do
new MyClass() === new MyClass()
it returns false, because, even if they have the same content, they have different addresses.
To conclude, when you set a whole new object, Vue is able to track the difference, but when you change the content of one key, it can't.
Full boring text
You can read the whole documentation about reactivity in Vue here

Express custom GET/POST parameter binding nestjs / routing-controllers

I have a Controller (nestjs / routing-controllers) and I'm passing a GET request the following way: /collect?t=My-name
t is actually a full name which I can't change.
Bellow im injecting #QueryParams() data: CollectData, Im looking for a way (like java strong and .net) to tell the controller that fullName is actually t.
Something like
export interface CollectData{
#PropertyName('t')
fullName: string
}
I'm expecting fullName to represent the t variable.
#JsonController()
export class CollectController {
#Get('/collect')
collect(#QueryParams() data: CollectData){
return data.fullName;
}
}
You could use some sort of solution using the class-transformer library and the ValidationPipe given by Nest (it also does transformations!) and have your CollectionData class (use a class so that the data can be serialized after transpiling, interfaces go away in JavaScript) look like this:
// CollectData.ts
export class CollectData{
#Expose({ name: 'fullName' })
t: string
}
//Collect.controller.ts
#Controller() // Changed from JSONController to Controller
export class CollectController {
#Get('/collect')
collect(#Query(new ValidationPipe({ tranform: true }) data: CollectData){ //Changed from QueryParams to Query
return data.fullName;
}
}
OR in your main.ts add the app.useGlobalPipes(new ValidationPipe({ tranform: true }) to set the validation pipe to run against all your incoming requests

Import variables into aurelia-dialog view-model or view

Is there a way to import additional variables/data from the dialog-service to the controller?
For example I have an array of possible options in a form of my app-view. I fetch the data via an API from a server.
I'd like to edit an entry with an aurelia-dialog and don't want to fetch the data again to avoid unnecessary traffic in my app.
How can i pass the array additionally to the model. Pack it all together in an Object and unwrap it in the controller?
As far as I know the activate-method of the controller only takes one argument, doesn't it?
Thank you
Isn't the example in the repository exactly what you are looking for?
The person attribute is passed to the dialog service via the settings object (model: this.person). This may be data you fetched from the server. As you mentioned, you can of course add multiple objects to the model as well which will be available in the activate() method of your dialogs vm.
import {EditPerson} from './edit-person';
import {DialogService} from 'aurelia-dialog';
export class Welcome {
static inject = [DialogService];
constructor(dialogService) {
this.dialogService = dialogService;
}
person = { firstName: 'Wade', middleName: 'Owen', lastName: 'Watts' };
submit(){
this.dialogService.open({ viewModel: EditPerson, model: this.person}).then(response => {
if (!response.wasCancelled) {
console.log('good - ', response.output);
} else {
console.log('bad');
}
console.log(response.output);
});
}
}

Aurelia property-observation warning when using #children

I'm getting the following warning when using the #children decorator:
vendor-bundle.js:14294 WARN [property-observation] Cannot observe property 'columns' of object
My custom element code is:
#children('data-grid-column') columns = [];
I'm trying to bind it to this view-model so that I can get an array of objects with the column data:
import {bindable, noView} from 'aurelia-templating';
#noView
export class DataGridColumn {
#bindable name;
#bindable display;
#bindable align;
}
It works perfectly, but the error seems to indicate something is wrong. I have no need for property-observation here, but would like to know why I'm getting the error.
<data-grid data.bind="records">
<data-grid-column name="acc_code" display="Code"></data-grid-column>
<data-grid-column name="acc_name_orig" display="Account"></data-grid-column>
</data-grid>
It seems to be a known issue that has been already fixed. I think this warning will be gone in the next aurelia-templating release. See https://github.com/aurelia/templating/issues/520
Right now, it doesn't happen if you use #children at the class level.
#children({ name: "columns", selector: "column" })
export class DataGridColumn {
//...
}

Aurelia validation errors not displayed when validation initialized on attached

I want to show invalid input fields when the view is shown.
I have validation rules setup in a separate class (UserValidation) with one function (initValidatorOn).
export class UserValidation {
public _validation: Validation;
constructor(validation: Validation) {
this._validation = validation;
}
initValidatorOn(user: UserDto): ValidationGroup {
return this._validation.on(user, null)
.ensure('name').isNotEmpty();
}
}
Everything works for this setup.
export class User {
public validationGroup : ValidationGroup;
public userValidation : UserValidation;
public user : UserDto;
constructor(uv: UserValidation) {
this.userValidation = uv;
//this code works here and in activate method
this.validationGroup = this.userValidation.initValidatorOn(this.user);
}
attached() {
this.validationGroup.validate();
}
}
As I said before, the above works. But sometimes the object that I need to validate I not available in constructor or in activate function so I need to initialize validation in attached function. When I do that, my view no longer shows validation errors until I update the value in input.
So, this doesn't work
attached() {
this.validationGroup = this.userValidation.initValidatorOn(this.user);
//no validation errors displayed on the view
this.validationGroup.validate();
}
Can someone explain why errors aren't displayed on the view after I call validate when I initialize validationGroup in attached function?
Thank you.