I'm taking an online course which corrects code using a bot. In this course I'm learnig HTML, JS and Vue.JS.
The assignemnt is to create a component, greet that produces <div>Welcome, Alice!</div> when it's called using <greet></greet> The assignment also wants me to use data and methods to make it so it says Welcome, Bob instead when you click om the div element in the component. It tells me to use the data and methods key in the component.
This is my code so far, but I'm stuck.
HTML
<body>
<div id="app"><greet></greet>
</div>
</body>
Vue code:
Vue.component('greet', {
data() {
return {
}
},
template: '<div>Welcome</div>'
})
new Vue({ el: '#app' })
How do I make it so it says Welcome, Alice! and when pressed, it says Welcome, Bob! ?
The bot gives me an output of
file.js
✓ exists
✓ is valid JavaScript
1) renders the correct markup when no clicks are made
2) renders the correct markup when one click is made
You can use the event #click on the div, with that you bind the click event to a specific method in this case I create toggleName()
Vue.component('greet', {
data() {
return {
name: 'Alice'
}
},
methods: {
toggleName() {
this.name = this.name == 'Alice' ? 'Bob' : 'Alice';
}
},
template: '<div #click="toggleName">Welcome {{ name }}</div>'
})
Then we create a method that accesses to the property defined in data in this case is name When we use the reserved word this we access to our instance and from there w can access the property name. Then we create a toggle for the name.
In order to access the variable in our template we need to use the special curly braces
{{ name }} this time the this is not needed.
In your data property, define a variable that will hold access to the name so to speak. In this case, we'll initialize it with a value of Alice:
data () {
return {
name: 'Alice'
}
}
We can add a click method now and bind it to our div element so that it will swap the name:
methods: {
swap () {
this.name = 'Bob'
}
}
Finally, we need to modify our template to receive a click handler and use dynamic bindings to display our name property from our data object:
template: `<div #click="swap">Welcome, <span v-text="name"></span></div>`
Note that in the above illustration the v-text is a directive and we have bound the value of our name property to it, which we modify through the #click handler we assigned to the swap function.
Related
I am looking to use custom Javascript to interact with form fields tied with Vue framework. The form appears in a WordPress theme search page (https://wilcity.com/search-without-map/)
Autoselect the region value (this I can perform using the JS below)
markerCityName = "Atlanta";
for (i = 0; i < document.getElementsByClassName("wilcity-select-2 select2-hidden-accessible")[0].options.length; i++) {
if (document.getElementsByClassName("wilcity-select-2 select2-hidden-accessible")[0].options[i].innerText == markerCityName) {
document.getElementsByClassName("wilcity-select-2 select2-hidden-accessible")[0].selectedIndex =
document.getElementsByClassName("wilcity-select-2 select2-hidden-accessible")[0].options[i].index;
triggerEvent(document.getElementsByClassName("select2-selection select2-selection--single")[0], 'focus');
triggerEvent(document.getElementsByClassName("select2-selection select2-selection--single")[0], 'keydown');
triggerEvent(document.getElementsByClassName("wilcity-select-2 select2-hidden-accessible")[0], 'change');
}
}
Selected value participates in the search without having to manually select it from the form interface.
(i) After the javascript runs and selects "Atlanta" in the region drop down.
(ii) Select any other field in the form for search to be executed.
(iii) you will notice this search did not take into account the pre-select region value "Atlanta"
I am unable to do (2). The autoselected value is not sent in post when form value changes, and the autoselected value is not picked up.
Modifying the DOM directly won't work, as you've discovered, because Vue doesn't know about those changes and is still working based on its internal state. You need to modify Vue's underlying data model instead.
Every Vue component's root DOM element will have a __vue__ property attached, which you can use to access and modify the component's internal state from outside:
// Set up a Vue component with some data in it:
Vue.component('child', {
data() {
return {
foo: 'Data from inside Vue'
}
},
template: '<div id="component">{{foo}}</div>'
})
new Vue({
el: '#app',
});
// now outside Vue:
document.getElementById('component').__vue__.$data.foo = "Updated value from outside vue"
<script src="https://unpkg.com/vue#latest/dist/vue.min.js"></script>
<div id="app">
<child></child>
</div>
Use of the __vue__ property isn't officially supported, as far as I know, but Vue's author says it's safe to use:
the official devtool relies on it too, so it's unlikely to change or break.
I am pretty new to Vue Framework. I am trying to propagate the changes from parent to child whenever the attributes are added or removed or, at a later stage, updated outside the component. In the below snippet I am trying to write a component which shows a greeting message based on the name attribute of the node which is passed as property from the parent node.
Everything works fine as expected if the node contains the attribute "name" (in below snippet commented) when initialized. But if the name attribute is added a later stage of execution (here for demonstration purpose i have added a set timeout and applied). The component throws error and the changes are not reflected . I am not sure how I can propagate changes for dynamic attributes in the component which are generated based on other events outside the component.
Basically I wanted to update the component which displays different type of widgets based on server response in dynamic way based on the property passed to it .Whenever the property gets updated I would like the component update itself. Why the two way binding is not working properly in Vuejs?
Vue.component('greeting', {
template: '#treeContainer',
props: {'message':Object},
watch:{
'message': {
handler: function(val) {
console.log('###### changed');
},
deep: true
}
}
});
var data = {
note: 'My Tree',
// name:"Hello World",
children: [
{ name: 'hello' },
{ name: 'wat' }
]
}
function delayedUpdate() {
data.name='Changed World';
console.log(JSON.stringify(data));
}
var vm = new Vue({
el: '#app',
data:{
msg:data
},
method:{ }
});
setTimeout(function(){ delayedUpdate() ;}, 1000)
<script src="https://vuejs.org/js/vue.js"></script>
<div id="app">
<greeting :message="msg"></greeting>
</div>
<script type="text/x-template" id="treeContainer">
<h1>{{message.name}}</h1>
</script>
Edit 1: #Craig's answer helps me to propagate changes based on the attribute name and by calling set on each of the attribute. But what if the data was complex and the greeting was based on many attributes of the node. Here in the example I have gone through a simple use case, but in real world the widget is based on many attributes dynamically sent from the server and each widget attributes differs based on the type of widget. like "Welcome, {{message.name}} . Temperature at {{ message.location }} is {{ message.temp}} . " and so on. Since the attributes of the node differs , is there any way we can update complete tree without traversing through the entire tree in our javascript code and call set on each attribute .Is there anything in VUE framework which can take care of this ?
Vue cannot detect property addition or deletion unless you use the set method (see: https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats), so you need to do:
Vue.set(data, 'name', 'changed world')
Here's the JSFiddle: https://jsfiddle.net/f7ae2364/
EDIT
In your case, I think you are going to have to abandon watching the prop and instead go for an event bus if you want to avoid traversing your data. So, first you set up a global bus for your component to listen on:
var bus = new Vue({});
Then when you receive new data you $emit the event onto the bus with the updated data:
bus.$emit('data-updated', data);
And listen for that event inside your component (which can be placed inside the created hook), update the message and force vue to re-render the component (I'm using ES6 here):
created(){
bus.$on('data-updated', (message) => {
this.message = message;
this.$forceUpdate();
})
}
Here's the JSFiddle: https://jsfiddle.net/9trhcjp4/
I have a custom element which will take user input, and on [save] button click, I want to pass information to the parent view-model so I can send it to the server and move to the next section. I'm going to simplify this for example's sake:
my-element.js:
import { customElement, bindable } from 'aurelia-framework';
#customElement('my-element')
#bindable('save')
export class MyElement { }
my-element.html:
<template>
<button click.delegate="save()">Click this</button>
</template>
parent-view-model.js:
export class ParentViewModel {
parentProperty = 7;
parentMethod() {
console.log(`parent property: ${this.parentProperty}`);
}
}
parent-view-model.html:
<template>
<require from="./my-element"></require>
<div class="content-panel">
<my-element save.bind="parentMethod"></my-element>
</div>
</template>
For a demo, see (app.js and app.html represent parent-view-model.js and parent-view-model.html):
https://gist.run/?id=96b203e9ca03b62dfb202626c2202989
It works! Kind of. Unfortunately, this seems to be bound to my-element instead of parent-view-model, so in this example, what is printed to console is: parent property: undefined. That will not work.
I know I can utilize the EventAggregator to facilitate some communication between the custom element and the view-model, but if I can help it I'd like to avoid the added complexity.
You have two options for this. You could handle this using Custom Events, or you can do it using the call binding that Anj mentioned in his answer. Which one you use depends on your actual use case.
If you want the custom element to be able to call a method on your parent VM and pass data out of the custom element, then you should use a Custom Event as shown in this gist: https://gist.run/?id=ec8b3b11f4aa4232455605e2ce62872c:
app.html:
<template>
<require from="./my-element"></require>
<div class="content-panel">
<my-element save.delegate="parentMethod($event)"></my-element>
</div>
parentProperty = '${parentProperty}'
</template>
app.js:
export class App {
parentProperty = 7;
parentMethod($event) {
this.parentProperty = $event.detail;
}
}
my-element.html:
<template>
<input type="text" value.bind="eventDetailValue" />
<button click.delegate="save()">Click this</button>
</template>
my-element.js:
import { inject, customElement, bindable } from 'aurelia-framework';
#customElement('my-element')
#inject(Element)
export class MyElement {
eventDetailValue = 'Hello';
constructor(element) {
this.element = element;
}
save() {
var event = new CustomEvent('save', {
detail: this.eventDetailValue,
bubbles: true
});
this.element.dispatchEvent(event);
}
}
You would basically attach any data you need to pass on the detail property of the Custom Event. In the event binding declaration, you would add $event as a parameter to the function and then check the detail property of $event in your event handler (you could also just pass $event.detail if you wanted).
If you want the custom element to be able to call a method on your parent VM and have data passed in from the parent VM (or from another custom element or something), then you should use the call binding. You can specify arguments that will be passed to the method by specifying them in the binding declaration (foo.call="myMethod(myProperty)". These arguments come from the VM context where the binding is being declared, not from the Custom Element's VM).
I was able to solve this after trolling through the aurelia docs hub for a bit. I don't know all the nuances that may be involved, but for this simple example, I was able to fix it by doing the following simple change:
In parent-view-model.html (or app.html in the gist-run example), change save.bind="parentMethod" to save.call="parentMethod()"
I am still unsure how to pass data from the custom element into the parent view-model's method, however.
Here is the documentation from aurelia website.
To pass parametrized functions to custom child components write the following (according to documentation http://aurelia.io/docs/binding/basics#function-references).
Parent ViewModel
private _generate(myParam:string) {
return myParam + " world";
}
Parent View
<custom-comp generator.call="_generate(myParam)"></custom-comp>
Child View
<template bindable="generator">
${generator({myParam:"hello"})}
</template>
This has been updated for aurelia v2:
The call binding no longer assigns properties of the first argument pass to the call to the calling override context. This is unreasonably dynamic and could result in hard-to-understand templates.
In Aurelia 1, you would have used call bindings like this:
export class MyElement {
onChange;
onInternalButtonClick() {
this.onChange({ value: this.value });
}
}
<my-element on-change.call="propertyChanged(value)">
In Aurelia 2, the property name is now on the $event property passed to the callback. It's a minor change, but you now do this instead:
<my-element on-change.call="propertyChanged($event.value)">
I'm starting out with vuejs and a vue grid at https://jsfiddle.net/kc11/7fqgavvq/2/.
I want to display the checked row objects in the:
<pre> {{ selected| json}} </pre>
at the bottom of the table. I've come across Check all checkboxes vuejs with an associated fiddle at https://jsfiddle.net/okv0rgrk/206/ that shows what I mean if you look at the outputted Selected Ids.
To get this working I'll need to add a method to the table component similar to
methods: {
selectAll: function() {
this.selected = [];
for (user in this.users) {
this.selected.push(this.users[user].id);
}
}
in https://jsfiddle.net/okv0rgrk/206/
Can someone explain this function as I am having trouble in particular with what 'this' means in this context.
this refers to your component. So anything inside of your component can be called using this. You can access data with this.users or this.selected, run methods with this.selectAll() or access anything else in your component.
In that fiddle, there is a users attribute on the data, so this.users refers to that array. The function selectAll() empties the this.selected array, then goes through and re-adds every user to the array, this.selected.
Edit -- computed properties
Add this to your component:
computed:{
userCount: function(){
return this.users.length;
}
}
then, anywhere in this component, this.userCount will return the number of users. This variable will update anytime the number of users changes. That is why its a "computed" property - you don't have to update, it just automatically recalculates when it needs to.
Following the guide from the Angular2 site I have this html:
<input #something (keyup)="doneTyping($event)">
<button (click)="add(something .value)">Add</button>
with this controller:
#Component({
selector: 'my-app',
appInjector: [SomeService]
})
#View({
templateUrl: 'index-angular',
directives:[NgFor]
})
class MyAppComponent {
name: string;
stuff: Array<string>;
constructor(someService: SomeService) {
this.name = 'Angular2Sample';
this.stuff= someService.getStuff();
}
add(st: string){
this.stuff.push(st);
}
doneTyping($event) {
if($event.which === 13) {
this.stuff.push($event.target.value);
$event.target.value = null;
}
}
}
When the user hits enter in the input, the doneTyping method clears the input with $event.target.value = null;.
However I can't come with a way of doing the same after pushing the button.
You can pass the input as a parameter in the button
<input #something (keyup)="doneTyping($event)">
<!-- Input as paramter -->
<button (click)="add(something)">Add</button>
And later in the add function
add(st: HTMLInputElement){
this.stuff.push(st.value);
st.value = null;
}
Also, you usually want to avoid interacting with DOM as much as possible. I just checked the Todo app example on the angular2 github and they also access the DOM element, but the last real commit is 2 months old.
If you use data binding you can have a cleaner code which would result in something like :
<input [value]="_newStuff" (keyup.enter)="addStuff()">
<button (click)="addStuff()">Add</button>
Then in your class you can just define the member _newStuff : string, that you can implement addStuff as follow :
addStuff() {
this.stuff.push(_newStuff);
this._newstuff = '';
}
In most cases you might want _newStuff to be a model object that works as an interface like this :
class Stuff {
id : any;
property : any;
otherProperty : any;
}
And then your _newStuff would be _newStuff : Stuff; and you could map it like this : <input [value]="_newStuff.property" (keyup.enter)="addStuff()">.
I know your sample code is very simple and you just try to get it to work, but I believe the data binding way is more in the logic of the framework, and the Form API basically gives tools such as Control, ControlGroup and FormBuilder that help you map your model on your view with Validators and such. It would be too cumbersome on something a bit larger to access the DOM everytime you need to change the view. In the end your example is almost raw javascript executed in an Angular2 context.
Coming back to your example, now imagine you have another event that triggers a new stuff to be added, say a double click or so, you'd need to add another method that handles this event, passing it again the HTMLInputElement, and do basically the same thing as you do on the keyup event handler, thus duplicating code again. With data binding your component owns the state of the view and you can therefore have one simple method that won't be affected by what kind of event triggered it. There you can do the test if the model is valid ( even though for this you'd use the Form API then ).
Anyways, I know this has been answered already, but I thought I would just help to improve the solution given my current understanding of it and how it could be applied to real cases.
You can use a one-direction bindings to access the value of the input. This is a very clear architecture; you don't have to pass DOM elements to the controller.
Template:
<!-- controller.value -> input.value binding-->
<input #myinput [value]=myValue>
<button (click)="done(myinput.value)">Add</button>
Controller:
myValue: string; // If you change this then the bound input.value will also change
// be sure to import ngOnInit from #angular/common
ngOnInit() {
this.myValue = "";
}
done(newValue) {
// ... processing newValue
this.myValue = ""; // Clear the input
}
Here's a good way to actually get your input objects to manipulate
Just need to import ViewChild from #angular/core
Template:
<input #something (keyup)="doneTyping($event)">
Class:
#ViewChild("something") something : HTMLInputElement;
doneTyping(e : KeyboardEvent) {
var text = this.something.value;
if (text != "") {
//DO SOME STUFF!!!!
}
}
Since version 2 of Angular is old now, and we developers are in need much more trend solutions and but we may look old topics in order to find solutions here, I felt I should mention an answer from another topic similar to this.
That answer works and solved my problem in Angular 7 with Type Script. Here its link
There are two ways:
1) I it is created with var or let or const, then it cannot be deleted at all.
Example:
var g_a = 1; //create with var, g_a is a variable
delete g_a; //return false
console.log(g_a); //g_a is still 1
2) If it is created without var, then it can be deleted.. as follows:
declaration:
selectedRow: any;
selectRowOfBankForCurrentCustomer(row: any) {
this.selectedRow = row; // 'row' object is assigned.
}
deleting:
resetData(){
delete this.selectedRow; // true
console.log(this.selectedRow) // this is error because the variable deleted, not its content!!
}
A quick way to do it:
<input #something (keyup)="doneTyping($event)">
<button (click)="add(something.value);something.value=''">Add</button>
A more Angular way to do it:
HTML:
<input [(ngModel)]="intermediateValue">
<button (click)="add()">Add</button>
Controller:
intermediateValue: string;
add() {
this.doStuff(this.intermediateValue);
this.intermediateValue= "";
}