I follewed this instructions to create a Vue instance programmatically. I use this to dynamically add component instances in project by user events. My problem now it, that my component to initialize needs a model. I regular I would use it like this:
<my-component v-model="variable"/>
But now I create this component with this code snippet within another components methods section:
import MyComponent from '../MyComponent'
...
add () {
const Component = Vue.extend(MyComponent)
const instance = new Component()
instance.$mount()
document.getElementById('app').appendChild(instance.$el)
}
I know using a $ref here is better, but it must work globally, so I didn't know how to add it else to the DOM. But just as side note.
Now i need to give this instance a v-model binding. I already know how to define props or slots, but not a model. In the official docu they mention something for that. But to be honest I don't understand it and didn't get it work.
Can anybody tell me how I have to extend my code to define the model for this instance? Something like instance.$model = this.variable would be awesome. Thank u!
Finally I got some kind of workaround for this. I'm not aware if there is a better solution out there, but this works for me.
The MyComponent used this description to handle the v-model. By this is emit the change event for the parent component. So The idea is simply to pass the model variable as property, work in MyComponent on a copy of this variable and emit changes to the parent. To catch this change event I can add to my instance the following:
const Component = Vue.extend(EditWindow)
const instance = new Component({
propsData: { content: this.variable }
})
instance.$on('change', value => {
this.variable = value
})
instance.$mount()
document.getElementById('app').appendChild(instance.$el)
I guess this is pretty the same as Vue actually does in the background (maybe?). But after all it works and I'm happy. Of cause I'm open for the 'correct' solution, if such one should exist.
Related
I have a component called ShowComment and a component called EditComment.
In ShowComment there is a variable this.CommentRecID. I want to use this variable in the component EditComment.
The problem is that a console.log(this.CommentRecID); shows that the variable is undefined in EditComment, but defined in ShowComment, but I don't know why it's undefined:
I used this to "use" this.CommentRecID in EditComment, but I don't know if this is the correct way to do it because it's related to jquery:
import * as $ from "jquery";
import DatePicker from "vue2-datepicker";
export default {
props: ["CommentRecID"],
components: { DatePicker },
Here's the full ShowComment component: https://pastebin.com/fcy4PCq0
Here's the full Editcomment component: https://pastebin.com/uik7EwD1
I'm fairly new to Vue.js. Does someone know how one can solve this issue?
You should not use jQuery and Vue.js at the same time.
You should try to use props to send data from parent to child.
You could add EditComment as an element in your ShowComment something like this:
<EditComment CommentRecID="this.CommentRecID" v-if="showEdit" />
And toggle the showEdit flag from the editItem method
editItem() {
this.showEdit = true
}
If you want to show a modal, then your EditComment component is probably up the tree so you could either use EventBus or use Vuex.
It seems like you are already using Vuex in your project, so add a mutation that stores the CommentRecID and use it in a similar manner to show the dialog.
You can use Vue Props to easily solve this problem, you have to send the variable from parent component to child component, please check this PROPS documentation out, its self explanatory:
Props Vuejs documentation
please let me know if you find trouble in using props
I have a problem working with two components (pop ups) in which i have to send data from a chlid component to another one (parent) who doesn't have an event to extract this data.
logically i have to find a sort of function that makes the parent listen to the changes made in the child.
The Changes have to appear in the same time in both components.
Could any one help ?
The answer is in your question. You need an Output property, which is the Angular generalization of a JS event.
In your child component:
class ChildComponent {
#Input() someProperty: string;
#Output() dataChanged = new EventEmitter<string>();
whenSomethingHappensInChild() {
this.dataChanged.emit('something');
}
}
In your parent template:
...
<app-child [someProperty]="'someValue'" (dataChanged)="doSomething($event)"></app-child>
...
And in you Parent code:
class ParentComponent {
...
doSomething(theValue: string) {
// TA-DAA! you have the value.
}
...
}
Please, do yourself a favor and READ THE DOCS, or, better, a book ;)
In particular:
https://angular.io/guide/architecture-components has a full overview of the basics of binding, which this problem falls into.
Have a nice day.
Yes, you can use a shared BehaviorSubject to push values and both components have to subscribe to get this changes
Problem Solved: I used the #Host() tag to get the current instance of the Parent component and access the methode that changes it's attributes.
Here is what you should do.
First:
You should declate your parent component in the child
parent:ParentComponent;
Second :
you should pass your current parent instance to your new declaration in the constructor
constructor(#Host() currentParent:ParentComponent){
this.parent=currentParent
}
Third:
Now try just to access the methods and attributes in the parent components
changeParentAttribute(){
this.parent.iAmInTheParent();
}
I hope you find this helpful
I'm fairly new to unit testing. I wonder how I could test if a smart component is passing some as an #Input() to a dumb one (using Angular 4+).
At first, I thought about checking if the property exists:
it('should have some data', async(() => {
expect(component.data).toBeTruthy();
}));
However, I faced two issues: 1) that tells me if data is true but doesn't necessarily means it's being passed as an input to my dumb component; 2) if the data property doesn't exist, then the test suite won't be executed.
Any tips? Is there a better way to approach this? Thanks.
Since input bindings are processed as part of change detection you can basically change the property used in bindings on the parent component, then run detectChanges() on the parent component and check if the input property has changed in the child component. Something along these lines:
parentComponent = TestBed.createComponent(BannerComponent);
const childComponentEl = fixture.debugElement.query(By.directive(ChildComponent));
const childComponent = childComponentEl.injector.get(ChildComponent);
it('should have some data', async(() => {
parentComponent.componentInstance.boundProperty = 3;
parentComponent.detectChanges();
expect(childComponent.inputProperty).toBe(3);
}));
You can read more about why input bindings update in:
Everything you need to know about change detection in Angular
The mechanics of property bindings update in Angular
My use case requires me to add several copies of a child component to a template programmatically (think: iterating through an array with *ngFor="let childComponent of componentArray"). The children components all emit a select event; the twist is that each of these components has a different event handler within the parent component. Trying to be clever, I decided to store the event handler as a property of each member of componentArray. So my template is now something like:
<my-cmp *ngFor="let childComponent of componentArray" (select)="childComponent.callback()"></my-cmp>
My model contains:
componentArray = [{data:0, callback:this.onSelect0}, {data:1, callback:this.onSelect1}];
onSelect0(){/*do stuff specific to childComponent 0*/}
onSelect1(){/*do stuff specific to childComponent 1*/}
callback is a reference to the class method I would like that particular childComponent's select event to trigger. callback is triggered properly; the problem is that from it I cannot access the rest of my component because this in that context refers to the component during that iteration of the loop.
It sounds more confusing than it is. I've found a workaround but it seems really clunky (I store the class instance as a property in each member of componentArray). I've made a plunkr accessible from http://plnkr.co/edit/VxxCR8hjUsxQ3SABWe8E?p=preview. Basically my question is: if I pass the event handler as an object property (childComponent.callback above), how can I access my class instance? Any feedback is welcome.
That's default JS/TS behavior if you pass a method reference directly. You can either use bind like methodName.bind(this) or a fat arrow function like () => methodName() to retain the scope of this.
In your Plunker just change this line
private thing = {name:'ThingOne', onSelect: this.handlerOne };
to
private thing = {name:'ThingOne', onSelect:() => this.handlerOne() };
Plunker example
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'});
}
})