Process custom elements specified in returned json - aurelia

I have a custom Aurelia that can be invoked like
Test
This works fine using in a html page in the site.
However, I have some restful services that return html with these custom
elements. I then use this html as content in my Aurelia site. At this point the custom elements won't execute. This there a way to tell Aurelia to process the custom elements in the returned html before display ?
Thanks

You could use a combination of <compose> and InlineViewStrategy.
Gist demo: https://gist.run/?id=bd1122986ba8dabde8111c3b4ab6df6f
The main idea here is to have a custom component, which is able to use the html extracted from returned JSON result. With InlineViewStrategy, it's possible to get a full-featured template parser on-the-fly and to feed <compose> with it.
This way that chunk of html will be treated as any other part of the application. It can contain other custom elements as well.
Custom Element
Basically, it's just an extended <compose>, but for view bindable property it accepts a string variable instead of a view path.
part.html
<template>
<compose view.bind="viewStrategy" model.bind="model"></compose>
</template>
part.js
import {inject, bindable, InlineViewStrategy} from 'aurelia-framework';
import {Api} from './api'; // REST of your magic goes here
#inject(Api)
export class Part {
// template string
#bindable view;
// data model
#bindable model;
// rest api url (optional)
#bindable templateHref;
constructor(api) {
this.api = api;
}
// render html template
viewChanged() {
if (this.view) {
this.viewStrategy = new InlineViewStrategy(this.view);
}
}
// load html from remote url (optional feature)
templateHrefChanged() {
if (this.templateHref) {
this.api
.loadTemplate(this.templateHref)
.then(result => {
this.view = result.content;
});
}
}
}
Usage
remote api processing
// api call goes here
// JSON result could be similar to this
result = {
content:
`<template>
<require from="./custom-part"></require>
<h2>${model.title}</h2>
<custom-part title.bind="model.title"></custom-part>
<p>${model.content}</p>
</template>`,
error: false
};
this.testView = result.content;
app.html
Load template string from a variable
<part view.bind="testView" model.bind="testModel"></part>
Load template from an url
<part template-href="http://remote-api-url" model.bind="{title: 'Another...', content: '...'}"></part>
Load template from a non-existing url
<part template-href="./remote-api-notfound" model.bind="testModel"></part>
I hope it helps.

Related

Adding a svelte component to DOM at runtime?

Adding a svelte component (Button) statically in the body section works. Adding the Button via appendChild does not?
Details:
Imagine a database table. For each row I add a line into my HTML body.
How could I add a svelte component (Button.svelte) to each row, too?
The problem: Standard HTML gets appended, but my svelte Button does not. (Probably because svelte needs to render at compile time.)
For example in +page.svelte:
const e = document.getElementById('my_div_container');
if(e)
{
const p = document.createElement("p");
const txt = document.createTextNode("test node");
p.appendChild(txt);
e.appendChild(p); // <-- ok, gets displayed
const b = document.createElement("Button");
e.appendChild(b); // <-- NOT displayed
}
Example lib/Button.svelte:
<script>
function on_click()
{
console.log('clicked');
}
</script>
<button on:click={() => on_click()}>Click</button>
FYI: Statically adding a button to the HTML body works of course:
<p>Some text</p>
<Button />
You can either build components as custom elements, then they can be created with createElement or just use the client component API. You just need to import the component and construct it, setting the target to the element you want to add it to:
new Button({ target: e })

Unable to add elements using the setAttribute

I am using the VUE JS code and trying to add the setAttribute to some of the tags.
Here is the code I am using :
changetab() {
const demoClasses = document.querySelectorAll(".delCon__select");
demoClasses.forEach(button => {
button.setAttribute("tabindex", "0");
});
return true;
},
but when I view in the code inspector, It does not show added to it, I have added the above function in computed.
template is like this :
<template>
<el-container class="orders"></el-download>
</template>
You need to make this type of request in Vue's Lifecycles, like: created or mounted.
Something like:
mounted() {
this.changetab()
}
Computed would not be the most appropriate place for this type of action.

vue.js : click a button, look through dom, find all classes with a specific class name and append a new class to that existing class list

So, i'm using Axios to pull an html file, and then take everything in that html file past the <body> tag and appending it to {{htmlData}} inside of a div in my template
looks like this:
<template>
<somebutton> I click on</somebutton>
<div id="some-container-name" v-html="htmlData">
{{ htmlData }}
</div>
</template>
data: function() {
return {
htmlData:''
};
},
methods: {
pullView: function(html) {
this.axios.get('http://someUrl.html').then(response => {
let corsHTML = response.data;
let htmlDoc = (new DOMParser()).parseFromString(corsHTML, "text/html");
this.htmlData = htmlDoc.documentElement.getElementsByTagName('body')[0].innerHTML;
})
},
The user has an option to click on a button - the code then searches through the dom and then appends a classname to every existing you-can-edit-me class name from the html document that is pulled in via axios.
Does this make sense?
Because of how I'm pulling this content in, I don't really have the chance to bind anything to this content using Vue's :bind directive. My Google-fu has failed me and need some suggestions. Is it possible to edit this.htmlData and make the transformation in that object and then i guess re-render the data??
I don't want to pollute my work with jQuery and wondering if anyone else has done something like this?
Since you already have a parsed htmlDoc:
for (const element of htmlDoc.querySelectorAll('.you-can-edit-me')) {
element.classList.add('additional-css-class');
}
inside your pullView method, before assigning it to this.htmlData.

Failed to access innerhtml elements in angular5

I am trying to access innerhtml elements in my component using it's id attribute.It works if I add the element directly into my template,but when I added the element through html binding I failed to access it in component.
html
<div [innerHtml]="testVal | safingHtml"></div>
component
testVal='<a id="up1">hello</a>';
ngAfterViewInit() {
const elem=document.querySelector('#up1');
console.log(elem);
}
In the above code I am trying to access the html elelment after completing the initialization process.The code works if I added the elelment directly into the template otherwise it returns null.
I am also using custom pipe having 'DomSanitizer' to get the html attributes(id,style).
custom pipe
import {DomSanitizer} from '#angular/platform-browser';
import {PipeTransform, Pipe} from "#angular/core";
#Pipe({ name: 'safingHtml'})
export class SafingHtmlPipe implements PipeTransform {
constructor(private sanitized: DomSanitizer) {}
transform(value) {
return this.sanitized.bypassSecurityTrustHtml(value);
}
}
is there anyway to access the innerhtml elements by using it's id apart from using 'viewchild' property in angular5?

Call a custom attribute method from the view model

I have a custom attribute with a method to show and hide some HTML content, I've attached the attribute to an element in a view model.
How can I call a method defined in the custom attribute from the view model?
To access the custom attribute's view-model, just put the custom attribute on the element a second time, but this time put .ref="viewModelPropertyName" on to the attribute. Then, in the parent view-model, you can access methods on the attribute using viewModelPropertyName (or whatever name you gave it). You can see an example of this here: https://gist.run/?id=9819e9bf73f6bb43b07af355c5e166ad
app.html
<template>
<require from="./has-method"></require>
<div has-method="hello" has-method.ref="theAttribute"></div>
<button click.trigger="callMethod()">Call method</button>
</template>
app.js
export class App {
callMethod() {
const result = this.theAttribute.someMethod('blah');
}
}
has-method.js
export class HasMethodCustomAttribute {
someMethod(foo) {
console.log('someMethod called with foo = ' + foo + ', this.value = ' + this.value);
return `Hello ${foo}`;
}
}
There are some ways to do it, but I believe the ideal would be binding a property from your custom-attribute to your view-model. For example:
MyCustomAttribute {
#bindable showOrHide; //use this to show or hide your element
}
MyViewModel {
visible = false;
}
Usage:
<div my-custom-attribute="showOrHide.bind: visible"></div>
So, whenever you change visible you will also change showOrHide.
Nevertheless, is good to remember that Aurelia already has a show and if custom-attributes:
<div show.bind="visible" my-custom-attribute></div>
<div if.bind="visible" my-custom-attribute></div>
Make sure if you really need to create this behaviour in your custom-attribute.
This can be done without the need for a ref. Here is an example that shows how.
It calls a showNotification method on the custom attribute from the custom element using the custom attribute.
In the custom attribute:
#bindable({ defaultBindingMode: bindingMode.twoWay }) showNotificationCallback: ()=> void;
bind() {
this.showNotificationCallback = this.showNotification.bind(this);
}
showNotification() {
// Your code here
}
In the custom element view (Note the absence of parens in the value of this binding):
<div notification="show-notification-callback.bind: showSaveSuccessNotification;></div>
In the custom element view-model:
// Show the save success view to the user.
if (typeof this.showSaveSuccessNotification=== 'function') {
this.showSaveSuccessNotification();
}