Array length is incorrect - javascript

I have this code:
public toggle(event): void {
console.log(event.currentTarget.parentNode.children);
html:
<div class="activity-header" timeline-item ng-click="vm.toggle($event);">
So when I click the header I pass in the event, grab the parent element and than it's children.
When I check the log I see:
HTMLCollection(2) [div.activity-header, div.activity-footer]
When I click to expand the array in the log I see:
HTMLCollection(2) [div.activity-header, div.activity-footer]
0: div.activity-header
1: div.activity-body
2: div.activity-body
3: div.activity-body
4: div.activity-body
5: div.activity-body
6: div.activity-body
7: div.activity-body
8: div.activity-footer
length: 9
The body elements are rendered from a ng-repeat:
<div ng-repeat="activity in vm.activities.items track by activity.id" class="activity-body">
Just for reference the html structure is like so:
<div>
<div class="activity-header" timeline-item ng-click="vm.toggle($event);">
<i class="material-icons">person</i>
</div>
<div ng-repeat="activity in vm.activities.items track by activity.id" class="activity-body">
<div class="contact-moment">
<i class="material-icons">person</i>
<div class="contact-moment-body">
<b>{{vm.activity.channel | activityChannel}}: <span ng-bind-html="vm.case.title || (vm.activity | activityTitle)"></span></b>
<br />
<div ng-include="'/CustomerService/ActivityTimeline/ActivityTemplates/' + activity.type + '.html'" id="{{activity.type}}"></div>
</div>
</div>
</div>
<div class="activity-footer">
<md-button>
Button 1
</md-button>
</div>
</div>
So the issue is that Javascript doesn't count the activity-body elements while they are there.

That's because when you console.log, the div.activity-body aren't rendered to the DOM yet. When you expand the list of DOM nodes, the body elements have been rendered, and the event.currentTarget.parentNode.children reference has been updated to reflect this.
What's written to the log is just a snapshot - when you interact with it, it gets updated to reflect the real world.

children is a live list and console.log() holds a reference to an object just like most other JavaScript.
The order of events is:
Log children: It has 2 elements in it.
Add 7 more elements to it.
Click the expand icon in the Console which causes the object to be reexamined, so all 9 elements are shown.

Related

How to get array index from html element?

Hey In my Vue application I have the opportunity to create several persons:
<div v-for="newContent in temp" :key="newContent.id" #click="showId(newContent.id)" v-show="true">
<!-- Head of part personal data -->
<h4 class="person" style="margin-left: 0.95vw">Person 1</h4>
<!-- Enter Person Data -->
<testmodul1></testmodul1>
<div class="trash">
<button class="btn btn-outline-danger" #click="deletePerson()">Person löschen</button>
</div>
<hr />
</div>
If I want to add more persons, there is a button, which appends more of these person inputs:
<button class="btn btn-outline-secondary" #click="useIt()">Add more persons</button>
useIt(){
this.temp.push({
id:this.id+=1
})
console.log(this.temp);
},
data() {
return {
id:0,
temp:[{
id:0,
}]
};
},
Output of the console.log method in the console (clicked the add button 2 times):
Proxy {0: {…}, 1: {…}, 2: {…}}
[[Handler]]: Object
[[Target]]: Array(2)
0: {id: 0}
1: {id: 0}
length: 2
[[Prototype]]: Array(0)
Now the following problem: Let's say for example we created 3 new persons. Now I recognize, that the second person is false and I want to delete it. When I click on the of person 2 I want to get the array index of the html element. I have already tried this, but it does not really work well:
<div v-for="newContent in temp" :key="newContent.id" #click="showId(newContent.id)" v-show="true">
showId(index){
alert(index);
}
Is there an other way how I could find out the index in the array of the html div I clicked?
Please, check Vue.js list rendering.
You can iterate through array with both element and index like this:
<li v-for="(item, index) in items">
{{ index }} - {{ item.message }}
</li>
For your case:
<div v-for="(newContent, index) in temp" :key="newContent.id" #click="showId(index)" v-show="true">
You can define index variable in v-for loop itself. Like
<div v-for="(person,index) in persons" :key="index">
{{index}} //which is index of current item
</div>

Target element when inside v-for loop Vuejs

I am trying to build a To-Do list in Vue.js which has 3 columns: To-Do, Doing, Done.
I would like to be able to move an item between columns by clicking on arrows that are inside the list item.
Right now I have a list of objects that I separate in 3 arrays depending on a "status" attribute. I would like to change that attribute when clicking on left/right arrow then refresh the UI with new arrays.
I haven't found the way to target the element that received the click.
<ul>
<li v-for="todo in todoTodos" v-bind:key="todo._id">
<span v-if="todo.importance == 1" class="bg-success"></span>
<span v-else-if="todo.importance == 2" class="bg-warning"></span>
<span v-else-if="todo.importance == 3" class="bg-alert"></span>
<div>
<h3>{{ todo.title }}</h3>
<p>{{ todo.description }}</p>
</div>
<p class="todo__date">Début: {{ todo.datebegin }} - Fin espérée: {{ todo.dateend }}</p>
<div class="todo__actions">
<i #click="editTodo" class="icofont-edit"></i>
<i #click="moveRight" class="icofont-arrow-right"></i>
<i #click="moveLeft" class="icofont-arrow-left"></i>
</div>
</li>
</ul>
My linter prevents me from using v-for + v-if, but I guess that means I will have to re-calculate each list (todoTodos, doingTodos, doneTodos) after each modification. Is there a better way ?
I tried console.logging this e.target e.currentTarget but
this logs the entire data model
e.target and e.currentTarget logs the element which I can't use to find my way back to the todo item I want to modify
You /usually/ pass the ($event) argument to a method if you want to access an event.
<i #click="moveLeft($event)" class="icofont-arrow-left"></i>
......
methods: {
moveLeft(e){
console.log(e.target)
}
}

How to handle event delegation with priorities?

I have this look-a-like code in my app :
let item;
$('.parent').on('click', function () {
item = $(this).attr('item') || 0;
});
$('.parent').on('click', '.children', this, function () {
doSomething();
});
<div class="parent" item="50">
<div class="children">
</div>
</div>
<div class="parent" item="51">
<div class="children">
</div>
</div>
<div class="parent" item="52">
<div class="children">
</div>
</div>
I have a parent element with several children in it. Clicking anywhere on the parent element will give me an item information, so I will be able to load functions according to this item variable on click on children.
My problem is : I have to delegate the onClick event on children to parent element otherwise the events will trigger in this order :
Click on any child
Click on parent, which is too late because I need item variable first
I have a functionality that replaces the parent element if activated, since it was dynamically inserted into the DOM, I have to delegate the onClick event as well, like this :
$('.grandParent').on('click', '.parent', function () {
item = $(this).attr('item') || 0;
});
Now my problem is that the events are triggering in the wrong order again :
Click on any child
Click on parent
How can I manage to set click event on parent as top priority?
Edit :
I will be more specific. I have a messages list on my page, each .message element contains the message content but also some functionnalities like edit, delete, set as favorite, like, etc.
Like this :
<div class="message" data-item="1">
<a class="edit">E</a>
<a class="delete">X</a>
<a class="favorite">Fav</a>
<a class="like">Like</a>
<div class="content">
Hello world !
</div>
</div>
<div class="message" data-item="2">
<a class="edit">E</a>
<a class="delete">X</a>
<a class="favorite">Fav</a>
<a class="like">Like</a>
<div class="content">
Hello world !
</div>
</div>
Each one of those functionnalities will trigger different functions when clicked : edit(), delete(), like(), etc.
All of them will make AJAX requests which will then send the item variable to my server in order to know what item has to be impacted by this click.
To avoid repetition in my events handlers, I am trying to get the data-item attribute's value with one event bound to click on .message element, relying on the bubbling of children elements.
Get the item where you actually want it and get rid of the handler on parent:
$('.parent').on('click', '.children', function () {
item = $(this).closest('.parent').attr('item');
doSomething();
});
EDIT:
Then get the item when you click on the grandparent:
$('.grandParent').on('click', '.children', function () {
item = $(this).closest('.parent').attr('item');
});
EDIT #2:
Based on the OPs updated requirements, do it like this:
<div class="message" data-item="1">
<a class="action edit" data-type="edit">E</a>
<a class="action delete" data-type="delete">X</a>
<a class="action favorite" data-type="favorite">Fav</a>
<a class="like">Like</a>
<div class="content">
Hello world !
</div>
</div>
<div class="message" data-item="2">
<a class="action edit" data-type="edit">E</a>
<a class="action delete" data-type="delete">X</a>
<a class="action favorite" data-type="favorite">Fav</a>
<a class="like">Like</a>
<div class="content">
Hello world !
</div>
</div>
$(document).on('click','.message .action',function(e) {
const item = $(this).closest('message').attr('item');
switch($(this).data('type')) {
case 'edit': doEdit(item); break;
case 'delete': doDelete(item); break;
case 'favorite': doFavorite(item); break;
}
});
See, one event handler?
It's a very common pattern to do what you're doing. In the absence of a more robust framework (e.g. React), then using plain jQuery this is how it should be done.

Vue JS does renders braces instead of result

Im using Vue JS (2.0.8) to render a list of devices. I have a health status that is represented by a number, so I use a method to convert the number to a CSS class to display it correctly. The problem is that Vue does not render the result of the method, but the method call itself.
My Vue method:
methods: {
...
health: function (device) {
if (device !== null) {
switch (device.health.status) {
case 2:
return "connected";
break;
case 1:
return "warning";
break;
case 0:
return "disconnected";
break;
case -1:
default:
return "unsupported";
break;
}
}
},
...
}
My HTML (Im using Laravel, hence the '#'):
<div v-for="device in devices" class="device">
<div class="device-top">
<div class="device-bullet #{{ health(device) }}"></div>
<div v-on:click="device.open = !device.open" v-bind:class="{open: device.open}" class="device-more-info"><span class="icon icon-show-more"></span>More info</div>
</div>
</div>
This renders the following HTML:
<div class="device>
<div class="device-top">
<div class="device-bullet {{ health(device) }}"></div>
<div class="device-more-info"><span class="icon icon-show-more"></span>More info</div>
</div>
</div>
However, this is the odd bit. If I move the #{{ health(device) to inside the device-bullet div instead of using it in an attribute, it renders like this (correctly, "connected" being the result of the function).
<div class="device>
<div class="device-top">
<div class="device-bullet">connected</div>
<div class="device-more-info"><span class="icon icon-show-more"></span>More info</div>
</div>
</div>
I have tried any combination I can think of to get it to render correctly but cannot seem to find the problem.
Any help is appreciated, thank you in advance.
Interpolation within attributes has been removed in Vue 2.x
Use v-bind:class instead:
<div class="device-bullet" v-bind:class="[health(device)]"></div>
Not sure of how laravel works, never used before.
But in "pure" vue can't use {{}} syntax to bind a html property. You need to use v-bind directive
<div v-bind:class="'device-bullet '+ health(device)" ></div>
Without v-bind: you're literally using device-bullet {{ health(device) }} as value.
For more clarification check this docs section

angularjs anchor in iterator (anchorscroll, ng-repeat,loop)

for example,
<div ng-repeat="item in items">
<p>item.title</p>
...
...
...
<p>item.up</p>
</div>
As i know ,anchor scroll need to specify an id for the anchor, but how can i do the anchor scroll without id, The situation is there's another loop wrap this item repeat. and i can't specify an id like
<div ng-repeat="item in items">
<p id="anchor{{$index}}">item.title</p>
...
...
...
<p>item.up</p>
</div>
So if the code is like below, there will be multi id="anchor{{$index}}"
<div ng-repeat="parent in parents">
<div ng-repeat="item in items">
<p id="anchor{{$index}}">item.title</p>
........
</div>
</div>
And i don't want to use two indexs to specify the id, or the code will be too complex.
How can i solve it .....Thanks
Without knowing your exact scenario, I would say that you are going to have to artificially create some kind of unique anchorId on the elements you care about.
Again, I don't know what your controller or model look like, but you could use a technique similar to this.
angular.forEach(this.parents, function(parent, i){
//Add unique anchorId to parent
parent.anchorId = "anchor-" + i;
angular.forEach(parent.children, function(child, j){
//Add anchorId to child as well
child.anchorId = "anchor-" + i + "-" + j;
});
});
This will basically give you something you can bind to in the DOM and then trigger a scroll with a click event. The bindings would be similar to what you have above.
<div data-ng-repeat="parent in ctrl.parents">
<h1 id="{{parent.anchorId}}">{{parent.name}}</h1>
<div data-ng-repeat="child in parent.children">
<h2 id="{{child.anchorId}}" class="child">
{{child.name}}
<small>
<a href="" ng-click="ctrl.scrollTo(parent.anchorId)">
| to {{parent.name}}
</a>
</small>
</h2>
</div>
</div>
And finally in your controller you could create a scrollTo method that looked something like this.
Ctrl.prototype.scrollTo = function(anchorId){
this.$location.hash(anchorId);
this.$anchorScroll();
};
This will basically do what you want and allow you to scroll around to your hearts content.
I've wrapped all this up in a jsFiddle example so you can see the full working example.

Categories

Resources