This is my first experience with Meteor and Dragula.
I'm trying to do a dynamic list, with database data.
What I need is:
To mount the 'divs' with the data from database
To become these 'divs' draggable
"To insert" some itens "inside" these 'divs' (from database to)
To become these itens draggable, inside the 'divs' and between other 'divs'
To resume, is something like columns and cards in Trello.
I'm using Dragula and, first, I saw the documentation here, and this and this examples.
To solve the numbers 1 and 2, I'm trying this code.
And, the situation now is: the 'drake' contain the array with lists name, but I can't 'transform' them in 'divs'.
The result in the log is:
"element: (3) [{…}, {…}, {…}]
main.js:36 counter: 3
main.js:39 i: 0
main.js:41 listName: Lista 1
main.js:39 i: 1
main.js:41 listName: Lista 2
main.js:39 i: 2
main.js:41 listName: Lista 3
main.js:45 dragula: {containers: Array(3), start: ƒ, end: ƒ, cancel: ƒ, remove: ƒ, …}
containers: (3) ["Lista 1", "Lista 2", "Lista 3"]"
Can you help me ?
Thank you a lot.
PS1: the "draglist template" is only to show that Dragula is working.
PS2: sorry for English mistakes.
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import dragula from 'dragula';
import '../node_modules/dragula/dist/dragula.css';
import './main.html';
Listas = new Mongo.Collection('listas');
Tarefas = new Mongo.Collection('tarefas');
console.log("Before enter drag");
Template.dragList.onRendered(function(){
console.log("entrou no onRendered");
dragula([document.querySelector('#left1'), document.querySelector('#right1')]);
});
Template.lists.helpers({
'list': function(){
return Listas.find({});
},
'tasks': function(){
return Tarefas.find({});
},
'mount': function(){
console.log("Inside mount");
var drake = dragula({});
var element = Listas.find({}).fetch();
var counter = Listas.find({}).count();
console.log("element: ", element);
console.log("counter: ", counter);
var i;
for (i = 0; i < counter; i++) {
console.log("i: ", i);
var listName = element.[i].nome;
console.log("listName: ", listName);
drake.containers.push(listName);
// dragula([document.getelemententById(listName)]);
};
console.log("dragula: ", drake);
},
});
<head>
<title>Dragula test</title>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
</head>
<body>
<h1>Welcome to Meteor!</h1>
<h1>Draglist</h1>
{{> dragList}}
<br>
<h1>Lists</h1>
{{> lists}}
<br>
</body>
<template name="dragList">
<h5 class="card-title">Container 1</h5>
<div id="left1">
<p class="card-text">This is a draggable p</p>
<button class="btn btn-primary">First draggable button</button>
<button class="btn btn-primary">Second draggable button</button>
</div>
<h5 class="card-title">Container 2</h5>
<div id="right1">
<p class="card-text">This is another draggable p</p>
<button class="btn btn-primary">Third draggable button</button>
<button class="btn btn-primary">Fourth draggable button</button>
</div>
</template>
<template name="lists">
<div id=container>
{{mount}}
</div>
</template>
Related
Could you please help with this error? I have spent a lot of time on it, but no progress so for. Thanks!!
Error:
VM4705 knockout-debug.js:3326 Uncaught ReferenceError: Unable to process binding "with: function (){return currentCat }"
Message: Unable to process binding "text: function (){return clickCount }"
Message: clickCount is not defined
Expected behavior:
The program should list the cat names in the div with id "catNames" and update the div that has id "cat" with the information of the first cat from the "data" observable array. Next, when you click on different cat names from the names list, it should set the value of "currentCat" to the cat that is clicked on, which in turn should update the div with id "cat".
Link to JsFiddle
Below is my JS code:
var cats = [
{name:'cat1', photo: 'https://s20.postimg.org/owgnoq5c9/cat_1.jpg', clicks: 0 },
{name:'cat2', photo: 'https://s20.postimg.org/f9d5f0ccp/cat_2.jpg', clicks: 0 },
{name:'cat3', photo: 'https://s20.postimg.org/su3xe4s5l/cat_3.jpg', clicks: 0 },
{name:'cat4', photo: 'https://s20.postimg.org/xdg5zna15/cat_4.jpg', clicks: 0 },
{name:'cat5', photo: 'https://s20.postimg.org/78yuqivex/cat_5.jpg', clicks: 0 }
];
function CatRecord(cat){
var self = this;
self.catName = ko.observable(cat.name);
self.imgSrc = ko.observable(cat.photo);
self.clickCount= ko.observable(cat.clicks);
};
var ViewModel = function(){
var self = this;
self.data = ko.observableArray([]);
// data
cats.forEach(function(cat){
self.data.push(new CatRecord(cat));
}); // -- end of for Each
// view
self.currentCat = ko.observable(self.data()[0]);
self.setCurrentCat = function(catIndex){
self.currentCat(self.data()[catIndex]);
};
// actions
self.incrementClicks = function(){
var clickCount = self.currentCat().clickCount();
self.currentCat().clickCount(clickCount + 1);
};
};
ko.applyBindings(new ViewModel());
and the html:
<body>
<div id="catNames">
<ul id="catList" data-bind="foreach: data">
<li data-bind="text: catName, click:$parents[0].currentCat($index()) "></li>
</ul>
</div>
<div id="cat" data-bind="with: currentCat">
<h2 id="clicks" data-bind="text: clickCount"></h2>
<img id="photo" src="" alt="cat photo" data-bind=" click: $parent.incrementClicks,
attr: {src: imgSrc}">
<h4 id="name" data-bind="text: catName"></h4>
<button type="submit">Admin</button>
</div>
</body>
The issue is actually with the click binding inside foreach. It should be:
<ul id="catList" data-bind="foreach: data">
<li data-bind="text: catName, click:$parent.setCurrentCat"></li>
</ul>
The first parameter of setCurrentCat is the cat object which triggered the event. So you don't need the index. You can simply do this:
self.setCurrentCat = function(cat){
self.currentCat(cat);
};
I'm still not sure why you're getting the error for with binding.
Updated fiddle
I tried to make my Load More data when my page scroll to the bottom. The first thing is I make a div element that I put at the end of the data loop.
<div class="products">
<p>{{ status }}</p>
<div class="product" v-for="(item, index) in items">
<div>
<div class="product-image"><img :src="item.link" alt=""></div>
</div>
<div>
<h4 class="product-title">{{ item.title }}</h4>
<p>Price : {{ price }}</p>
<button class="add-to-cart btn" #click="addItem(index)">Add Item To Cart</button>
</div>
</div>
<div id="product-list-bottom"></div>
</div>
Div element with id product-list-bottom I will detect it using scrollMonitor.js
My default data :
data: {
status: 'Empty product',
total: 0,
items: [],
cart: [],
newSearch: 'anime',
lastSearch: '',
price: STATIC_PRICE,
result: []
}
Inside mounted I detected scroll to bottom :
mounted: function() {
this.onSubmit()
var vueInstance = this
var elem = document.getElementById('product-list-bottom')
var watcher = scrollMonitor.create(elem)
watcher.enterViewport(function() {
vueInstance.appendItems()
})
}
Inside mounted I call onSubmit :
onSubmit: function() {
this.items = ''
this.status = "Searching keyword '" + this.newSearch + "' on server ..."
this.$http.get('/search/'.concat(this.newSearch))
.then(function(response) {
this.lastSearch = this.newSearch,
this.status = 'Find ' + response.data.length + ' data'
this.result = response.data
this.appendItems()
})
}
And inside onSubmit I call appendItems function :
appendItems: function() {
if(this.items.length < this.result.length) {
var start = this.items.length
var end = parseInt(this.items.length + 5)
var append = this.result.slice(start, end)
this.items = this.items.concat(append)
console.log(append)
}
}
All goes well, but when I scroll down I get an error message :
This is because this line :
this.items = this.items.concat(append)
How do I make the data on xxx change (always added five new data from the array) according to the command on the line :
var end = parseInt(this.items.length + 5)
Thanks
it seems '/search/'.concat(this.newSearch) gets evaluated into function and not an actual string value
Try this if you are using babel/webpack
this.$http.get(`/search/`${this.newSearch}`)
Or if not
this.$http.get('/search/' + this.newSearch)
I think since Vue 2.3+ or so you can get this done without any jQuery stuff or any other dependencies:
<style>
.scrollbar{
overflow-y: scroll;
//...
}
.styled-scrollbar::-webkit-scrollbar
.styled-scrollbar::-webkit-scrollbar-thumb
.styled-scrollbar::-webkit-scrollbar-track{
//styling
}
</style>
<template>
//...
<div #scroll="scroll" class="scrollbar">
<div v-for="item in items" :key="item.id">
//TODO: item content
</div
</div>
//...
</template>
<script>
{
data: {
//..
lastScrollUpdate:0
}
//..
mounted: {
scroll:function (e) {
var scrollBar=e.target;
if((scrollBar.scrollTop + scrollBar.clientHeight >= scrollBar.scrollHeight-20)){
var t=new Date().getTime();
if((t-this.lastScrollUpdate)>3000){
this.lastScrollUpdate=t;
console.log('reached end: '+scrollBar.scrollTop+' '+scrollBar.clientHeight+' '+scrollBar.scrollHeight);
//TODO: load more data
}else{
console.log("< 3sec between scoll. no update");
}
}
},
//..
}
}
</script>
You may also want to adjust this to "#scroll.passive", in order to let the scroll-function be executed parallel to the UI (https://v2.vuejs.org/v2/guide/events.html#Event-Modifiers)
I'm having issues with my ember app when i build it on production, using ember serve all components work beautifully, but when I deploy it in my Digital Ocean droplet with Ubuntu 16.04 the app crashes with one component.
Here you had the code of the component that crashes:
import Ember from 'ember';
import pagedArray from 'ember-cli-pagination/computed/paged-array';
export default Ember.Component.extend({
init(){
this._super(...arguments);
this.send('fillQuestions');
},
didDestroyElement(){
this.send('reset');
},
last: false,
toggleModal: false,
aciertos: 0,
errores: 0,
contenido: Ember.computed('questions', function () {
let i = 0,
content = [],
contenido = [];
for (i; i < this.get('questions.length'); i++) {
content.push(this.get('questions.' + i + '.id_question_group.questions'));
}
contenido = [].concat.apply([], content);
return contenido;
}),
count: 0,
page: 1,
perPage: 1,
pagedContent: pagedArray('contenido', {
pageBinding: "page",
perPageBinding: "perPage"
}),
totalPagesBinding: "pagedContent.totalPages",
progressValue: 0,
color: Ember.computed('count', function () {
return new Ember.String.htmlSafe("width: " + this.get('progressValue') + "%;");
}),
actions: {
...(all my actions)
}
});
Inside my view I had this:
{{#if exam.show_progressbar}}
<div class="progress-bar progress-bar-info" role="progressbar" aria-valuenow={{progressValue}} aria-valuemin="0" aria-valuemax="100" style={{color}}>
<span>{{progressValue}}%</span>
</div>
{{/if}}
{{#if exam.show_time}}
{{countdown-timer autoStart='false' startTime=exam.duration action='saveresponse'}}
{{/if}}
{{#each pagedContent as |result index|}}
<div class="text-center">
<p>{{result.questionID.description}}</p>
<br/><br/>
</div>
<form {{action 'saveresponse' on="submit"}}>
{{radio-group group=result.questionID.temp elements=result.questionID.rows action="updateValues" nombre=result.questionID.description}}
<br/>
<div class="row">
<div class="column text-left">
<button type="button" {{action 'showAlert'}} class="btn btn-primary">
Cancelar
</button>
</div>
</div>
{{#if last}}
<div class="row">
<div class="col-md-12 col-sm-12 col-xs-12 text-right">
<div class="column text-right">
<button type="submit" class="btn btn-primary">
Guardar examen
</button>
</div>
</div>
</div>
{{/if}}
</form>
{{/each}}
<div class="pager">
{{page-numbers content=pagedContent currentPage=page action='checkLast'}}
</div>
The error its in the next components: {{countdown-timer}} and {{radio-group}}
The countdown-timer is a component based on ember-cli-timer which counts from a set time to 0 and the radio-group component only had inside a radio-button helper.
Any ideas of why in production is not working and locally it's working?
UPDATE 03-23-2017
Using the chrome developer tools I've got this error, maybe this will explain a little more my problem.
Property set failed: You passed an empty path
UPDATE 04-24-2017
I just had found the exact error in the component, it's the next action:
actions: {
...
fillQuestions(){
let questions = this.get('contenido'); //Filled with exam questions
for(let i = 0; i < questions.length; i++){
if(questions[i].questionID !== null && questions[i].questionID !== undefined){
console.log(questions[i].questionID.description); //consoles the description of the question
this.set(questions[i].questionID.description, ''); //here it's the problem.
}
}
}
...
}
The line with this.set() its making the problem, and it's because questions[i].questionID.description it's the empty path it's there a way to create new properties in the component with an action of the same?
I finally found the error, the error was displayed when the component tried to set the new property.
The real problem was:
when I attempted to save the property of the question some questions got "." and it seems to be that Ember.computed does not allow to use them for a property, so when I tried this.set('some.question.with.dots', '') the error was triggered and display the Property set failed.
The solution was simple, I only had to use .replace() function of javascript to replace all the dots in the string.
The final code is next:
actions: {
...
fillQuestions(){
let questions = this.get('contenido'); //Filled with exam questions
for(let i = 0; i < questions.length; i++){
if(questions[i].questionID !== null && questions[i].questionID !== undefined){
let desc = questions[i].questionID.description.replace(/\./g,'');
this.set(desc, ''); //the problem has gone
}
}
}
...
}
Thank to all of the people that support me with they points of view.
I have multiple questions generated within ractive.js loop. Each question has multiple answers with different prices. I need to calculate total price and recalculate it every time selected answers change. Need help with this.
I made this codepen: http://codepen.io/Nitoiti/pen/GjxXvo
<h1>Get total price</h1>
<div id='container'></div>
<script id='template' type='text/ractive'>
{{#each designQuestions}}
<p><span>{{id}} </span> {{question}}</p>
{{#each answers}}
<label><input type='radio' name='designQuestions-answers-{{id}}' value='{{price}}' {{#if selected}} checked {{/if}} >{{answer}} - {{price}}</label>
{{/each}}
{{/each}}
<table class="total">
<tr>
<td>total price:</td>
<td>{{total}}</td> <!-- how to calculate total price and change it on radio buttons change? -->
</tr>
</table>
</script>
<script src='http://cdn.ractivejs.org/latest/ractive.min.js'></script>
<script>
var ractive, designQuestions;
designQuestions = [
{ id: '1',
question: 'Question1 ?',
answers: [
{answer:'answer1',price:'222', selected: 'selected'},
{answer:'answer2',price:'553'},
{answer:'answer3',price:'22'},
{answer:'answer4',price:'442'}
]
},
{ id: '2',
question: 'Question2 ?',
answers: [
{answer:'answer1',price:'22'},
{answer:'answer2',price:'55', selected: 'selected'},
{answer:'answer3',price:'0'},
{answer:'answer4',price:'44'}
]
}
];
var ractive = new Ractive({
// The `el` option can be a node, an ID, or a CSS selector.
el: '#container',
// We could pass in a string, but for the sake of convenience
// we're passing the ID of the <script> tag above.
template: '#template',
// Here, we're passing in some initial data
data: {
designQuestions: designQuestions
}
});
</script>
Well' i've found solution myself.
<!doctype html>
<html>
<head>
<meta charset='utf-8'>
<title>Ractive test</title>
</head>
<body>
<h1>Калькулятор</h1>
<!--
1. This is the element we'll render our Ractive to.
-->
<div id='container'></div>
<!--
2. You can load a template in many ways. For convenience, we'll include it in
a script tag so that we don't need to mess around with AJAX or multiline strings.
Note that we've set the type attribute to 'text/ractive' - though it can be
just about anything except 'text/javascript'
-->
<script id='template' type='text/ractive'>
{{#each designQuestions}}
<p><span>{{id}} </span> {{question}}</p>
{{#each answers}}
<label><input on-change='calc' type='radio' name='{{select}}' value='{{selected}}' >{{answer}} - {{price}}</label>
{{/each}}
{{/each}}
<table class="total">
<tr>
<td>Итого:</td>
<td>{{sum}}</td>
</tr>
</table>
</script>
<script src='http://cdn.ractivejs.org/latest/ractive.min.js'></script>
<script>
var ractive, designQuestions;
designQuestions = [
{ id: '1',
question: 'Какой дизайн вы предпочитаете?',
answers: [
{answer:'уникальный и лично свой',price:222,selected:1},
{answer:'у меня уже есть готовый дизайн',price:553,selected:2},
{answer:'дизайн не нужен',price:22,selected:3},
{answer:'купим тему на themeforest',price:442,selected:4}
],
select:1
},
{ id: '2',
question: 'есть ли у вас наработки по дизайну?',
answers: [
{answer:'да, есть',price:22,selected:0},
{answer:'нет, ничего нет',price:55,selected:1},
{answer:'дизайн не нужен',price:0,selected:2},
{answer:'еще крутой ответ',price:44,selected:3}
],
select:1
}
];
var ractive = new Ractive({
// The `el` option can be a node, an ID, or a CSS selector.
el: '#container',
// We could pass in a string, but for the sake of convenience
// we're passing the ID of the <script> tag above.
template: '#template',
// Here, we're passing in some initial data
data: {
designQuestions: designQuestions,
sum:0
},
onrender:function(options)
{
var self = this;
// proxy event handlers
self.on(
{
calc: function (e)
{
calc();
}
});
calc();
function calc()
{
var arrayLength = designQuestions.length;
sum = 0;
for (var i = 0; i < arrayLength; i++)
{
var lengthans = designQuestions[i].answers.length;
for (var j = 0; j < lengthans; j++) {
if (designQuestions[i].answers[j].selected === designQuestions[i].select){
sum = sum + designQuestions[i].answers[j].price;
}
}
}
self.set('sum',sum);
}
}
});
</script>
</body>
</html>
I am developing one prototype application in ionic framework. I am newbie for angular js, HTML, CSS , Java Script and all this stuff.
I have one json file which I am using as an input. I am able to parse this Json file and able to get json object from this. This json object contains array of items. You can refer below json content for this. Here items are application A,B.....
Updated Input Json :
{
"data": [
{
"applicationname": "A",
"permissions": [
{
"text": "at"
},
{
"text": "at1"
}
]
},
{
"applicationname": "B",
"permissions": [
{
"text": "bt"
},
{
"text": "bt1"
}
]
}
]
}
When the application loads for the first time, application should load only the first item from above json array which means only application "A" (first item) data.
Once user clicks on any button (install/cancel) in Footer then it should changed its data and display application "B"'s contents. This process should continue till the end of json array.
My current code is not loading even the first item data in. Am I doing something wrong in HTML?
Updated Code :
HTML file :
<ion-header-bar class="bar-calm">
<h1 class="title">Application Permissions</h1>
</ion-header-bar>
<ion-nav-view name="home" ng-repeat="app in applicationdata">
<div class="bar bar-subheader bar-positive">
<h3 class="title"> {{app.applicationname }}</h3>
</div>
<ion-content class="has-subheader">
<div class="list" ng-controller="CheckboxController">
<ion-checkbox ng-repeat="item in app.permissions" ng-model="item.checked" ng-checked="selection.indexOf(item) > -1" ng-click="toggleSelection(item)">
{{ item.text }}
<h3 class="item-text-wrap"> details come soon </h3>
</ion-checkbox>
<div class="item">
<pre ng-bind="selection | json"></pre>
</div>
<div class="item">
<pre ng-bind="selection1 | json"></pre>
</div>
</div>
</ion-content>
<ion-footer-bar align-title="left" class="bar-light" ng-controller="FooterController">
<div class="buttons">
<button class="button button-balanced" ng-click="infunc()"> Install </button>
</div>
<h1 class="title"> </h1>
<div class="buttons" ng-click="doSomething()">
<button class="button button-balanced"> Cancel </button>
</div>
</ion-footer-bar>
</ion-nav-view>
app.js file :
pmApp.controller('CheckboxController', function ($scope, $http, DataService) {
// define the function that does the ajax call
getmydata = function () {
return $http.get("js/sample.json")
.success(function (data) {
$scope.applicationdata = data;
});
}
// do the ajax call
getmydata().success(function (data) {
// stuff is now in our scope, I can alert it
$scope.data = $scope.applicationdata.data;
$scope.devList = $scope.data[0].permissions;
console.log("data : " + JSON.stringify($scope.data));
console.log("first application data : " + JSON.stringify($scope.devList));
});
$scope.selection = [];
$scope.selection1 = [];
// toggle selection for a given employee by name
$scope.toggleSelection = function toggleSelection(item) {
var idx = $scope.selection.indexOf(item);
var jsonO = angular.copy(item);
jsonO.timestamp = Date.now();
DataService.addTrackedData(jsonO);
$scope.selection1 = DataService.getTrackedData();
// is currently selected
if (idx > -1) {
$scope.selection.splice(idx, 1);
}
// is newly selected
else {
DataService.addSelectedData(item);
$scope.selection = DataService.getSelectedData();
/* $scope.selection.push(item);*/
}
};
});
Problems :
1 : Why is the data of first item not getting loaded? I have done changes in HTML as per my understanding.
2 : How Can I navigate through all items. I will try #John Carpenter's answer. Before that first problem should be resolved.
Please help me, thanks in advance.
OK, so I'm not 100% sure what you want but I'll take a stab at it. In the future, it would be helpful to post less code (probably not the entire project you are working on). It is a good idea to make a simpler example than the "real" one, where you can learn what you need to learn and then go apply it to the "real" code that you have.
Anyways, this example is a simple button that you click on to change what is displayed.
var app = angular.module('MyApplication',[]);
app.controller('MyController', ['$scope', function($scope){
$scope.indexToShow = 0;
$scope.items = [
'item 1',
'item 2',
'item 3'
];
$scope.change = function(){
$scope.indexToShow = ($scope.indexToShow + 1) % $scope.items.length;
};
}]);
.simple-button {
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApplication" ng-controller="MyController">
<div ng-repeat="item in items track by $index" ng-show="$index == indexToShow">
{{item}}
</div>
<div class="simple-button" ng-click="change()">click me!</div>
</div>