Vue, getting sender element on change event - javascript

Im just learning VUE and I've been trying something as simple as getting the sender-element of a #change event.
My markup looks like this
<v-text-field label="Takst 1" ref="tarif1Input" v-model="Income.Tariffs[0]" field="#SecurityHelper.SimpleCrypt("CUST_TARIFF1")" ##change="tariffChanged"></v-text-field>
and the event is wired up like this:
methods: {
tariffChanged: function (newVal) {
var $s = $(this.$refs.tarif1Input.$el).find('input');
var newValue = $s.getValue();
$.post('/api/Budget/SaveTariff', {
FieldKey: $s.attr('field'),
Value: newVal
}).done(function () {
app.populateIncome();
});
}
}
The only way I've managed to get the sending element is by using refs and using this :
this.$refs.tarif1Input.$el but is that really the best way to do this? cant I somehow get the sending element through the functions arguments.
It really seems like a step backwards from wiring this up manually in javascript; kind of like overusing global variables.
I'm using vuetifyjs.

Have a look on this fiddle https://jsfiddle.net/55e46mn3/4/
<div id="app">
<input type="text" #change="tariffChange"/>
</div>
const app = new Vue({
el: '#app',
methods: {
tariffChange(event) {
const inputTarget = event.target;
console.log(inputTarget.value)
}
}
})

Related

calling vue function from js on change

I'm trying to make a call from a on.("change") event to a vue method and that works fine but trying to give the received data from the change event to a Vue variable, the console log says that the variable has the new data, but it doesn't really change the variable correctly, it changes the last variable when you duplicate the components.
here is some of my code:
Vue.component('text-ceditor',{
props:['id'],
data: function (){
return {
dataText: "this is something for example"
}
},
template: '#text-ceditor',
methods: {
setData: function(data){
console.log(data)
this.dataText = data
console.log(this.dataText)
}
},
mounted: function(){
CKEDITOR.replace(this.$refs.text);
self = this;
CKEDITOR.instances.texteditor.on('change', function() {
self.setData(this.getData())
})
}
})
the component works correctly but the variable just change the last one
here is the fiddle: https://jsfiddle.net/labradors_/3snmcu84/1/
Your problem isn't with Vue but with CKEDITOR and its instances (with the ids you defined in the template and the way you reference them).
First problem is that you're duplicating ids in the text-ceditor component:
<textarea ref="text" v-model="dataText" id="texteditor" rows="10" cols="80"></textarea>
Why do we need to fix this? Because CKEDITOR instances in Javascript are id-based.
So now we need to change the id attribute to use the one passed in the component's props, like this:
<textarea ref="text" v-model="dataText" :id="id" rows="10" cols="80"></textarea>
Once we took care of that, let's reference the correct CKEDITOR instance from within the mounted method in the component.
We want to reference the one that matches with the id in our component.
From:
mounted: function(){
CKEDITOR.replace(this.$refs.text);
self = this;
CKEDITOR.instances.texteditor.on('change', function() {
self.setData(this.getData())
})
}
To:
mounted: function () {
CKEDITOR.replace(this.$refs.text);
var self = this;
var myCKEInstance = CKEDITOR.instances[self.id]
myCKEInstance.on('change', function () {
self.dataText = myCKEInstance.getData()
})
}
Notice that I also removed the call to setData as there is no need to have it and also declared self as a variable avoiding the global scope (which would overwrite it everytime and reference the latest one in all different components).
Now everything is updating correctly, here's the working JSFiddle.

Execute a nested function in vue

I have two functions that are nested in vue, the parent function is supposed to get the value of an attribute, while the child is supposed to use the value of the attribute to make an api call. How can I execute this function once to ensure I get this attribute and make the api call at once?
//button with the attribute I want
<button :data-post-id="My id">Click Me</button>
//Here I'm calling the parent function
<button #click="getPostId">Submit to api</button>
Javascript
getPostId: function (evt) {
const postId = evt.target.getAttribute('data-postid');
//console.log(postId);
function usePostId(){
console.log("I am accessible here " +postId)//null
}
return usePostId()
}
Your approach will create function multiple time, Just start with the simple function and keep separate.
new Vue({
el: '#app',
data: {
postid: ''
},
methods:{
setPostId: function (id){
this.postid = id;
},
getPostId: function () {
console.log(this.postid);
}
}
})
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<div id="app">
<button #click="setPostId(11)">Set 11</button>
<button #click="setPostId(22)">Set 22</button>
<button #click="setPostId(33)">Set 33</button>
<button #click="getPostId">Get postid</button>
<div>{{postid}}</div>
</div>
I am no vue expert but I can spot one inconsistency.
You are binding your callback to child but set the attr data-post-id on parent and then expecting child to have that attr. Also, it seems the attribute name doesn't match i.e. what you have set and what you are trying to get.
As for the original problem, i am not sure why you didn't add the attribute to child element as well and in case you can't do that you will need to find the desired parent through DOM.
#mots you could do something like the below,
usePostId: function(id) {
console.log("I am accessible here " + id)
},
getPostId: function(evt) {
const postId = evt.target.getAttribute('data-post-id')
const output = this.usePostId(postId)
return output
}

Vue.js - "Bind" bootstrap modal to Vue model

So, in my Vue instance, I have a currentTask model, which is null by default.
new Vue({
el: '...',
data: {
currentTask: null
}
});
When I click on a 'task-item', which has v-on="click: openTask" directive, i want to launch the modal with the currentTask:
methods: {
openTask: function(e) {
this.currentTask = this.clickedTask;
$('#task-modal').modal('show');
e.preventDefault();
}
}
This is working just fine, although I don't know if there is a more "magical" way to two-way bind the whole modal + visibility to the currentTask.
Now, what I need, if there is no better way to go about this, is to somehow listen for the modal close event, which normally we would do in jQuery with $('#myModal').on('hidden.bs.modal', function() {}); inside of Vue and set this.currentTask = null;.
Thank you.
You could probably use a custom directive to handle this.
Vue.directive('task-selector', {
bind: function () {
var vm = this.vm;
var el = $(this.el);
el.on('hidden.bs.modal', function() {
vm.data.currentTask = 'whatever value you want here';
});
},
update: function (newValue, oldValue) {
// i don't think you have anything here
},
unbind: function () {
// not sure that you have anything here
// maybe unbind the modal if bootstrap has that
}
})
In your html you would need to put this directive on the modal element like so...
<div id="task-modal" v-task-selector>
... modal body stuff here...
</div>

How do you access object properties from a nested polymer element?

I would like to access javascript object data from a custom polymer element nested inside a list. The host page has the following:
<core-list id="eventData">
<template>
<event-card event="{{model}}"></event-card>
</template>
</core-list>
With the following script:
<script type="text/javascript">
var data = [
// server side code to populate the data objects here
];
var theList = document.querySelector('core-list')
theList.data = data;
function navigate(event, detail, sender) {
window.location.href = '/events/show/?eventId=' + event.detail.data.id
}
theList.addEventListener("core-activate", navigate, false);
</script>
Inside event-card.html the following markup in the template achieves the desired result:
<core-toolbar>
<div class="title">{{event.title}}</div>
</core-toolbar>
<div class="responses">{{event.numResponses}} responses</div>
However when I run the following in the template script:
Polymer('event-card', {
ready: function() {
var eventDates = [];
var theEvent = this.getAttribute('event');
console.log(theEvent);
console.log(theEvent.proposedDate);
console.log(theEvent.possibleStartDate);
console.log(theEvent.possibleEndDate);
if (theEvent.proposedDate) {
eventDates[0] == theEvent.proposedDate;
} else {
if (theEvent.possibleStartDate && theEvent.possibleEndDate) {
var startDate = moment(theEvent.possibleStartDate);
var endDate = moment(theEvent.possibleEndDate);
var difference = endDate.diff(startDate, 'days');
for (i = 0; i < difference; i++) {
eventDates[i] = startDate.add(i, days);
}
}
}
console.log(eventDates);
this.dates = eventDates;
},
created: function() {
// hint that event is an object
this.event = {};
}
});
</script>
the log statements print
{{model}}
undefined
undefined
undefined
Array[0]
So I seem to be getting caught by the different ways that properties and attributes are evaluated in different contexts but I am not sure whether it is a bug in my code or even what approach to try next.
Because the "event" attribute is set by Polymer when the template is processed, the ready event handler is the wrong place to do your stuff (by the way, using Polymer you should consider using the "domReady" event handler).
Anyway, to get your scenario working, just add a "eventChanged" method to your Polymer component. Whenever an attribute changes (which is also the case when Polymer executes the template element) a "[propertyName]Changed(oldVal, newVal)" will be called (if existing) to signal the change to your component.
So implement this method and you're done.
One more caveat in your code : You should consider using "this.event" to access the attribute value (or as best solution the "newVal" parameter of your eventChanged(oldVal,newVal) method).
Why not using Polymer's attribution model from the get go?
<polymer-element attributes="event">
<script>
Polymer("event-card", {
ready: function() {
var theEvent = this.event;
}
});
</script>
</polymer-element>

Wrap a JavaScript function reference

I have the following JavaScript code, which works as expected...
/** #jsx React.DOM */
var TreeView = React.createClass({
render: function() {
return <div ref="treeview"></div>;
},
componentDidMount: function() {
console.log(this.props.onChange);
var tree = $(this.refs.treeview.getDOMNode()).kendoTreeView({
dataSource: ...,
dataTextField: "Name",
select: this.props.onChange
}
});
}
});
var MyApp = React.createClass({
onChange: function(e) {
console.log(e);
this.setState({key: e});
},
render: function() {
return (
<div>
<TreeView onChange={this.onChange}/>
<GridView />
</div>
);
}
});
However, with the kendo treeview, on selecting a tree node, the whole node is passed. To get at the underlying key, I would need to process the node as follows:
select: function(e) {
var id = this.dataItem(e.node).id;
this.props.onChange(id);
}
However I've obviously not quite got it right since, and here please excuse my noobness, it seems that in the working instance a reference to the function is being used, whereas in the non-working instance, the function is actually being executed... Or something like that: the error message being returned is:
Cannot call method 'onChange' of undefined.
So what would I need to do to be able to reference the function which extracts the key before calling the onChange method? Note that, if my understanding is correct, onChange needs to be executed in the context of the MyApp class so that any child components will get notified on the change.
EDIT: I've tried using partial application but am still not quite there. I've updated the onChange routine to take a function which returns the key from the node
onChange: function(getKey, e) {
this.setState({Key: getKey(e)});
},
But am not sure how to wire it all up.
Your code looks mostly right. I believe your only problem is that the select callback you're passing to the treeview is being called with the wrong scope. Note that you're using this to mean two different things within the function (once for the actual tree view object and the other for the React component). Easiest solution is probably to store a reference to the onChange function like so:
componentDidMount: function() {
var onChange = this.props.onChange;
var tree = $(this.refs.treeview.getDOMNode()).kendoTreeView({
dataSource: ...,
dataTextField: "Name",
select: function(e) {
var id = this.dataItem(e.node).id;
onChange(id);
}
});
}
Hope this helps.

Categories

Resources