Vuejs class component fields undefined - class

https://jsfiddle.net/78mLj9vb/
class App extends Vue {
message = 'Hello!';
shrek;//属性 property
constructor(){
super();
this.shrek = "This is my swamp!";//代入-assignment
//"This is my swamp!"がコンソールで出力されている Prints as it should
console.log(this.shrek);
this.print();
}
print(){
console.log(this.shrek);//[undefined]が出力されている Undefined printed to console
}
}
I'm learning Vuejs with class components and typescript. I do not understand why I can't access the fields of my class within methods. They are always undefined. I have tried doing the initial assignment to the field inline along with the property declaration, and I have also tried doing the assignment in the constructor. I imagine it's the Vuejs data binding mangling the class fields in a way that I do not understand, I have tried accessing them through this.$data to no avail. I understand it's probably not good design to have data that is unrelated to presentation in a component class, but this time around I don't have a database so I'm trying to hard code some data into a class method to fake it, so that I can then loop over the data w/v-for to create a select list. I've included a fiddle that looks nothing like what I'm actually trying to do, but illustrates the "issue" (my lack of understanding really).
How do you declare normal class fields outside of the Vuejs data-binding magic, or alternatively, how do you access the data that has been bound and changed by Vue?

For the best use change you code to this:
//...
#Component({
template: `
<div>
<h1>{{ message }}</h1>
<h1>{{ shrek }}</h1>
</div>
`
})
class App extends Vue {
data(){
return {
shrek: 'test',
message: 'Hello world!'
};
}
created(){
this.shrek = "This is my swamp!";
console.log(this.shrek);
this.print();
}
print(){
console.log(this.shrek);
}
}
new Vue({
el: '#app',
render: h => h(App)
})
//...
On your link here

After having practiced using Vue for a few more weeks and researching various topics on the internet I have come to the conclusion (which should have been obvious to me at first) that the answer is to use components only for what components are good for. That is to say, if you have some logic that gets gnarly and you want to alleviate it with helper functions, those helper functions can be declared outside the class as regular functions and used within the component code. Another upside of this approach is that if multiple components would get use out of your helper function or initialization code, you can instead move it into a typescript or javascript module and import it into those components that need it.
データ初期化や普通の論理を実施するヘルパー関数をコンポーネント内に置くより別の標準なTypescriptやJavascript関数に置いた方が良さそうです。Vuejsのコンポーネント・クラスの良い点はview作りだけでviewと関係ない論理をコンポーネント以外に置くべきです。それにそうするとその論理を別のファイルに置いてモジュール形で複数のコンポーネントはその論理を使えるようになります。

Related

How to detect svelte component from DOM?

Currently making an google chrome extension to visualize svelte components, this would only be used only development mode. Currently I am grabbing all svelte components by using const svelteComponets = document.querySelectorAll(`[class^="svelte"]`); on my content scripts but it is grabbing every svelte element. What are some approaches to grab only the components?
Well you mostly can't get to the Svelte component from the DOM elements.
The reason, appart from Svelte won't give you / expose what's needed, is that there isn't a reliable link between components and elements.
A component can have no elements:
<slot />
Or "maybe no elements":
{#if false}<div />{/if}
It can also have multiple root elements:
<div> A </div>
<div> B </div>
<div> C </div>
By bending the cssHash compiler option a lot, you would probably be able to extract the component "name", maybe class name from the CSS scoping classes generated by Svelte. (Which, in turn could break CSS-only HMR updates with Vite, but that's another story.)
But from there, you won't be able to reliably get to the individual component instances... If we keep the component from the last example, once you've grabbed those 6 divs:
<div> A </div>
<div> B </div>
<div> C </div>
<div> A </div>
<div> B </div>
<div> C </div>
... how do you know where one component instance ends and where the other begins? Or even that there are two components?
I believe, the most reliable way to achieve what you want is probably to use internal Svelte APIs, including those that are used by the actual Svelte dev tools that you want to mimic. (Gotta love when private APIs are the "most reliable"!)
Necessary disclaimer: this only seems reasonable to do this in your case because it is a study subject, and because it's dev only. It would certainly not be wise to rely on this for something important. Private / internal APIs can change with any release without any notice.
If you go in the Svelte REPL and look at the generated JS after enabling the "dev" option, you'll see that the compiler adds some events that are provided for the dev tools.
By trials and experimentation, you can get a sense of how Svelte works, and what dev events are available. You'd also probably need to dig the sources of the compiler itself to understand what's happening with some functions... Being comfortable with a good debugger can help a lot!
For your intended usage, that is build a representation of the Svelte component tree, you'll need to know when a component instance is created, what is its parent component, and when it is destroyed. To add it to the tree, in the right place, and remove it when it goes away. With that you should be able to maintain a representation of the component tree for yourself.
You can know when a component is created with the "SvelteRegisterComponent" dev event (squared in red in the above screenshot). You can know the parent component of a component being instantiated by abusing { current_component } from 'svelte/internal'. And you can know when a component is destroyed by abusing the component's this.$$.on_destroy callbacks (which seems like the most fragile part of our plan).
Going into much more detail about how to proceed with this seems of bit out of scope for this question, but the following basic example should give you some ideas of how you can proceed. See it in action in this REPL.
Here's some code that watches Svelte dev events to maintain a component tree, and exposes it as a Svelte store for easy consumption by others. This code would need to run before your first Svelte component is created (or before the components you want to catch are created...).
import { current_component } from 'svelte/internal';
import { writable } from 'svelte/store';
const nodes = new Map();
const root = { children: [] };
// root components created with `new Component(...)` won't have
// a parent, so we'll put them in the root node's children
nodes.set(undefined, root);
const tree = writable(root);
// notify the store that its value has changed, even
// if it's only a mutation of the same object
const notify = () => {
tree.set(root);
};
document.addEventListener('SvelteRegisterComponent', e => {
// current_component is the component being initialized; at the time
// our event is called, it has already been reverted from the component
// that triggered the event to its parent component
const parentComponent = current_component;
// inspect the event's detail to see what more
// fun you could squizze out of it
const { component, tagName } = e.detail;
let node = nodes.get(component);
if (!node) {
node = { children: [] };
nodes.set(component, node);
}
Object.assign(node, e.detail);
// children creation is completed before their parent component creation
// is completed (necessarilly, since the parent needs to create all its
// children to complete itself); that means that the dev event we're using
// is fired first for children... and so we may have to add a node for the
// parent from the (first created) child
let parent = nodes.get(parentComponent);
if (!parent) {
parent = { children: [] };
nodes.set(parentComponent, parent);
}
parent.children.push(node);
// we're done mutating our tree, let the world know
notify();
// abusing a little bit more of Svelte private API, to know when
// our component will be destroyed / removed from the tree...
component.$$.on_destroy.push(() => {
const index = parent.children.indexOf(node);
if (index >= 0) {
parent.children.splice(index, 1);
notify();
}
});
});
// export the tree as a read only store
export default { subscribe: tree.subscribe }

How to extend HTML attribute interfaces when designing reasonml react components?

I'm learning reasonml and quite excited about it. Something I often do in typescript react code is:
type Props = React.HTMLProps<HTMLButtonElement> & { foo: boolean }
const SuperButton: React.FC<Props> = (props) => <button {/* stuff with props */ />
In this regard, I communicate to my users as a component library provider that this button extends normal HTML button attributes.
How can I express and extend normal html component attributes in my components?
I see that reason explicitly doesn't support spreading props: https://github.com/reasonml/reason-react/blob/master/docs/props-spread.md.
I do see that there is a composition strategy: How to compose props across component in reason-react bindings?, but not sure how to marry that up with normal HTML element component stuffs.
Any recommendations? Thanks!
It's possible to do something similar using ReasonReact.cloneElement, as Amirali hinted. The idea is to split up your component's props and the HTML button's props into two separate parameters for your component, render your button, and then clone it while also injecting the extra button props.
This page shows a component which encapsulates this clone-and-injection functionality:
module Spread = {
[#react.component]
let make = (~props, ~children) =>
ReasonReact.cloneElement(children, ~props, [||]);
};
Now, you can use this Spread component for your SuperButton component:
module SuperButton = {
[#react.component]
let make = (~foo, ~htmlButtonProps) =>
<Spread props=htmlButtonProps>
<button> (foo ? "YES" : "NO")->React.string </button>
</Spread>;
};
The htmlButtonProps prop will contain the regular HTML button props, while separately foo is your component's specific prop. The component can be used like this:
<SuperButton foo=true htmlButtonProps={"autofocus": true} />
Small housekeeping note: you don't actually need to define the modules with the module keyword. If you want you can put them in separate files called Spread.re and SuperButton.re. Reason files automatically become modules.

Durandal - dispose of viewmodel after completion of registration process

Just wondering if anyone knows a good/simple approach using Durandal to disposing of or re-initializing a viewmodel once it becomes invalid?
I have a registration form that I could 're-initialize' manually after a user has completed the form and registered successfully, but I'd prefer to just dispose of it so that Durandal creates a new registraion view/view model when that particular route is accessed again.
If your viewmodel module returns a function rather than an object, it will create a new one each time rather than reusing the 'singleton' object. See the Module Values section of Creating a Module.
Updated link for the Durandal Module constructor function information: Module Values
You can split the difference:
var cache;
var ctor = function () {
if (cache) return cache;
// init logic
cache = this;
}
Just replace the if(cache) check with whatever "do I need a new thing or not" logic you like.
If you're using routing, simply redirect the user to an instance-based module (one that returns a constructor function). The user will most likely click or touch a button that signifies that he is done with the registration form. That would be the redirect action.
If you're using composition, you would still create an instance-based module. Then, you would use dynamic composition to swap it in once the user signified he was done with the registration form.
Dynamic composition is where the view and/or model attributes on a Durandal composition are, themselves, observables, referencing something like the following in the viewModel:
this.currentView = ko.observable('');
this.currentModel = ko.observable('');
Then, in your HTML:
<div>
<div data-bind="compose: {view: currentView(), model: currentModel())"></div>
</div>
When the user clicks "Done", or something to that effect, functions on your viewModel might look something like:
ctor.prototype.done = function () {
this.setCurrentView('viewmodels/registrationForm.html');
this.setCurrentModel('viewmodels/registrationForm.js');
}
ctor.prototype.setCurrentView = function (view) {
this.currentView(view);
}
ctor.prototype.setCurrentModel = function (model) {
this.currentModel(model);
}
Either one of the approaches above will create the registrationForm only when it's needed.
With Durandal 2.0, you can use the deactivate callback within the composition lifecycle. Here is some documentation http://durandaljs.com/documentation/Hooking-Lifecycle-Callbacks

scope of 'this' inside mootools class

I'm trying to define a click handler in a Mootools class. My handler presumes opening a block of links, each of which should be 'equipped' with its own click handler, which should trigger a link specific action. What I mean is let's suppose I have the following HTML code:
<div id="wrapper">
open options
<div class="optionsBlock" style="display:none">
1
2
3
</div>
</div>
Then I'm trying to define a class like this in Mootools:
var myHandler = new Class({
Implements : [Events],
initialize : function(element){
this.element = document.id(element);
this.elements = this.element.getChildren('a');
this.elements.addEvents('click', function(ev){
ev.preventDefault();
//'this' as a reference to the current element in the array, which is being clicked, correct?
this.getSibling('div.optionsBlock').setStyle('display', 'block');
var parentLink = this;
this.getSibling('div.optionsBlock').getChildren('a').addEvent('click', function(e){
e.preventDefault();
//should append the text of currently clicked link into the parent link
parentLink.appendText(this.get('text'))
});
});
}
});
new myHandler('wrapper');
This is just an illustration of how I can imagine the code should be like (and I'm sure this code is not good at all), but I really need some help regarding the following:
Since adding new events constatly changes the scope of 'this', how should I keep a reference both to the class instance and the element being clicked?
How should I modify the class in order not to have the entire code inside the initialize method? I tried to create separate methods for every event handler, but as a result I got confused with the scope of 'this', with binding and trying to get all of this together really annoys me, but I want to get a grip of this knowledge.
How to keep track of the scope of 'this' when adding nested event handlers inside a class? I honestly googled and searched for an answer but for no avail.
Thanks!
scope, take your pick - asked many many times - search here for [mootools]scope this:
Mootools class variable scope
mootools variable scope
Mootools - Bind to class instance and access event object
to recap:
use a saved reference var self = this; then reference self.prop or use the fn.bind pattern
add more methods. follow single responsibility principle. eg, in your class, create attachEvents: function() {} and have initialize call that.
by using the saved reference pattern. you can fix it upriver by delegating events as opposed to creating new event callbacks on parent clicks.

Zend Form Element with Javascript - Decorator, View Helper or View Script?

I want to add some javacsript to a Zend_Form_Element_Text .
At first I thought a decorator would be the best way to do it, but since it is just a script (the markup doesn't change) then maybe a view helper is better? or a view script?
It seems like they are all for the same purpose (regarding a form element).
The javascript I want to add is not an event (e.g. change, click, etc.). I can add it easily with headScript() but I want to make it re-usable , that's why I thought about a decorator/view helper. I'm just not clear about the difference between them.
What is the best practice in this case? advantages?
UPDATE: Seems like the best practice is to use view helpers from view scripts , so decorators would be a better fit?
Thanks.
You could create your own decorator by extending Zend_From_Decorator_Abstract and generate your snippet in it's render() method :
class My_Decorator_FieldInitializer extends Zend_Form_Decorator_Abstract {
public function render($content){
$separator = $this->getSeparator();
$element = $this->getElement();
$output = '<script>'.
//you write your js snippet here, using
//the data you have in $element if you need
.'</script>';
return $content . $separator . $output;
}
}
If you need more details, ask for it in a comment, i'll edit this answer. And I didn't test this code.
Use setAttrib function.
eg:-
$element = new Zend_Form_Element_Text('test');
$element->setAttrib('onclick', 'alert("Test")');
I'm not actually seeing where this needs to be a decorator or a view-helper or a view-script.
If I wanted to attach some client-side behavior to a form element, I'd probably set an attribute with $elt->setAttrib('class', 'someClass') or $elt->setAttrib('id', 'someId'), some hook onto which my script can attach. Then I'd add listeners/handlers to those targeted elements.
For example, for a click handler using jQuery , it would be something like:
(function($){
$(document).ready(function(){
$('.someClass').click(function(e){
// handle the event here
});
});
})(jQuery);
The benefit is that it is unobtrusive, so the markup remains clean. Hopefully, the javascript is an enhancement- not a critical part of the functionality - so it degrades gracefully.
Perhaps you mean that this javascript segment itself needs to be reusable across different element identifiers - someClass, in this example. In this case, you could simply write a view-helper that accepts the CSS class name as the parameter.
"the markup doesn't change", Yap,
but I like to add some javascript function throw ZendForm Element:
$text_f = new Zend_Form_Element_Text("text_id");
$text_f->setAttrib('OnChange', 'someFunction($(this));');
The best way is if you are working with a team, where all of you should use same code standard. For me and my team this is the code above.