angular dragdrop drag across ng repeat elements - javascript

here is the plunkr which will best illustrate my problem.
https://plnkr.co/edit/VrT7TmK6tY2u4k2NYJ2U?p=preview
there are two panels the panel on the left has a list of items that you can drag and drop onto wells on the right. the wells are created with a ng repeat. unfortunately I can not drag from one well to the another well on the right side of the screen. not sure why.
<div ng-controller="oneCtrl"><!--one-->
<div class="span3" style='margin-left:10px;'>
<div class="thumbnail"
data-drop="true"
ng-model='list1'
data-jqyoui-options="optionsList1"
jqyoui-droppable="{multiple:true}">
<div class="alert alert-info btn-draggable"
ng-repeat="item in list1"
ng-show="item.title" data-drag="{{item.drag}}"
data-jqyoui-options="{revert: 'invalid'}"
ng-model="list1" jqyoui-draggable="{index: {{$index}},animate:true}"
><!--alert-->
{{item.title}}
</div><!--alert-->
</div>
</div>
<div class="span3" style='margin-left:150px;'>
<div class=thumbnail>
<div class="well"
ng-repeat = "org in orgs"
data-drop="true"
ng-model="org.list"
data-jqyoui-options="{accept:'.btn-draggable:not([ng-model=org.list])'}"
jqyoui-droppable="{multiple:true}">
<div class=" alert alert-success btn-draggable" ng-repeat="item in org.list"
ng-show="item.title"
data-drag="{{item.drag}}"
data-jqyoui-options="{revert: 'invalid'}"
ng-model="org.list"
jqyoui-draggable="{index: {{$index}},animate:true}">
{{item.title}}
</div>
</div>
</div>
</div>
</div><!--one-->
and the javascript
var App = angular.module('drag-and-drop', ['ngDragDrop']);
App.controller('oneCtrl', function($scope, $timeout) {
$scope.orgs = [
{name: 'org1', list: []},
{name: 'org2', list: []},
{name: 'org3', list: []},
]
$scope.list1 = [
{ 'title': 'Item 1', 'drag': true },
{ 'title': 'Item 2', 'drag': true },
{ 'title': 'Item 3', 'drag': true },
{ 'title': 'Item 4', 'drag': true },
{ 'title': 'Item 5', 'drag': true },
{ 'title': 'Item 6', 'drag': true },
{ 'title': 'Item 7', 'drag': true },
{ 'title': 'Item 8', 'drag': true }
];
});

That's because you have data-jqyoui-options="{accept:'.btn-draggable:not([ng-model=org.list])'}" for the repeater in the right panel. This selector is the same for all the generated droppable-zones and it controls which draggable elements are accepted by the droppable. You will be able to drag and drop the items between the generated zones, if you will remove this option (or modify it according to your requirements).
Working plnkr.

Related

How can I render two object in div?

I have 2 objects with data and I need to restruct the rendering in html.
This is what I get:
but this is what I need to obtain:
Black box is the *ngFor of articles object, respectively red one is *ngFor of adsContainer object
<div *ngFor="let article of articles; let i = index;">
<mat-card class="card">
<div>
<a class="title">
{{article.title}}
</a>
<a>
<p>{{article.content}}</p>
</a>
</div>
</mat-card>
<div class="container" *ngIf="(i % 7) === 6">
<div *ngFor="let data of adsContainer">
<div>
<h1>{{data.value}}</h1>
</div>
</div>
</div>
</div>
public adsContainer: any = [
{ id: '1', value: 'text value here' },
{ id: '2', value: 'text value here' },
{ id: '3', value: 'text value here' }
]
public articles: any = [
{ id: '1', title: 'title value here', content: 'content here' },
{ id: '2', title: 'title value here', content: 'content here' },
{ id: '3', title: 'title value here', content: 'content here' },
........
{ id: '20', title: 'title value here', content: 'content here' },
]
Your issue is that you add your ads containers in a loop
<div class="container" *ngIf="(i % 7) === 6">
<!-- this loop here -->
<div *ngFor="let data of adsContainer">
<div>
<h1>{{data.value}}</h1>
</div>
</div>
</div>
what you want is to add only one ad container at a time, in order to do that, you have to rmove the loop
In order to have the three ads show, you'll have to do some math to get the ad index from the article index
something like this :
<div class="container" *ngIf="(i % 7) === 6">
<div>
<h1>{{adsContainer[mathFloor(i / 7)].value}}</h1>
</div>
</div>
note that Math is not available in angular templates, you'll have to redefine floor as a component method
function mathFloor(val) {return Math.floor(val)}
Try to use directly the array , also when you call
adsContainer[(parseInt(i / 6)-1)% adsContainer.length, it will always restart the index:
<div *ngFor="let article of articles; let i = index;">
<mat-card class="card">
<div>
<a class="title">
{{article.title}}
</a>
<a>
<p>{{article.content}}</p>
</a>
</div>
</mat-card>
<div class="container" *ngIf="(i % 7) === 6">
<div>
<div>
<h1>{{adsContainer[(parseInt(i / 6)-1)%adsContainer.length].value}}</h1>
</div>
</div>
</div>
</div>

Javascript - loop to create list of cards

HTML and simple JS (no react / vue).
I need to do a list of cards and would like to do a loop to avoid repeating the html.
My current code :
<div id="products-cards-container">
<div class="products-cards">
<div class="product-header">
<img src="../assets/img/image1.png"/>
</div>
<div class="product-content">
<h4>title 1</h4>
<p>super content 1</p>
</div>
<button class="info-button">+ info</button>
</div>
<div>
<div class="product-header">
<img src="../assets/img/cards/products/image2.png"/>
</div>
<div class="product-content">
<h4>title 2</h4>
<p>super content 2</p>
</div>
<button class="info-button">+ info</button>
</div>
<div>
<div class="product-header">
<img src="../assets/img/cards/products/image-3.png"/>
</div>
<div class="product-content">
<h4>title 3</h4>
<p>blablablablbalbalbabla blablaba</p>
</div>
<button class="info-button">+ info</button>
</div>\
</div>
I am trying to return html
script.js
const valuesCards = [
{
image: '../img/image1.png',
title: 'title 1',
content: 'super content 1',
},
{
image: '../img/image2.png',
title: 'title 2',
content: 'super content 2'
},
{
image: '../img/image-3.png',
title: 'title3',
content: 'blablablablbalbalbabla blablaba'
},
]
creating a function that inserts the list of cards in the .products-cards div :
function returnCards() => {
----- AND HERE I AM STUCK
(as well, all tries I did with a simple array / object returned only the last info) ----
}
Use Array.prototype.map function and a template literal.
const container = document.getElementById('products-cards-container');
const valuesCards = [{
image: '../img/image1.png',
title: 'title 1',
content: 'super content 1',
},
{
image: '../img/image2.png',
title: 'title 2',
content: 'super content 2'
},
{
image: '../img/image-3.png',
title: 'title3',
content: 'blablablablbalbalbabla blablaba'
},
]
function returnCards(valuesCards) {
return "<div class=\"products-cards\">" + valuesCards.map(valuesCard => `
<div>
<div class="product-header">
<img src="${valuesCard.image}"/>
</div>
<div class="product-content">
<h4>${valuesCard.title}</h4>
<p>${valuesCard.content}</p>
</div>
<button class="info-button">+ info</button>
</div>`).join('') + "</div>";
}
container.innerHTML = returnCards(valuesCards);
<div id="products-cards-container"></div>
You can do it this way
const valuesCards = [
{
image: '../img/image1.png',
title: 'title 1',
content: 'super content 1',
},
{
image: '../img/image2.png',
title: 'title 2',
content: 'super content 2'
},
{
image: '../img/image-3.png',
title: 'title3',
content: 'blablablablbalbalbabla blablaba'
},
];
let cardHTML = '';
valuesCards.map(element => {
cardHTML += '<div> \
<div class="product-header"> \
<img src="'+element.image+'"/> \
</div> \
<div class="product-content"> \
<h4>'+element.title+'</h4> \
<p>'+element.content+'</p> \
</div> \
<button class="info-button">+ info</button> \
</div> \
';
});
document.getElementsByClassName('products-cards')[0].innerHTML = cardHTML;
<div id="products-cards-container">
<div class="products-cards">
</div>
</div>
const valuesCards = [
{
image: '../img/image1.png',
title: 'title 1',
content: 'super content 1',
},
{
image: '../img/image2.png',
title: 'title 2',
content: 'super content 2'
},
{
image: '../img/image-3.png',
title: 'title3',
content: 'blablablablbalbalbabla blablaba'
},
]
valuesCards.map(card=> {
var cardDiv = document.createElement('div');
cardDiv.innerHTML = `
<div class="product-header">
<img src="${card.image}"/>
</div>
<div class="product-content">
<h4>${card.title}</h4>
<p>${card.content}</p>
</div>
<button class="info-button">+ info</button>`
document.getElementsByClassName('products-cards')[0].appendChild(cardDiv);
})
<div id="products-cards-container">
<div class="products-cards">
</div>
</div>
You need to do a "for loop" that iterates over your valuesCards object and for each value return an html template an inject into the desired element in DOM.
For example:
// Define the object
const valuesCards = [
{
'image': '../img/image1.png',
'title': 'title 1',
'content': 'super content 1',
},
{
'image': '../img/image2.png',
'title': 'title 2',
'content': 'super content 2'
},
{
'image': '../img/image-3.png',
'title': 'title3',
'content': 'blablablablbalbalbabla blablaba'
}
]
// Identify the DOM element to store cards
cardsContainer = document.querySelector('#products-cards-container');
// Create a loop that iterates over valuesCards object
for (let value of Object.values(valuesCards)) {
console.log(value.title);
// Create an element to store new card content
let newCard = document.createElement('DIV');
// Add products-cards class to new element
newCard.classList.add('products-cards');
// Create a multiline string template with current values each time
cardHtml = `
<div class="">
<div class="product-header">
<img src="${value.image}"/>
</div>
<div class="product-content">
<h4>${value.title}</h4>
<p>${value.content}</p>
</div>
<button class="info-button">+ info</button>
</div>
`;
// Append the new element to the cardsContainer element in DOM
cardsContainer.appendChild(newCard);
// Inject the template html on DOM's new append item
newCard.innerHTML = cardHtml;
}
Important!! create a div element with the id="products-cards-container" in the html where you want to print the cards.

Controller data set another page in angular

After that clicked user.html list. I have to show clicked product data in productDetail.html file.
ProductController.js
$scope.selectedProduct = function(product)
{
console.log(product.post_title);
}
user.html
<div ng-repeat="p in users.products | filter:searchBox">
<a href="#/{{$index}}/{{$index}}" ng-mouseover="selectedProduct(p)" ng-click="selectedProduct(p)" style="text-decoration:none;">
<ion-item class="item widget uib_w_109 d-margins item-button-left" data-uib="ionic/list_item_button" data-ver="0">
{{p.post_title}} <br>
</ion-item>
</a>
</div>
productDetail.html
<div class="users">
<a>
<ion-item data-uib="ionic/list_item_button" data-ver="0">Product Name : {{clickedProduct.post_title}} <br>
Product Id : {{selected.product}}<br>
Post Date : {{product.post_title}}
</ion-item>
</a>
</div>
Need more Clarification, whether both HTML are shown in same view or different on product click.
here is a Plunker i made understanding your question and comments.
https://plnkr.co/edit/Ii52KZkjpNndmAMvQQYU?p=preview
$scope.products = [{
ID: 'P1',
post_title: 'product 1',
post_date: '20th July 2016',
post_author: 'John Doe'
}, {
ID: 'P2',
post_title: 'product 2',
post_date: '29th July 2016',
post_author: 'Jane Doe'
}, {
ID: 'P3',
post_title: 'product 3',
post_date: '12th July 2016',
post_author: 'John William'
}];
$scope.selectedProduct = {};
$scope.selectProduct = function(index){
$scope.selectedProduct = $scope.products[index];
};
and here is the HTML
<div>
Product List
<div ng-repeat="product in products">
<ion-item>
{{product.post_title}}
<button ng-click="selectProduct($index)">select</button>
<br>
</ion-item>
</div>
</div>
<div ng-include="'product.html'"></div>
the other HTML will pick the same angular controller.

How to drag and drop items to external container?

Basically, I have the following markup:
<script>
$(function() {
$('.box').on('mousedown', function(e) {
console.log(e);
});
});
</script>
<body ng-controller="MainCtrl">
<ul ui-sortable="sortableOptions" ng-model="list">
<li draggable ng-repeat="item in list">Item: {{item}}</li>
</ul>
<div class="boxes">
Drop to external area: <br/><br/>
<div class="box" style="background: red;"></div>
<div class="box" style="background: yellow;"></div>
<div class="box" style="background: orange;"></div>
</div>
</body>
Fiddle: http://plnkr.co/edit/xKw6sSbymA5M8R2v4OGF?p=preview
Now, I want to be able to drag and drop items from the list to that "external" container.
When user "drops" item to any of colored areas, I want to know that event just happend and which element is affected, as well
Already tried to listen for mousedown/mouseup events on each .box element, but it doesn't work as expected.
The library you are using is sorting library. You need to use drag-drop library for this.
For example, you can use https://github.com/codef0rmer/angular-dragdrop
Refer the docs for all available event callbacks.
Copying the example code snippet directly (for reference check here),
HTML:
<ul class="thumbnails">
<li style='margin-left:10px;' ng-repeat="item in list1">
<div class="btn"
data-drop="true"
ng-model='list1'
jqyoui-droppable="{index: {{$index}}}">
<div class="btn btn-info"
ng-show="item.title"
data-drag="{{item.drag}}"
data-jqyoui-options="{revert: 'invalid'}"
ng-model="list1"
jqyoui-draggable="{index: {{$index}},placeholder:true,animate:true}">
{{item.title}}
</div>
</div>
</li>
</ul>
<div class="container form-inline" style="text-align: center;">
<div class="btn"
ng-repeat="item in list2 | orderBy : 'title'"
data-drop="true"
ng-model='list2'
jqyoui-droppable="{index: {{$index}}, applyFilter: 'filterIt'}">
<div class="btn btn-info"
data-drag="{{item.drag}}"
data-jqyoui-options="{revert: 'invalid'}"
ng-model="list2"
jqyoui-draggable="{index: {{$index}},animate:true, applyFilter: 'filterIt'}"
ng-hide="!item.title">
{{item.title}}
</div>
</div>
</div>
Controller:
App.controller('YourCtrl', function($scope, $filter) {
$scope.filterIt = function() {
return $filter('orderBy')($scope.list2, 'title');
};
$scope.list1 = [];
$scope.list2 = [
{ 'title': 'Item 3', 'drag': true },
{ 'title': 'Item 2', 'drag': true },
{ 'title': 'Item 1', 'drag': true },
{ 'title': 'Item 4', 'drag': true }
];
angular.forEach($scope.list2, function(val, key) {
$scope.list1.push({});
});
});
Hope this solves your problem.
Try replace the 'mousedown' with 'drop'.

MeteorJs and Masonry

I am using MeteorJS (So great :) ), to develop simple app. I want to use masonry, so I am using sjors:meteor-masonry package.
When I use this code everything works fine:
var itemsData = [
{
title: 'First item',
description: 'Lorem 1',
price: 20
},
{
title: 'Secounde item',
description: 'Lorem 2',
price: 40
},
{
title: 'Third item',
description: 'Lorem 3',
price: 10
},
{
title: 'Fourth item',
description: 'Lorem 4',
price: 10
},
{
title: 'Five item',
description: 'sit 4',
price: 10
}
];
Template.itemsList.helpers({
items: itemsData
});
Template.itemsList.rendered = function() {
var container = document.querySelector('#main');
var msnry = new Masonry( container, {
// options
columnWidth: 200,
itemSelector: '.item'
});
};
But when I change part (code) for Template.itemsList.rendered to masonry don't work:
Template.itemsList.helpers({
items: function() {
return Items.find();
}
});
Any ideas ?
EDIT
myapp/lib/collections/items.js
Items = new Mongo.Collection('items');
And it is populated whit data from mongoshell. Data is ok, but masonry dont work.
EDIT 2
Masonry stops animating on screen resize and grid is not working as it should. No errors.
myapp/client/templates
<template name="itemSingle">
<div id="profile-widget" class="panel item">
<div class="panel-heading">
</div>
<div class="panel-body">
<div class="media">
<div class="media-body">
<h2 class="media-heading">{{title}}</h2>
{{description}}
</div>
</div>
</div>
<div class="panel-footer">
<div class="btn-group btn-group-justified">
<a class="btn btn-default" role="button"><i class="fa fa-eye"></i> 172</a>
<a class="btn btn-default" role="button"><i class="fa fa-comment"></i> 34</a>
<a class="btn btn-default highlight" role="button"><i class="fa fa-heart"></i> 210</a>
</div>
</div>
</div>
</template>
<template name="itemsList">
<div id="main">
{{#each items}}
{{> itemSingle}}
{{/each}}
</div>
</template>
I encountered the same problem, without never finding the perfect solution.
I tried to resolve with the setTimout() workaround :
Template.main.rendered = function() {
setTimeout(function() {
$('#container').masonry({
columnWidth: 35,
itemSelector: '.thumb',
gutter: 10,
isFitWidth: false
});
}, 1);
}

Categories

Resources