How can I dynamically move child div between parent divs using Knockout? - javascript

In my sample, I have three parent divs (colored red) ... First, Second and Third.
Initially I want to put my child div (colored yellow) in the "Second" div.
After this, I want to move the child div between parent divs just by changing columnID.
Here's my test code that does not work...
var dataColumns = [{
id: 1,
text: "First"
},
{
id: 2,
text: "Second"
},
{
id: 3,
text: "Third"
}
]
var viewModel = {
data: ko.observableArray([]),
columns: ko.observableArray([]),
};
dataColumns.forEach(function(c) {
let column = {
id: c.id,
text: c.text,
items: ko.computed(function() {
return ko.utils.arrayFilter(viewModel.data(), function(d) {
return d.columnID() === c.id;
});
}, this)
};
viewModel.columns.push(column);
});
ko.applyBindings(viewModel);
let item = {
columnID: ko.observable(2),
caption: "I am the moving item"
};
viewModel.data.push(item);
function MoveTo(index) {
item.columnID(index);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div data-bind="template: { name: 'columnTemplate', foreach: columns, as: 'column' }"></div>
<script type="text/html" id="columnTemplate">
<div style="background-color: red;">
<div data-bind="text: text"></div>
<div data-bind="template: { name: 'itemTemplate', foreach: column.items, as: 'item' }"></div>
</div>
</script>
<script type="text/html" id="itemTemplate">
<div style="background-color: yellow;">
<div data-bind="text: caption"></div>
</div>
</script>
<button onclick="MoveTo(1)">Move to column 1</button>
<button onclick="MoveTo(2)">Move to column 2</button>
<button onclick="MoveTo(3)">Move to column 3</button>
Fiddle: https://jsfiddle.net/MojoDK/at8grkwe/3/
Is my goal impossible to do with Knockuot?
My reallife project is a kanban system, where the id of the task specify which kanban column the task should be in - and then just by changeing then tasks columnID, the task should be moved to another kanban column.
UPDATE:
I updated snippet and Fiddle to correct the error HeyJude pointed out - but it still doesn't move between columns when clicking the buttons.

It's only a small mistake you've got there.
Change this:
viewModel.columns.push(c);
... into this:
viewModel.columns.push(column);

Related

How do I make it so that I can edit my vue to do list to add items?

Ok, so, I am new to Vue and I am trying to make a simple to do list. I have managed to display it and stuff but I am having trouble with the code to add items to the list. When I click the button to add the task, it adds a bullet point but it doesn’t show the text given. For example, in the input box, I will type the name of a task but when I click the button, what I wrote doesn’t show up, just the bullet point.
I have tried putting the newTask part in other places but the same problem happens. I know this is probably a pretty silly question and the solution is probably pretty obvious but please help as fast as you can, I need to do this for school.
Here is my HTML:
<body>
<div id="app">
<!-- Vue App HTML Code -->
<h1>To Do List</h1>
<div id="app">
<ul>
<li v-for="x in tasks">
{{ x.text }}
</li>
</ul>
</div>
<div id="newTask">
<input type="text" value="taskName" placeholder="Task Name" v-model="newTask"></input>
<button v-on:click="tasks.push(newTask)">Add Task</button>
</div>
</body>
And my JavaScript/ Vue:
myObject = new Vue({
el: '#app',
data: {
tasks: [
{ text: '1' },
{ text: '2' },
{ text: '3' },
],
newTask: ''
},
})
I would really appreciate any other Vue tips (relevant or not) because it is my first week learning it and anything would be really useful.
You can achieve this by pushing the newTask in the existing tasks array.
Working Demo :
var vm = new Vue({
el: '#app',
data: {
tasks: [
{ text: '1' },
{ text: '2' },
{ text: '3' },
],
newTask: ''
},
methods: {
addTask() {
this.tasks.push({ text: this.newTask })
this.newTask = '';
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h1>To Do List</h1>
<ul>
<li v-for="x in tasks">
{{ x.text }}
</li>
</ul>
<input type="text" placeholder="Task Name" v-model="newTask"/>
<button v-on:click="addTask">Add Task</button>
</div>

To display dynamic JSON data in 2 different div's

My JSON looks like this:
users: [
{ 'name': 'User 1'},
{ 'name': 'User 2'},
{ 'name': 'User 3'},
{ 'name': 'User 4'},
{ 'name': 'User 5'},
{ 'name': 'User 6'},
]
Now i am looping this and i am displaying in the same div, But i need to diaplay the data by these conditions:
Up to length 3 should display in one div and rest things should display in another div(Ex another div right to that div). Here the JSON will be dynamic, The length may be <3 or >3. My requirement looks like this:
JSFiddle link
Vue is convenient to accomplish such tasks.
You need another two computed properties based on users.
template:
<div>{{left}}</div>
<div>{{right}}</div>
computed: {
left: function(){
return this.users.slice(0, 3);
},
reight: function() {
return this.users.slice(3);
}
}
Another option: https://jsfiddle.net/kth61cLu/1/
<div id="app">
<h3>Users</h3>
<div class="users1">
<div v-for="(user, index) in users" v-if="index < 3">
<p>{{user.name}}</p>
</div>
</div>
<div v-if="users.length > 3" class="users2">
<div v-for="(user, index) in users" v-if="index > 3">
<p>{{user.name}}</p>
</div>
</div>
</div>
Create a computed property that splits your array into two with the first having three elements and the second having the rest.
Then loop over the splitUsers array to display.
new Vue({
el: '#app',
data: {
users: [{"name":"User 1"},{"name":"User 2"},{"name":"User 3"},{"name":"User 4"},{"name":"User 5"},{"name":"User 6"}]
},
computed: {
splitUsers () {
const split = [ this.users.slice(0, 3) ]
if (this.users.length > 3) {
split.push(this.users.slice(3))
}
return split
}
}
})
#app { display: flex; }
#app div { padding: 1rem; border: 1px solid black; }
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<div id="app">
<div v-for="chunk in splitUsers">
<p v-for="user in chunk">{{ user.name }}</p>
</div>
</div>
OK, here is what you can try.
<h3>Users</h3>
<div v-for="(user) in users.slice(0,3)">
<p>{{user.name}}</p>
</div>
<div v-for="(user) in users.slice(3)">
<p>{{user.name}}</p>
</div>
Hope it helps!

Show/Hide button onclick in ng-repeat inside repeat

How can I access and show/hide the exact button in second repeat?
<div class="row" ng-repeat="person in people">
<div class="row" ng-repeat="ticket in tickets">
<button type="button" ng-if="!ticket.added" ng-click="add(ticket)">+</button>
<button type="button" ng-if="ticket.added" ng-click="remove(ticket)">-</button>
</div>
</div>
For example I have 3 persons and 4 different tickets. When someone click on button I want to add clicked ticket for that person.
Now when I click on add button, it's adding clicked ticket for all persons :(
Thanks in advance!
I'm not sure (your question is not too clear) but, maybe you can pass the person to your functions too?
Example:
$scope.people = [{
name: 'Jhon Snow',
tickets: [{
name: 'ticket1',
added: false
}, {
name: 'ticket2',
added: false
}]
}, {
name: 'Peter Parker',
tickets: [{
name: 'ticket3',
added: false
}, {
name: 'ticket4',
added: false
}]
}];
$scope.add = function (ticket) {
ticket.added = true;
}
$scope.remove = function (ticket) {
ticket.added = false;
}
And the html:
<div class="row" ng-repeat="person in people">
{{ person.name}}
<div class="row" ng-repeat="ticket in person.tickets">
- {{ ticket.name }}
<button type="button" ng-if="!ticket.added" ng-click="add(ticket)">+</button>
<button type="button" ng-if="ticket.added" ng-click="remove(ticket)">-</button>
</div>
</div>
You can check a working example here

Active a item inside a v-for loop when clicked

I am a bit stuck at the moment trying to figure out how to active the current clicked element inside a loop. Basically I just want to change some CSS to that item, for example opacity, and know where I actually clicking inside the loop (this one I can handle with onclick I think).
So basically I tried this:
<div class="panel panel-default">
<ul class="list-group">
<li :style="panel.color" v-for="(panel,key,index)in getPanels"
:class="{active: panel === activeItem}" class="list-group-item"
>
A section {{panel.section}} was {{panel.action}}
</li>
</ul>
</div>
data() {
return {
activeItem: null
}
},
.active {
opacity: 0.7;
}
The active class is not getting applied to the specific clicked item. What is wrong – can anyone help?
Your code has a couple problems:
instead of :style="panel.color", you should put in an object, so it should be :style="{ color: panel.color }"
you forgot to add a click handler, i.e., you should add v-on:click="..."
Note: To simplify things, I didn't use your getPanels but use an array instead, it shouldn't affect how you understand how everything works.
const app = new Vue({
el: '#app',
data: {
panels: [
{section: 'One', action: 'Action 1', color: 'red' },
{section: 'Two', action: 'Action 2', color: 'blue' },
{section: 'Three', action: 'Action 3', color: 'green' },
{section: 'Four', action: 'Action 4', color: 'orange' },
{section: 'Five', action: 'Action 5', color: 'purple' }
],
activeItem: -1
},
methods: {
clickHandler(idx) {
this.activeItem = idx
}
}
});
.active {
opacity: 0.7;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<div id="app">
<div class="panel panel-default">
<ul class="list-group">
<li
class="list-group-item"
v-for="(panel, index) in panels"
:class="{active: index === activeItem}"
:style="{ color: panel.color }"
v-on:click="clickHandler(index)"
>
A section {{panel.section}} was {{panel.action}}
</li>
</ul>
</div>
</div>

Knockout.js {{each}} not listing items in javascript template

I've created a simple app that should iist each item from a model in a list, created using a javascrit template.
Fiddle
Html:
<div id="tagsList" class="box">
<div class="box-head">
<h2 class="left">Tags</h2>
</div>
<div class="box-content">
<input type="text" placeholder="Add New Tag" />
<button>+ Add</button>
<div data-bind="template: 'tagsTempl'"></div>
</div>
</div>
<script id="tagsTempl" type="text/html">
<ul>
{{each tags}}
<li class="tagItem">
<span>${Name}</span>
<div>
Edit
Delete
</div>
</li>
{{/each}}
</ul>
</script>
Javascript:
$(function () {
//$("#tagDialog").hide();
var data = [
{ Id: 1, Name: "Ball Handling" },
{ Id: 2, Name: "Passing" },
{ Id: 3, Name: "Shooting" },
{ Id: 4, Name: "Rebounding" },
{ Id: 5, Name: "Transition" },
{ Id: 6, Name: "Defense" },
{ Id: 7, Name: "Team Offense" },
{ Id: 8, Name: "Team Defense" }
];
var viewModel = {
tags: ko.observableArray(data),
tagToAdd: ko.observable(""),
addTag: function() {
this.tags.push({ Name: this.tagToAdd() });
}
}
ko.applyBindings(viewModel)
});
Output of list:
{{each tags}}
${Name}
Edit Delete
{{/each}}
The scripts file is accessible through viewing source. I'm not sure where my error is. Any help?
I updated your fiddle. Now it is working like you want it to: The list of tags is being rendered using the knockout standard method as described in the docs.
HTML
<ul data-bind="template: {name: 'tagsTempl', foreach: tags}"></ul>
Template
<script id="tagsTempl" type="text/html">
<li class="tagItem">
<span data-bind="text: Name"></span>
<div>
Edit
Delete
</div>
</li>
</script>
Also I connected the viewmodel to the view.
For example:
<button data-bind="click: addTag">+ Add</button>
You simply forgot most of it. I suggest you follow the interactive tutorials on how to do this.

Categories