Execute a nested function in vue - javascript

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
}

Related

Intialize Vue data with the result of an AJAX call

I need to initialize a Vue component's data with the result of an AJAX call. I tried the following:
data: function () {
return {
supplierCount: 0
}
},
created: function () {
axios.get("/supplier/list").then(response => {
this.supplierCount = response.data.length;
});
}
However, this approach doesn't work, because the template can access the data before the AJAX handler updates supplierCount.
What's the correct way to initialize the data with the result of an asynchronous call? For example, if I return a promise (instead of an object) from data, will Vue wait until the promise is rejected/resolved before exposing the data to the template?
I don't think you can force your component to initialize only after the ajax call, but you can configure it to be hidden before the ajax data is loaded, either by hiding it with css (using v-show) or by simply preventing its rendering (using v-if).
For example, you can add a property hasLoaded to your component, and bind either v-show or v-if to it, like this:
On your js:
data: function () {
return {
supplierCount: 0,
hasLoaded: false
}
},
created: function () {
axios.get("/supplier/list").then(response => {
this.supplierCount = response.data.length;
this.hasLoaded = true;
});
}
On your template:
<!-- The top element is your root element, and you should always render it, so the v-show is appended to the immediate child -->
<div>
<div v-show="hasLoaded">
<!-- the rest of your template goes here -->
</div>
</div>
Render your template html conditionally.
That's the best way I believe - Nothing get added to DOM if condition fails.
<div v-if="supplierCount">
</div>

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.

Vue, getting sender element on change event

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)
}
}
})

Knockout component dispose not called

I have a custom KO component address-input
ko.components.register('address-input', {
viewModel: { createViewModel: function ({}, componentInfo) {
var self = {};
self.dispose = function() {
// When removed by KO, dispose computeds and subscriptions
};
return self;
}},
template: 'address-input'
});
The corresponding template is address-input.html
<div class`enter code here`="clearfix row">
<!-- elements come here -->
</div>
My application is an SPA one whose basic layout will be like below
A main.html will contain section.html which inturn holds address-input,html. On page nav , section.html will be replaced by another html and so on.
The section htmls are loaded through AJAX
$j.ajax({
url: url,
success: function(htmlText) {
var $el = $j(element);
$el.html(htmlText);
ko.applyBindingsToDescendants(bindingContext, $el[0]);
},
cache: false,
mimeType: 'text/html-ko'
});
I might have some observables subscribed in the address-input component in future. When that happens i would like the dispose method called when navigating away from the page. But it is not happening now. What is wrong here? Is it a case of DOM not getting removed from memory? If it is so , why?
You're using jQuery to replace a part of the DOM tree. Knockout has no way of knowing which elements are removed and cannot call dispose on the bound models.
Use knockout's html binding to add/remove the new section or (not recommended) call ko.cleanNode(element) before calling $el.html.
An example that shows:
When you manually remove a component from the DOM, knockout isn't notified and cannot call dispose
When you use a regular binding to alter the DOM (e.g. foreach, if, with) knockout does call dispose when stuff has to be removed
When you call ko.cleanNode, knockout detaches all nodes from their models, calls dispose, and let's you do what you want with the remaining DOM nodes.
ko.components.register('mycomponent', {
viewModel: function(params) {
this.dispose = () => console.log("Dispose called");
},
template: "<li>My Component</li>"
});
// Some example data to render a list
const comps = ko.observableArray([1, 2, 3, 4]);
// Remove straigt from the DOM without knockout...
const badRemove = () => document
.querySelector("mycomponent:last-child")
.remove();
const manualDetach = () => ko.cleanNode(document.querySelector("div"));
// Use knockout to alter the DOM
const goodRemove = () => comps.shift();
ko.applyBindings({ comps, badRemove, goodRemove, manualDetach });
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div data-bind="foreach: comps">
<mycomponent></mycomponent>
</div>
<button data-bind="click: badRemove">bad remove</button>
<button data-bind="click: goodRemove">good remove</button>
<button data-bind="click: manualDetach">clean node</button>

How to access custom attributes from event object in React?

React is able to render custom attributes as described at
http://facebook.github.io/react/docs/jsx-gotchas.html:
If you want to use a custom attribute, you should prefix it with
data-.
<div data-custom-attribute="foo" />
And that's great news except I can't find a way to access it from the event object e.g.:
render: function() {
...
<a data-tag={i} style={showStyle} onClick={this.removeTag}></a>
...
removeTag: function(event) {
this.setState({inputVal: event.target????});
},
The element and data- property render in html fine. Standard properties like style can be accessed as event.target.style fine.
Instead of event.target I tried:
event.target.props.data.tag
event.target.props.data["tag"]
event.target.props["data-tag"]
event.target.data.tag
event.target.data["tag"]
event.target["data-tag"]
none of these worked.
event.target gives you the native DOM node, then you need to use the regular DOM APIs to access attributes. Here are docs on how to do that:Using data attributes.
You can do either event.target.dataset.tag or event.target.getAttribute('data-tag'); either one works.
To help you get the desired outcome in perhaps a different way than you asked:
render: function() {
...
<a data-tag={i} style={showStyle} onClick={this.removeTag.bind(null, i)}></a>
...
},
removeTag: function(i) {
// do whatever
},
Notice the bind(). Because this is all javascript, you can do handy things like that. We no longer need to attach data to DOM nodes in order to keep track of them.
IMO this is much cleaner than relying on DOM events.
Update April 2017: These days I would write onClick={() => this.removeTag(i)} instead of .bind
Here's the best way I found:
var attribute = event.target.attributes.getNamedItem('data-tag').value;
Those attributes are stored in a "NamedNodeMap", which you can access easily with the getNamedItem method.
Or you can use a closure :
render: function() {
...
<a data-tag={i} style={showStyle} onClick={this.removeTag(i)}></a>
...
},
removeTag: function (i) {
return function (e) {
// and you get both `i` and the event `e`
}.bind(this) //important to bind function
}
// Method inside the component
userClick(event){
let tag = event.currentTarget.dataset.tag;
console.log(tag); // should return Tagvalue
}
// when render element
<a data-tag="TagValue" onClick={this.userClick}>Click me</a>
<div className='btn' onClick={(e) =>
console.log(e.currentTarget.attributes['tag'].value)}
tag='bold'>
<i className='fa fa-bold' />
</div>
so e.currentTarget.attributes['tag'].value works for me
As of React v16.1.1 (2017), here is the official solution: https://reactjs.org/docs/handling-events.html#passing-arguments-to-event-handlers
TLDR: OP should do:
render: function() {
...
<a style={showStyle} onClick={(e) => this.removeTag(i, e)}></a>
...
removeTag: function(i, event) {
this.setState({inputVal: i});
}
This single line of code solved the problem for me:
event.currentTarget.getAttribute('data-tag')
You can access data attributes something like this
event.target.dataset.tag
If anyone is trying to use event.target in React and finding a null value, it is because a SyntheticEvent has replaced the event.target. The SyntheticEvent now holds 'currentTarget', such as in event.currentTarget.getAttribute('data-username').
https://facebook.github.io/react/docs/events.html
It looks like React does this so that it works across more browsers. You can access the old properties through a nativeEvent attribute.
You can simply use event.target.dataset object . This will give you the object with all data attributes.
I do not know about React, but in the general case you can pass custom attributes like this:
1) define inside an html-tag a new attribute with data- prefix
data-mydatafield = "asdasdasdaad"
2) get from javascript with
e.target.attributes.getNamedItem("data-mydatafield").value
Try instead of assigning dom properties (which is slow) just pass your value as a parameter to function that actually create your handler:
render: function() {
...
<a style={showStyle} onClick={this.removeTag(i)}></a>
...
removeTag = (customAttribute) => (event) => {
this.setState({inputVal: customAttribute});
}
This worked for me... My attribute is named "attr" in the example.
e.target.selectedOptions[0].attributes.attr.value
If you have multiple icons with different data-label (age,name,email):
<button
data-label="name"
onMouseOver={handleValue}
className="icon"
>
<FaUser />
</button>
when the mouse is over an icon, you change the title by accessing data-label
const handleValue = (e) => {
// making sure mouse is over an icon
if (e.target.classList.contains("icon")) {
const newValue = e.target.dataset.label;
setTitle(newValue);
setValue(person[newValue]);
}
};
In React you don't need the html data, use a function return a other function; like this it's very simple send custom params and you can acces the custom data and the event.
render: function() {
...
<a style={showStyle} onClick={this.removeTag(i)}></a>
...
removeTag: (i) => (event) => {
this.setState({inputVal: i});
},
I think it's recommended to bind all methods where you need to use this.setState method which is defined in the React.Component class, inside the constructor, in your case you constructor should be like
constructor() {
super()
//This binding removeTag is necessary to make `this` work in the callback
this.removeTag = this.removeTag.bind(this)
}
removeTag(event){
console.log(event.target)
//use Object destructuring to fetch all element values''
const {style, dataset} = event.target
console.log(style)
console.log(dataset.tag)
}
render() {
...
<a data-tag={i} style={showStyle} onClick={this.removeTag.bind(null, i)}></a>
...},
For more reference on Object destructuring
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring

Categories

Resources