How to get parent object in with.bind attribute of Aurelia Js - html

I have constructor of ViewModel below:
export class ViewModel{
data_1 = {id: 1, name: 'John'};
currency = {id: 1, name: 'USA'};
}
And in my html, I bind data by "with.bind":
<div with.bind="data_1">
<span textcontent.one-time="id"></span>
<span textcontent.one-time="name"></span>
<div with.bind="currency">
<span text.content="name"></span>
</div>
</div>
I have problem when I want to bind my currency in data_1, it can't not recognize currency . How can I get currency?

Why not just simplify and get rid of with.bind?
<div>
<span textcontent.one-time="data_1.id"></span>
<span textcontent.one-time="data_1.name"></span>
<div>
<span textcontent.one-time="currency.name"></span>
</div>
</div>

The thing is that with attribute doesn't provide a reference to $parent context. I'm not sure this is the best way to handle it but you can create your own custom attribute that would have $parent.
Based on the original with:
import {inject} from 'aurelia-dependency-injection';
import {BoundViewFactory, ViewSlot, customAttribute, bindable, templateController} from 'aurelia-templating';
#customAttribute('with-parent')
#templateController
#inject(BoundViewFactory, ViewSlot)
export class WithParent {
constructor(viewFactory, viewSlot) {
this.viewFactory = viewFactory;
this.viewSlot = viewSlot;
}
bind(executionContext) {
this.executionContext = executionContext;
this.valueChanged(this.value);
}
valueChanged(newValue) {
if (!this.view) {
newValue.$parent = this.executionContext;
this.view = this.viewFactory.create(newValue);
this.viewSlot.add(this.view);
}
else {
this.view.bind(newValue);
}
}
}
and then usage is transparent:
<div with-parent.bind="data_1">
<span textcontent.one-time="id"></span>
<span textcontent.one-time="name"></span>
<div with-parent.bind="$parent.currency">
<span textcontent.one-time="name"></span>
<div with-parent.bind="$parent.$parent.another">
<span textcontent.one-time="name"></span>
</div>
</div>
</div>

Using $parent alongside with.bind now works as of aurelia-framework#1.1.5
For example, the following will add a view model reference (bar) to the parent rather than to foo
<element with.bind="foo">
<child-element view-model.ref="$parent.bar"></child-element>
</element>

$parent gives you a reference to your view model so $parent.currency

Related

Angular NgFor Path Issue

In my Angular Application I have a simple ngFor loop showing logo images like this:
<div *ngFor="let item of list" class="logo-wrapper">
<div class="customer-logo">
<span
class="my-icon"
aria-label="My icon"
[inlineSVG]="'./assets/image/projects/logo/' + item.logo">
</span>
</div>
</div>
This is working fine!
But: If I try to slice the Array to limit the output as follow:
<div *ngFor="let item of list | slice: 0:10; let i = index" class="logo-wrapper">
<div class="customer-logo">
<span
class="my-icon"
aria-label="My icon"
[inlineSVG]="'./assets/image/projects/logo/' + item.logo">
</span>
</div>
</div>
I get this Error : "Object is of type 'unknown'".
Error output:
I really don't know what I'm doing wrong here. I hope someone can point me in the right direction.
Edit: The problem appears as soon as I add a index to the loop.
I tried to add the index to the object like: item.i.logo but its also unknown.
PS: Here is my .ts-file
#Component({
selector: 'app-logo-section',
templateUrl: './logo-section.component.html',
styleUrls: ['./logo-section.component.scss']
})
export class LogoSectionComponent implements OnInit {
list : any
constructor()
{
this.list = getProjects()
console.log(this.list)
}
ngOnInit(): void
{
}
private services = [{
slug : "s-l-u-g",
name : "name",
work : "work",
company : "company",
website : "https://www.google.com",
preview : "text",
logo : "logo.svg"
}]
getProjects()
{
return services
}
}
You would have to change the type of list to any[] instead of any. Update the declaration as follows in your typescript file.
list : any[];
It seems like the SlicePipe deprecates with the ng-inline-svg package because it uses HttpClientModule and works asynchronously.
if you use Array.slice method instead of the SlicePipe in the *ngFor it works fine.
Please find the Stackblitz example.
<div *ngFor="let item of list.slice(0, 10); let i = index" class="logo-wrapper">
<div class="customer-logo">
<span class="my-icon" aria-label="My icon" [inlineSVG]="item.logo"> </span>
</div>
</div>

Convert array of links into links - React

Im new to React. Im trying to convert an array contains links into something that will show the links in order in the website.
something like this:
external_references = ["https://google.com", "https://en.wikipedia.org/wiki/Wiki"]
into something to show up in the server like this:
external_references:
https://google.com(link)
https://en.wikipedia.org/wiki/Wiki(link)
I tried to do the following code I found but it failed to work:
<span className="externalRefs">External_references: <br></br> {external_references.forEach(link => {
return new DOMParser().parseFromString(link, "text/xml");
})}</span>
you can try the map function:
<p>external_references:</p>
{external_references.map(link=>{
return <div key={link}>
<a href={link}>{link}</a>
</div>
}
}
I recomend you as a good practice using the link as a key, since each of them should be different.
just
{external_references.map((reference, i) => {
return(
<div key ={i}>
<a href ={reference}>{reference}</a>
</div>
);
})}
You can use the Array.map function.
<span className="externalRefs">External_references: <br></br>
{
external_references.map(link, i) => {
<div key ={i}>
<a href ={link}>{link}</a>
</div>
}
}
</span>

File input with ng-model in nested components throws InvalidStateError

I have a ngform which includes a separate component to upload files. When I try to input a file in this component, the browser throws this error:
I don't understand where this might come from, here is my parent html:
<form
novalidate
#logosForm="ngForm"
(ngSubmit)="brandingService.setLogos(logosForm.value)">
<div class="columns">
<div class="column">
<app-file-upload
title="Logo principal"
name="logo"
label="Logo.png">
</app-file-upload>
</div>
</div>
Here is my child nested html (app-file-upload):
<div class="upload">
<span class="upload__label" [translate]="title"></span>
<div class="file is-fullwidth">
<label class="file-label">
<input
class="file-input"
type="file"
accept=".png, .jpg, .ico"
[name]="name"
(change)='handleFileInput($event)'
[(ngModel)]="file">
<span class="file-cta">
<span class="file-icon">
<i class="fas fa-upload"></i>
</span>
</span>
<span class="file-name">
{{label}}
</span>
</label>
</div>
<figure *ngIf="file" class="image previsualisation" [ngClass]="{'is-128x128': name == 'logo', 'is-48x48': name == 'favicon'}">
<img [src]="file">
</figure>
</div>
And here's the child's ts:
export class FileUploadComponent {
file: string | ArrayBuffer;
#Input()
title: string;
#Input()
name: string;
#Input()
label: string;
constructor() { }
handleFileInput(event: Event): void {
const userFile: File = (<HTMLInputElement> event.target).files[0];
if (userFile) {
this.label = userFile.name;
const reader: FileReader = new FileReader();
reader.onload = ((e: Event): void => {
const filereader: FileReader = <FileReader> e.target;
this.file = filereader.result;
});
reader.readAsDataURL((<HTMLInputElement> event.target).files[0]);
}
}
}
As I understand the error this might come from the fact that I try to bind on a file object (or string | ArrayBuffer) and so I try to change the value of this object and that is forbidden. I don't see how I could use the ngModel differently to get the child component to output the file uploaded by the user. If you have an idea, please help me, thanks !
While I don't immediately see an error in your code, file input fields in combination with NgModel show some very strange behaviours.
Ben Nadel recently wrote an article about how to properly access the file inputs value attribute using a ControlValueAccessor, perhaps you can adopt his method instead.
I suggest you to follow this link. use this way to upload a file by avoid duplicate files, maximum size.
/* Add application styles & imports to this file! */
#import url('https://unpkg.com/bootstrap#3.3.7/dist/css/bootstrap.min.css')
<div>
<label class="btn btn-primary">
Upload Documents <input type="file" #fileUpload (change)="fileChangeEvent($event)" onclick="this.value=null" multiple hidden style="display:none;">
</label>
</div>
<ul>
<li *ngFor="let fileName of selectedFileNames">{{fileName}} <button (click)="removeFile (fileName)" style="cursor: pointer;"><i class="fa fa-times"></i></button>
</li>
</ul>
//Typescript Code:
For Typescript code refer to
Working Demo

Select a node with its children based on its class, and turn it into an object

I want to find out how to scrape website data. This is a part of the html that I am interested in. I am using cheerio for finding the data I need.
<td class="col-item-shopdetail">
<div class="shoprate2 text-right hidden-xs">
<div class="currbox-amount">
<span class="item-searchvalue-curr">SGD</span>
<span class="item-searchvalue-rate text-black">42.0000</span>
</div>
<div class="item-inverserate">TWD 100 = SGD 4.2</div>
<div class="rateinfo">
<span class="item-timeframe">12 hours ago</span>
</div>
</div>
<div class="shopdetail text-left">
<div class="item-shop">Al-Aman Exchange</div>
<div class="item-shoplocation">
<span class="item-location1"><span class="icon icon-location3"></span>Bedok</span>
<span class="item-location2"><span class="icon iconfa-train"></span>Bedok </span>
</div>
</div>
</td>
I wish to make "col-item-shopdetail" class as an object and store all class with name "col-item-shopdetail" into an array for access.
So if possible, it will be access like array.item-inverserate or through cheerio selector like
$('.col-item.shopdetail').children[0].children[0].children[1]
I have tried looping through the names of shop and store in an array and use another loop after finish looping the names to find the rates. Then try and match the rates to the name by access same index of the array. However this did not work for unknown reason where each time the rate printed is of different value and index of the same name are different in each try.
This is close to what I want but it does not work:
how to filter cheerio objects in `each` with selector?
In other words, you want an array of objects representing elements having class .col-item-shopdetail and each of those objects should have a property corresponding to the .item-inverserate element they contain ?
You need the map method
my_array = $('.col-item-shopdetail').map(function(i, el) {
// Build an object having only one property being the .item-inverserate text content
return {
itemInverserate: $(el).find('.item-inverserate').text()
};
}).get();
// You can also directly target inverserate nodes
// which will exclude empty entries ('shopdetail' that have no 'inverserate')
// Loop over .item-inverserate elements found
// somewhere in a .col-item-shopdetail
// (beware, space matters)
my_array = $('.col-item-shopdetail .item-inverserate').map(function(i, el) {
// Build an object having only one property being the .item-inverserate text content
return {itemInverserate: $(el).text()};
// Note: If all you need is the inverserate value,
// Why not avoiding an intermediate full object?
// return $(el).text()
}).get();
Since Cheerio developers have built their API based on jQuery with most of the core methods, we can simply test snippets in the browser ...
my_array = $('.col-item-shopdetail').map(function(i, el) {
return {
itemInverserate: $(el).find('.item-inverserate').text()
};
}).get();
console.log(my_array[0].itemInverserate)
my_array_2 = $('.col-item-shopdetail .item-inverserate').map(function(i, el) {
// Build an object having only one property being the .item-inverserate text content
return {itemInverserate: $(el).text()};
}).get();
console.log(my_array_2[0].itemInverserate)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table><tr><td class="col-item-shopdetail">
<div class="shoprate2 text-right hidden-xs">
<div class="currbox-amount">
<span class="item-searchvalue-curr">SGD</span>
<span class="item-searchvalue-rate text-black">42.0000</span>
</div>
<div class="item-inverserate">TWD 100 = SGD 4.2</div>
<div class="rateinfo">
<span class="item-timeframe">12 hours ago</span>
</div>
</div>
<div class="shopdetail text-left">
<div class="item-shop">Al-Aman Exchange</div>
<div class="item-shoplocation">
<span class="item-location1"><span class="icon icon-location3"></span>Bedok</span>
<span class="item-location2"><span class="icon iconfa-train"></span>Bedok </span>
</div>
</div>
</td></tr>
</table>

Meteor #each block issues

I would like to iterate over a Mongo query for the user's collections using the each block.
I have the following html template that should load in different pieces of data from the profile object in the users collection.
To clarify, the users Collection has a services, status and profile object
{{#each profile}}
<div class="profileUser oneDiv">
<div class="profileUserLeft">
<div class="profileUserImage">
<div class="spin"> {{> spinner}} </div>
<img src="{{profile.picturelrg}}" class="profileUserImg">
</div>
<div class="profileUserGraph">
<label for="myChart"><b>Meetup Graph</b>
<br>
<span class="profileMonth"> {{profile.month}} </span>
<br>
<canvas id="myChart"></canvas>
</label>
</div>
</div>
<div class="profileUserRight">
<div class="profileUserName">
<ul>
<li><h1>{{profile.name}}</h1></li>
<li>
<div class="circle" style="background-color: {{online.color}}"></div>
</li>
</ul>
</div>
</div>
</div>
{{/each}}
Here is my helper that sets the query
profile: function() {
return Meteor.users.find({
_id: id
});
}
Currently the page loads in no data.
When I statically query for a property however it works. This is done like so.
profimg: function() {
return Meteor.users.find({
_id: id
}).fetch()[0].profile.picturelrg;
}
How can I be more efficient and use the each block instead of statically searching for each different property utilizing the fetch() method?
each of Blaze takes an array as parameter to loop while find method return a Cursor of MongoDB. What you need to do is fetch the Cursor to return the array
profile: function() {
return Meteor.users.find({
_id: id
}).fetch();
}
However, your logic is not correct. You are finding the profile that matches with the input id, thus the function should be
profile: function() {
return Meteor.users.findOne({
_id: id
});
}
and then you can access the property without the each loop