I'm making an application that uses TypeScript, and as a templating language I'm using Svelte.
That allows me to create DOM elements with classes that can change in realtime according to a variable, thanks to ternary operator. For instance:
<div class="{theme == "dark" ? "bg-black" : "bg-white"}"> Hello </div>
The thing is, my application has to dynamically generate some DOM elements. That makes me create some divs using the following piece of script:
const parentDiv = document.getElementById("parentDiv");
const childDiv = document.createElement("div")
childDiv.classList.add(theme == "dark" ? "bg-black" : "bg-white")
parentDiv.appendChild(childDiv)
In this case, the conditional operator is just calculated when .add() is called, which happens once. There is no "realtime calculation" of the value like in the first method above. How do I handle this ?
If you are not creating the elements via plain Svelte markup you are probably doing something wrong. There are various directives that help, e.g. {#if} and {#each}.
In some cases it makes sense to imperatively add things, but even then, you should add components not plain DOM elements. You can instantiate any component using the client API, e.g.
new MyComponent({ target: document.body })
Anything inside the component then can use the Svelte mechanism that automatically update. If you need to have reactivity from the outside, you can pass a store as a property or import a global store from within/load it from a context.
(Would recommend making the theme a store. Either global or passed through props/contexts.)
Note on contexts with the client API: They have to be passed explicitly; to inherit existing contexts you can use getAllContexts:
// somewhere at top-level
const context = getAllContexts();
// ...
new MyComponent({ ..., context })
Related
What are the use cases which require the use of the HTML attribute ref?
And what would be the value affected to it?
I've faced this example:
methods: {
myMethod(event) {
this.$refs.userInfo.open();
},
}
<template>
<myComponent
ref="userInfo"
:usr="usr" />
</template>
So my question what is userInfo really is?
From where comes open method ?
Template refs is a way of accessing the element itself in the DOM.
It's usually used to make some DOM-specific things or to handle some edge-cases (like a vanilla JS package not fully compatible with Vue's reactivity).
If not needed, you could use regular event listeners + iterations with v-fors.
In your given example, you're accessing either a component userInfo or a method/object with a public method of open. I recommend that you inspect your DOM tree in your Vue devtools.
I am trying to learn vue . I grabbed a demo project and have much of it working but I am not sure I under stand what this does.
const search = ref("");
const movies= ref([]);
in the template and other places it then accesses the value using search.value. Why use Ref("") ?
ref() allows us to create a "reference" to any value and pass it around without losing reactivity.
The beauty behind that is the fact that you can create reactive properties outside your Vue component. Inside a plain js (or ts) file, just import "ref" from the "vue" package, and then create a reactive property, e.g.
const msg = ref("hello")
For the examples you provided, you're simply initializing the variables with an empty string for search and empty array for movies.
In Vue 3, ref() makes a variable reactive.
https://vuejs.org/api/reactivity-core.html#ref
I wanted my directive to work as v-if since in my directive I have to check access rights and destroy the element if it does not have access.
Here is my code
Vue.directive('access', {
inserted: function(el, binding, vnode){
//check access
if(hasAccess){
vnode.elm.parentElement.removeChild(vnode.elm);
}
},
});
vue file
<my-component v-access='{param: 'param'}'>
The issue is that i'm applying this directive to a component, it's removing the component but not the execution of functions called by the created/mounted hook.
In the component(my-component) there are functions in mounted/created hook. The execution of these functions are done and I don't want these functions to be executed. Is there a way to stop execution of the mounted/created events?
It is impossible to replicate the behavior of v-if in a custom directive. Directives cannot control how vnodes are rendered, they only have an effect on the DOM element it is attached to. (v-if is special, it's not actually a directive but instead generates conditional rendering code when the template is compiled.)
Though I would avoid doing any of the following suggestions if possible, I'll provide them anyway since it's close to what you want to do.
1. Extend the Vue prototype to add a global method
You definitely need to use v-if to do the conditional rendering. So all we have to do is come up with a global helper method which calculates the access permission.
Vue.prototype.$access = function (param) {
// Calculate access however you need to
// ("this" is the component instance you are calling the function on)
return ...
}
Now in your templates you can do this:
<my-component v-if="$access({ param: 'param' })">
2. Define global method in the root component
This is basically the same as #1 except instead of polluting the Vue prototype with garbage, you define the method only on the root instance:
new Vue({
el: '#app',
render: h => h(App),
methods: {
access(param) {
return ...
}
}
})
Now in your templates you can do this:
<my-component v-if="$root.access({ param: 'param' })">
Now it's clearer where the method is defined.
3. Use a global mixin
This may not be ideal, but for what it's worth you can investigate the viability of a global mixin.
4. Use a custom component
You can create a custom component (ideally functional but it needn't be) that can calculate access for specific regions in your template:
Vue.component('access', {
functional: true,
props: ['param'],
render(h, ctx) {
// Calculate access using props as input
const access = calculateAccess(ctx.props.param)
// Pass the access to the default scoped slot
return ctx.scopedSlots.default(access)
}
})
In your templates you can do this:
<access :param="param" v-slot="access">
<!-- You can use `access` anywhere in this section -->
<div>
<my-component v-if="access"></my-component>
</div>
</access>
Since <access> is a functional component, it won't actually render it's own component instance. Think of it more like a function than a component.
A bit overkill for your situation, but interesting nonetheless if you ever have a more complicated scenario.
In current (2020) Angular, I have two components that are intended to share the state of activeProject through a service. I have the following defined on an ApplicationProjectService:
private activeProjectSource = new BehaviorSubject(undefined);
activeProject$ = this.activeProjectSource.asObservable();
set activeProject(v: any) {
this.activeProjectSource.next(v);
}
get activeProject() {
return this.activeProjectSource.value;
}
I'm using BehaviorSubject in the service since I want components to get the current value upon subscribing without any change. The getter/setter is there because I was doing some other binding directly to a service property, which I've since learned is not recommended.
The two sibling components that eventually trace back to a common parent, but I'm not using #Input() or #Output() or any parameter passing in the DOM:
this.appProjectService.activeProject$.subscribe(activeProject => {
this.activeProject = activeProject;
});
Each component is binding to the this.activeProject property in their respective component using [(ngModel)]:
<input type="checkbox" [(ngModel)]="activeProject.someProperty">
Question
If each component obtained what I thought was a copy of activeProject through this.appProjectService.activeProject$.subscribe(), how is it working that a change to the local property in one component is reflected in the other? In the end this is the behavior I want, but I can't understand why it works. Is there some passing by reference that I'm not understanding in rxjs observables?
sIf you have 2 components, the both local variables activeProject use the same reference of activeProject. ngModel is bound to a property of this reference. So it's working, because a change in a component only update the property of the reference, and does not change the reference. You can even use a variable activeProject without wrapping it in a BehaviorSubject.
I know this should be in comment but this much of letters comment won't accept.
Forget about RxJS for a while.
Now you have getter and setter for your property.
You set activeProjectValue in your service.
Now when you subscribe it in one component, you will get the object which will be passed by reference. Same for the other component. As both components accessing same object they are passed by reference.
If you have to break the reference, to use it differently.
Also each component obtained what I thought was a copy of activeProject .... this means they copy by refenrence of object.
I know, you know how to break reference, but this is just for sake for future viewers
To break the reference of object you can use JSON.parse(JSON.stringify(*ObjectName*)
In your example
this.appProjectService.activeProject$.subscribe(activeProject => {
this.activeProject = JSON.parse(JSON.stringify(activeProject));
});
Is it a good practice to do this in ReactJS?
var Component = React.createClass({
render: function () {
return (<div></div>);
},
field: 'value', // is this safe?
method: function () {
// do something with field
}
})
Before starting to suggest that I should use this.props or this.state, for me it's not the case, because those are fields that do not affect rendering in any way directly, they just work together to control the rendering.
I would like to use the React class as I do with regular javascript 'classes'.
My main concern here is how those fields and methods are handled inside React, and if the fields are set on the instance itself or directly on the prototype, which would not be suitable at all for what I need.
I ran a quick test and it seems that the fields are set on the instance, and the methods on the prototype, which is ideal. But is this the expected and documented behavior? And is this safe for future versions?
I think it can work the way you are doing and that it's safe. However if I understand well you are proceeding data calculation/transformation directly in the view.
So I would advise that you remove this logic from the view and treat it in the model part of a mvc or mv*, in your backbone models, or in your flux store for example.
This way you won't be mixing data transformation logic and pure rendering.
I would say so, I have been using things like this for a while and have not seen any issues. For example, let's say you want a handler of some sort that you want to pass to nested components, you would create the function in this component and pass it as a prop to a child. I believe they have examples that use similar concept in the ReactJS Facebook site.
Under the hood React is just looping through the properties of the object you pass to createClass and copying them to the prototype of the Component. Primitive values like strings or numbers obviously cannot be copied by reference, so don't get shared across all instances, whereas objects, functions, arrays and so on will.
If you want to work with values that are just local to the component instance you need to use the state API. I'm not sure what you mean by "[state and props] do not affect rendering in any way directly, they just work together to control the rendering". The whole point of props and state is that they work together to generate values to be used when (re)rendering.
https://facebook.github.io/react/docs/component-api.html
A React component should only render in response to either changing props or changing state. You cannot/shouldn't trigger a re-render by mutating other fields directly.
You need to think of your component as something closer to a pure function. State and props go in at the top, and static VDOM/HTML comes out.
I would re-write your example as,
var Component = React.createClass({
getInitialState: function () {
return {field: 'value'};
},
render: function () {
var field = this.state.field;
return (<div>{field}</div>);
},
method: function () {
var field = this.state.field;
// do something with field
this.setState({field: 'anotherValue'});
}
})