Setting an attribute dynamically in mithril.js - javascript

I'd like to set an attribute in mithril.js inside the oninit function. For example,
m('img', {
oninit: (vnode) => {
vnode.attrs.src = 'A url string';
}
})
I want to check for certain conditions before setting this attribute and I'd rather not use the ternary operator because that would get messy. The above example half worked, on logging vnode, the attrs object has the correct src string, however in the DOM, src is set to null for some reason.
I'm looking for a simple solution and not something like, give an id to the element and use it to change the attribute.

Usually such code could be placed in the component as a method or as a function when using a closure component.
This is in a component just for an image but such work could be done in a parent component that rendered that IMG directly.
function DynamicImg() {
function imageAttrs(vnode) {
let src = 'configurable';
return {src: src};
}
return {
view: function (vnode) {
return m('img', imageAttrs(vnode));
}
}
}
Setting an attr in init would not last as the view function would be called without it immediately. Since it is called at every render.

For anyone looking for a solution, this worked for me.
m('img', {
src: (() => {
return 'A url string';
})()
})
You can define the function elsewhere and call it as well.

Related

How to Programmatically render webcomponent as child of another component in VanillaJs?

I'm trying to learn vanilla webcomponents and I am stuck trying to do something simple and no combination of keywords I can think of return anything helpful in Google.
So in my render method, I see all sorts of examples that manually construct a tag and assign all the attribute, like so:
render()
{
this.innerHTML = `
${
this.data
.map(reason =>
`<${ReasonItem.elementName}
name="${reason.Name}"
description="${reason.Description}">
</${ReasonItem.elementName}>`)
.join('')
}
`;
}
This works, but it's extremely tedious for child controls that have LOTS of attributes.
I would like to do something like:
render()
{
this.innerHTML = `
${
this.data
.map(reason =>
{
let child = new ReasonItem();
child.reason = reason;
child.render();
return child.outerHTML;
})
.join('')
}
`;
This almost works, but apparently the constructor and other methods on an HTMLElement can be called out of order so I'm getting 'unidentified' in all my elements since the constructor calls render() and the setting of the property calls render(), but the constructors render is being called after the property is set, so I'm thinking I'm not doing this right.
Thank you for your time and attention.
since the constructor calls render()
That should never be done in the constructor. Leave that call to connectedCallback.
but the constructor's render is being called after the property is set
That's not possible unless you have an designed an async constructor.
Also, if you're not using Stencil or lit which do property binding and updating for you, you shouldn't try to mimic a render method to react to updates.
Instead, create your elements programmatically and have the component store references to those elements, so you can make targeted, partial updates later. Use getters and setters for the purpose.
Edit: Your solution code can be simplified:
render()
{
while(this.lastChild) this.lastChild.remove();
this.append(...this.data.map(reason => Object.assign(new ReasonItem, { reason })))
}
Also you probably need to empty your host element before rendering if you insist on a render() method.
#connexo So after lots of guess and testing, this appears to do what I need:
render()
{
this.data.forEach((reason) =>
{
const el = document.createElement(ReasonItem.elementName);
el.reason = reason;
this.appendChild(el);
});
}
I like the look of this better, not sure if it's equivalent or advisable, perhaps javascript pros can tell me what's 'generally preferred'?
render()
{
this.data.forEach((reason) =>
{
const el = new ReasonItem();
el.reason = reason;
this.appendChild(el);
});
}
Internally in my 'ReasonItem', the 'reason' property is observed and when changed, it loads (from the complex reason json object) all the dozens of attributes into their appropriate spots on the ReasonItem component.

access other properties of the same element by another property in Vue.js

Is there a way to access other properties of the same element by another property in Vue.js ?
example:
instead of writing:
<kpi title="some_kpi" v-if="displayed_kpi==='some_kpi'"></kpi>
do something like this:
<kpi title="some_kpi" v-if="displayed_kpi===title"></kpi>
which means that I want to access the value of the title inside the v-if of the same tag. (Obviously this doesn't work like this) Is there a way ?
You could have some_kpi be a variable. like this:
HTLM
<kpi :title="myKpi" v-if="displayed_kpi === myKpi"></kpi>
JS
export default {
data() {
return {
myKpi: 'some_kpi'
}
}
}
let me know if this works :)

Can I return null in vue computed to watch changes in data?

I would like to use vue's computed to detect changes in multiple data and execute the function automatically. But I just want to use computed as a function, I don't want any new values.
On the other hand, computed is not executed automatically unless it is used. So I decided to return null and use {{}} in the template tag to call that computed. Is this the right way to do it? I don't want to use watch as much as possible.
Also, can I write the process I want to execute after being rendered every time in my "afterRendering" function,which without returning anything, and force it to be called with {{afterRendering}}?
<template>
...
{{afterRendering()}} <!--this will be executed everytime after rendering.-->
{{MyComputed}} <!--this will be executed when any bindings that depend on changes.-->
</template>
...
export default {
methods: {
afterRendering: function () {
//...
},
},
computed: {
MyComputed: function () {
if (!!Achanged || !!Bchenged) {
//now I can watch if A and B have changed.
return null
}
},
},
}
#TatsuhikoMizuno instead of your afterRendering function just use the in-built mounted() function, or if you wanted something to run on every re-render you could use the updated() lifecycle hook. Docs found here, and Vue lifecycle diagram here.
And yes you can use your computed property in that way, it just can't have any side-effects like assigning a value to a data property for example.
This is not probably what the OP asked for but I consider this a valid alternative approach for the mentioned problem
export default {
watch: {
...['a', 'b', 'c', 'd', 'e'].reduce((obj, currentValue) => {
obj[currentValue] = {
handler: () => {
// Do something
},
immediate: true,
};
return obj;
}, {})
}
}
If you don't want code repetition you could use a reduce function over the names of the variables that hold your arrays, by simply using array destructuring you can assing this same function to all of your variables. Also this helps for code readability and makes it easier to understand if you actually handle changes in the actual watch handler.

Angular ngOnChanges in child doesn't trigger after subscribe manipulates data

I have a problem with ngOnChanges not being fired for a child component after data is being manipulated in parent via subscribe method.
Basically my main component looks like this:
public imageGroups: IImageGroup[];
public status$: Observable<ModelStatusesModel>;
public ngOnChanges(changes: SimpleChanges): void {
if (Boolean(this.treeData)) {
this.imageGroups = this.treeDataService.convertImageGroups(this.treeData);
this.status$ = this.updateStatus();
this.status$.subscribe((status: ModelStatusesModel) => {
this.treeDataService.updateStatus(this.imageGroups, status); // this function makes changes to this.imageGroups
console.log('subscribe happened');
});
}
}
HTML:
...
<ul class="treeview-nodes-wrapper">
<treeview-branch *ngFor="let group of (imageGroups)"
[group]="group"></treeview-branch>
</ul>
...
The branch has also ngOnChnages:
public ngOnChanges(): void {
this._currentNodeDisabled = this.group.isDisabled;
console.log(this.group); //returns isDisables as true
console.log(this.group.isDisabled); //returns false
console.log(this._currentNodeDisabled); //returns false
}
When I run my application this is the result in the console:
{ ... isDisabled: true ...},
false
false
subscribe happened
I was also trying to surround the call inside my subscription in a ngZone.run but without any success. Do you have any idea how to tell angular that ngOnChanges in the child triggered?
EDIT: What works for me is creating a property in the parent component (public change = false;) then toggling this property inside my subscribe method and giving it as an input to my children elements. That gets picked up as a change. Even though this solves my problem, it looks more like a very hacky way of writing the code.
This is a result of ngOnChanges being triggered when an input value changes, and with objects that are passed by reference the modification of their properties does not explicitly change the thing being passed down. The solution is often times to do a clone, ...spread operator, or reassign to the property that is being passed down in the parent's [input].
Your solution of having an additional input that changes to a new value to trigger ngOnChanges works as well, it's a bit of a workaround but that's web dev. just remember if you set the property to true then true again it won't trigger, so a count variable may work better (it's kinda hacky).
Doing a clone via JSON.parse solved my problem in a cleaner way than toggling a variable :
...
this.status$.subscribe((status: ModelStatusesModel) => {
this.treeDataService.updateStatus(this.imageGroups, status);
triggerChange();
});
...
private triggerChange() {
this.imageGroups = JSON.parse(JSON.stringify(this.imageGroups));
}

Render HTML string w/JSX expression in React dangerouslySetInnerHTML()

I have successfully used React's dangerouslySetInnerHTML to render HTML string responses I'm getting from an API, but now I need to add a click handler to parts of certain responses that are set by dangerouslySetInnerHTML. Is there a way to add handlers to dangerouslySetInnerHTML conent? Example:
var api_response = '<a onClick={this.props.methodName} href="www.google.com">example</a>';
return <div dangerouslySetInnerHTML={_markupFromString(api_response)}></div>
function _markupFromString (msg) {
return { '__html': msg };
}
This isn't going to work. dangerouslySetInnerHTML() doesn't work with JSX. A better strategy would be to have the AJAX call simply return the required strings and (link and title) and supply a component with these values as props. The component is simply the link in this situation.
Or you can add your SVG in the file public/index.html then create In your component a function like this
doSomething() {
console.log('ajajaj');
}
componentDidMount() {
window.NameVarible = this.doSomething;
}
And add your event onClick="NameVariable()" in the SVG

Categories

Resources