how to use jQuery Isotope to filter jQuery Fancybox gallery - fancybox

I use FancyBox and Isotope on my personnal gallery website and I discover that FancyBox shows all pictures even if they are filtered by isotope.
I have a look on different solutions:
jQuery Isotope filtering with Fancybox
How to only show filtered images in fancybox when using isotope filters and multiple filters?
but no one works for me.
In my case, I have a js file to configure FancyBox, and FancyBox may be used without isotope, in other part of my gallery:
$(document).ready( function() {
$().fancybox({
selector : '[data-fancybox="images"]',
loop : true,
margin : [20, 0],
buttons : [
'thumbs',
'slideShow',
'close'
],
protect : true,
animationEffect : 'fade',
touch : {
vertical : false
},
slideShow : {
autoStart : false,
speed : 3000
},
clickContent : function( current, event ) {
return current.type === 'image' ? 'toggleControls' : false;
},
clickSlide : false,
clickOutside : false,
dblclickContent : function( current, event ) {
return current.type === 'image' ? 'next' : false;
},
caption : function( instance, item ) {
if ($(this).find('.caption').length) {
return $(this).find('.caption').html();
} else {
return $(this).attr('title');
};
},
mobile : {
thumbs : false,
idleTime : 3,
clickContent : function( current, event ) {
return current.type === 'image' ? 'toggleControls' : false;
},
dblclickContent : function( current, event ) {
return current.type === 'image' ? 'next' : false;
},
dblclickSlide : false,
},
});
});
my html and js code is a following
<div class="pager">
<div class="btn-group filters-button-group">
<button class="btn btn-default btn-sm active" data-filter="*">Toutes</button>
<button class="btn btn-default btn-sm" data-filter=".2010">2010</button>
<button class="btn btn-default btn-sm" data-filter=".2011">2011</button>
<button class="btn btn-default btn-sm" data-filter=".2012">2012</button>
</div>
</div>
<div id="isotope-wrap" class="margin-bottom-double">
<div class="gutter-sizer"></div>
<div class="image-item image-item-height2 2010">
<a class="thumb" href="/albums/fetes-des-lumieres-de-lyon/img_8743.jpg" title="Cathédrale Saint-Jean / 2012" data-fancybox="images">
<img src="/cache/fetes-des-lumieres-de-lyon/img_8743_w150_h235_cw150_ch235_thumb.jpg" alt="Cathédrale Saint-Jean / 2012" class="remove-attributes img-responsive" width="150" height="235" />
</a>
</div>
<div class="image-item image-item-width2 2011">
<a class="thumb" href="/albums/fetes-des-lumieres-de-lyon/img_2216.jpg" title="Fontaine Bartholdi / 2005" data-fancybox="images">
<img src="/cache/fetes-des-lumieres-de-lyon/img_2216_w235_h150_cw235_ch150_thumb.jpg" alt="Fontaine Bartholdi / 2005" class="remove-attributes img-responsive" width="235" height="150" />
</a>
</div>
<div class="image-item image-item-height2 2012">
<a class="thumb" href="/albums/fetes-des-lumieres-de-lyon/img_8709.jpg" title="Cathédrale Saint-Jean / 2012" data-fancybox="images">
<img src="/cache/fetes-des-lumieres-de-lyon/img_8709_w150_h235_cw150_ch235_thumb.jpg" alt="Cathédrale Saint-Jean / 2012" class="remove-attributes img-responsive" width="150" height="235" />
</a>
</div>
[...]
</div>
<script type="text/javascript">
// init Isotope after all images have loaded
var $containter = $('#isotope-wrap').imagesLoaded( function() {
$containter.isotope({
itemSelector: '.image-item',
layoutMode: 'packery',
// packery layout
packery: {
gutter: '.gutter-sizer',
}
});
});
// bind filter button click
$('.filters-button-group').on( 'click', 'button', function() {
var filterValue = $(this).attr('data-filter');
$containter.isotope({ filter: filterValue });
});
// change is-active class on buttons
$('.btn-group').each( function( i, buttonGroup ) {
var $buttonGroup = $(buttonGroup);
$buttonGroup.on( 'click', 'button', function() {
$buttonGroup.find('.active').removeClass('active');
$(this).addClass('active');
});
});
</script>
what can I do to dynamicaly configure FancyBox ot display only displayed pictures filtered by Isotope?
you can see my website and my issue in action here http://www.vincentbourganel.fr/fetes-des-lumieres-de-lyon/
Thanks for your help.

Tweak your selector so it returns only visible items, example:
selector : '.image-item:visible > a'
You can use any valid jQuery selector here.

Related

VueJs submit multiple rows array in form

I have a form, which has a vue componenet that allows users to create additional input lines in the form.
I am having an issue getting all of these input lines to submit for the axios request, currently it only outputs the last added rows input.
Typically, in a normal PHP form, I would just make the field an array (name="MultiRow[]"), however I am lost at doing this in Vue and struggling to find any doc that covers this.
Here is the component in my form:
<div class="mt-5 md:mt-0 md:col-span-2">
<fieldset class="mt-6">
<div class="mt-6">
<response-set-input v-model="fields.response"></response-set-input>
</div>
</fieldset>
</div>
Here is my Vue File for form submission:
<script>
import Switch from '../../components/StatusToggle';
export default {
data() {
return {
fields: {},
errors: {},
statusToggle: false,
}
},
methods: {
toggled(toggleOn){
statusToggle: toggleOn
},
submit() {
this.errors = {};
axios.post('/submit', this.fields).then(response => {
alert('Message sent!');
}).catch(error => {
if (error.response.status === 422) {
this.errors = error.response.data.errors || {};
}
});
},
},
components: {
statusToggle: Switch
}
}
</script>
Here is my component code:
<template>
<div>
<div class="m-2" v-for="(row, index) in rows" :key="index">
<div class="col-span-12 sm:col-span-3 mb-1">
<label for="responseTitle" class="block text-sm font-medium leading-5 text-gray-700">Response</label>
<input
id="responseTitle"
class="mt-1 form-input block w-full py-2 px-3 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:shadow-outline-blue focus:border-blue-300 transition duration-150 ease-in-out sm:text-sm sm:leading-5"
type="text"
name="fields.response[]"
:value="responseInput"
#input="onInput($event.target.value)"/>
</div>
<div class="mt-2">
<button type="button" class="inline-flex items-center px-2.5 py-1.5 border border-transparent text-xs leading-4 font-medium rounded text-gray-700 bg-green-100 hover:bg-indigo-50 focus:outline-none focus:border-indigo-300 focus:shadow-outline-indigo active:bg-indigo-200 transition ease-in-out duration-150" #click="addRow">
Add new Response
</button>
<button type="button" class="inline-flex items-center px-2.5 py-1.5 border border-transparent text-xs leading-4 font-medium rounded text-gray-700 bg-red-100 hover:bg-indigo-50 focus:outline-none focus:border-indigo-300 focus:shadow-outline-indigo active:bg-indigo-200 transition ease-in-out duration-150" #click="removeRow(index)">
Remove
</button>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['responseInput'],
data () {
return {
rows: [],
stopRemoval: true,
}
},
watch: {
rows () {
this.stopRemoval = this.rows.length <= 1
}
},
methods: {
onInput(responseInput){
this.$emit('input', responseInput),
console.log(responseInput)
},
addRow () {
let checkEmptyRows = this.rows.filter(row => row.number === null)
if (checkEmptyRows.length >= 1 && this.rows.length > 0) {
return
}
this.rows.push({
responseTitle: null,
})
},
removeRow (rowId) {
if (!this.stopRemoval) {
this.rows.splice(rowId, 1)
}
}
},
mounted () {
this.addRow()
}
}
</script>
How do I submit the multiple rows to the form submission with Vue?
There's a decent amount wrong with your code, I suggest that you read the documentation.
Just to name a few things:
You shouldn't update a prop in a component as it will get overridden when the parent updates, props: ['responseInput'], and :value="responseInput"
You're not passing any prop called responseInput, v-model passes a prop called value.
Vue is only reactive on properties that processed during instance initialisation and that means it doesn't know about response on fields: {},
You're using rows (which is good), but then you're only emitting the prop you passed in responseInput. I think :value="responseInput" is supposed to be :value="row"

Semantic UI Custom Rule Validation for Select Drop-Down Field

I am trying to add some custom logic to my semantic ui validation but can't figure out what I am doing wrong.
Basically, when the user selects "Yes" from the drop-down, I would like to make the "input_field" mandatory. If the user selects "No", the "input_field" becomes optional and the form can be submitted.
I tried searching for examples and followed some code from the Semantic Ui website but can't figure out what I am missing. Any advice would be appreciated as I am on a deadline for a project I am working on.
Form:
<div class="ui dimmer">
<div class="ui huge text loader">Loading</div>
</div>
<form method="post" action="" class="ui form" autocomplete="on">
<div class="ui segment">
<div class="ui two fields">
<div class='field'>
<div class="ui selection dropdown">
<input type="hidden" class="selectOption" name="select">
<i class="dropdown icon"></i>
<div class="default text">Select an option</div>
<div class="menu">
<div class="item" data-value="Yes">Yes</div>
<div class="item" data-value="No">No</div>
</div>
</div>
</div>
<div class="field">
<input id="input_field" name="input_field" type="text"/>
</div>
</div>
</div>
<button id="submit" class="ui green button" name="submit" type="submit">Submit</button>
</form>
Validation:
<script>
$('.ui.form').form({
keyboardShortcuts: false,
on: 'blur',
inline: 'true',
fields: {
selectOption: {
identifier: 'select',
rules: [
{
type: 'empty',
prompt: 'Please select an option'
}]
},
input_field: {
identifier: 'input_field',
depends: 'select',
rules: [
{
type: 'empty',
prompt: function() {
$('.select').on('change', function() {
if( this.value == 'Yes') {
return "Custom Validation";
}
return false;
}).trigger("change");
}
}]
}
},
onSuccess: function() {
$('.ui.dimmer').dimmer('show');
},
onFailure: function() {
event.preventDefault();
}
}
);
});
</script>
Figured out a solution for this! It might not be the best answer but it works and does what I am looking for.
<div class="ui dimmer">
<div class="ui huge text loader">Loading</div>
</div>
<form method="post" action="" class="ui form" autocomplete="on">
<div class="ui segment">
<div class="ui two fields">
<div class='field'>
<div class="ui selection dropdown">
<input type="hidden" class="selectOption" name="select">
<i class="dropdown icon"></i>
<div class="default text">Select an option</div>
<div class="menu">
<div class="item" data-value="Yes">Yes</div>
<div class="item" data-value="No">No</div>
</div>
</div>
</div>
<div class="field">
<input id="input_field" name="input_field" type="text"/>
</div>
</div>
</div>
<button id="submit" class="ui green button" name="submit" type="submit">Submit</button>
</form>
<script>
$('.ui.form').form({
keyboardShortcuts: false,
on: 'blur',
inline: 'true',
fields: {
selectOption: {
identifier: 'select',
rules: [
{
type: 'empty',
prompt: 'Please select an option'
}]
}
},
onSuccess: function() {
$('.ui.dimmer').dimmer('show');
},
onFailure: function() {
event.preventDefault();
}
}
);
$('.selectOption').on('change', function() {
if ( this.value == 'Yes' ) {
$('.ui.form').form('add rule', 'input_field', ['empty', 'integer']);
$('.ui.form').form('add prompt', 'input_field', 'Enter an integer');
}
if ( this.value == 'No' ) {
$('.ui.form').form('remove prompt', 'input_field');
$('.ui.form').form('remove rule', 'input_field');
}
}).trigger("change");
});
</script>
I was able to implement the validation rule by extending Semantic UI setting rules.
See below example:
$.fn.form.settings.rules.dependsOnFieldValue = function (value, dependFieldValue) {
var identifier = dependFieldValue.split('[')[0];
var dependValue = dependFieldValue.match(/\[(.*)\]/i)[1];
if( $('[data-validate="'+ identifier +'"]').length > 0 ) {
matchingValue = $('[data-validate="'+ identifier +'"]').val();
}
else if($('#' + identifier).length > 0) {
matchingValue = $('#' + identifier).val();
}
else if($('[name="' + identifier +'"]').length > 0) {
matchingValue = $('[name="' + identifier + '"]').val();
}
else if( $('[name="' + identifier +'[]"]').length > 0 ) {
matchingValue = $('[name="' + identifier +'[]"]');
}
return (matchingValue !== undefined)
? !( dependValue.toString() === matchingValue.toString() && value === '')
: false
;
};
Then in the form validation initializer you will pass the desired values as below:
$(".ui.form").form({
fields: {
select: {
identifier: 'select',
rules : [
{
type : 'empty'
}
]
},
input_field: {
identifier : 'input_field',
rules : [
{
type : 'dependsOnFieldValue[select[Yes]]',
}
]
},
...
}
});
Notice that we pass the <select> identifier (in this case also called select) within the first [] and then the value that we want to see to make the input_field mandatory ("Yes" in this case).

Ionic - show splash screen until the first image loads

I have an app in Ionic v.1 and on page where I have a list of articles, I would like to have a splash screen until the first image is completely loaded, not sure how to do that?
This is the controller:
module.exports = angular.module('coop.controllers')
.controller('ArticlesController', function($scope, ArticleService, $state, $ionicScrollDelegate, $location, $ionicPosition, $ionicConfig) {
$scope.articles = ArticleService.all();
$scope.$on('$ionicParentView.afterEnter', function(event, data) {
$scope.videoPlaying = [];
$ionicConfig.views.swipeBackEnabled(false);
if (data.direction == 'back') {
$scope.doRefresh();
}
});
$scope.articleType = 'all';
$scope.articleFilter = function(button) {
$scope.articleType = button;
$scope.doRefresh();
};
$scope.showBulletPoint = function(which) {
return $scope.articleType == which;
}
$scope.like = function(article){
article.userLiked = !article.userLiked;
ArticleService.like(article)
};
$scope.doRefresh = function (){
var articleType = $scope.articleType ? $scope.articleType : 'all';
ArticleService[articleType]().$promise.then(function(data){
$scope.articles = data;
}).finally(function() {
$scope.$broadcast('scroll.refreshComplete');
});
};
$scope.videoPlaying = [];
$scope.playerVars = {
controls: 0,
showinfo: 0
};
$scope.playVideo = function(youtubePlayer, index) {
$scope.videoPlaying[index] = true;
youtubePlayer.playVideo();
};
$scope.$on('$ionicView.afterLeave', function(event, data) {
$scope.videoPlaying = false;
$ionicConfig.views.swipeBackEnabled(true);
//youtubePlayer.stopVideo();
});
});
And this the html:
<ion-view>
<div class="row articles-header">
<button menu-toggle="left" class="button button-icon icon ion-navicon-round"></button>
<div class="right-icons">
<!--<a class="button button-icon icon ion-ios-search-strong">
</a>-->
<a class="button button-icon" href="#" ng-click="articleFilter('all')"><i class="ion-ios-circle-filled" ng-show="showBulletPoint('all')"></i> Siste
</a>
<a class="button button-icon" href="#" ng-click="articleFilter('video')"><i class="ion-ios-circle-filled" ng-show="showBulletPoint('video')"></i> Video
</a>
<a class="button button-icon" href="#" ng-click="articleFilter('popular')"><i class="ion-ios-circle-filled" ng-show="showBulletPoint('popular')"></i> Populært
</a>
</div>
</div>
<ion-content class="articles-content">
<ion-refresher pulling-icon="false" on-refresh="doRefresh()">
</ion-refresher>
<ion-list>
<ion-item ng-repeat="article in articles" class="item-light">
<div class="article">
<a ng-if="authenticated" ng-show="article.external_media.length == 0" ui-sref="main.article({id: article.id})" nav-direction="forward" class="article-image-link">
<img class="img" src="{{ fileServer }}/imagecache/cover/{{article.cover_image}}">
<h1>{{ article.title.split(' ', 7).join(' ') }}</h1>
</a>
<a ng-if="!authenticated" ng-show="article.external_media.length == 0" ui-sref="main.articlePublic({id: article.id})" nav-direction="forward" class="article-image-link">
<img class="img" src="{{ fileServer }}/imagecache/cover/{{article.cover_image}}">
<h1>{{ article.title.split(' ', 7).join(' ') }}</h1>
</a>
<a ui-sref="main.article({id: article.id})">
<div class="iframe" ng-show="article.external_media.length > 0 && article.external_media.image != ''">
<img class="img" ng-src="{{ article.external_media[0].image }}">
<h1>{{ article.title.split(' ', 7).join(' ') }}</h1>
<div class="iframe-overlay">
<div class="play">
<img class="playButton" src="icons/playRectangle.svg"/>
</div>
</div>
</div>
</a>
</div>
<div class="row article-meta" ng-class="{ 'has-comments': article.enable_comments }">
<a ng-click="like(article)" class="subdued col col-30">
<img class="social-images" ng-src="icons/{{ article.userLiked ? 'heart' : 'heart-outline' }}.svg"/> Lik
</a>
<a ui-sref="main.article({id: article.id, gotoComments: true })" class="subdued col col-60" ng-if="article.enable_comments">
<img class="social-images" src="icons/comment.svg"/> {{ article.commentCount }} Kommentarer
</a>
<a ui-sref="main.article({id: article.id})" nav-direction="forward" class="col col-10 article-link right">
<img class="social-images" src="icons/arrow.svg"/>
</a>
</div>
</ion-item>
</ion-list>
</ion-content>
</ion-view>
One approach is to attach an onLoad handler to the images, and when the first image (or any other specific image, all, etc) have loaded you can remove your splash screen.
To do this we'll create a directive to handle the onload event, based on this outstanding solution from Peter, combined with the $ionicLoading loader.
var app = angular.module('app', ['ionic'])
app.controller('appCtrl', function($scope, $timeout, $ionicLoading) {
// Setup the loader
$ionicLoading.show({
content: 'Loading',
animation: 'fade-in',
showBackdrop: true,
maxWidth: 200,
showDelay: 0
});
// Add a simple onLoad callback
$scope.onLoad = function (id) {
if (id === 0) {
$ionicLoading.hide();
}
}
$scope.items = [
{img: 'http://placehold.it/5000x8000/f9009a/ffffff'},
{img: 'http://placehold.it/5000x8000/f9009a/ffffff'},
{img: 'http://placehold.it/5000x8000/f9009a/ffffff'}
];
});
// The imageonload directive
app.directive('imageonload', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.bind('load', function() {
//call the function that was passed
scope.$apply(attrs.imageonload);
});
}
};
})
Then use the directive:
<ion-item ng-repeat="item in items" href="#">
<img ng-src="{{item.img}}" imageonload="onLoad({{$index}})" id="img-{{$index}}" />
</ion-item>
When the app loads the $ionicLoading screen will be shown until the first image emits the onload event, causing the $ionicLoading screen to be removed.
For a working demo, see this Ionic Play demo.
The loading screen may only be noticeable the first time you load the demo, and on subsequent loads you might need to do a hard refresh.

Meteor: Subscribed Colletion doesn't update automatically (Reactivity)

I'm facing the problem that adding data to a subscribed Collection doesn't automatically refresh the shown elements of a collection. If I add a new element the element show's up for a second and then disappears! Refreshing the browser (F5) and the new element shows up.
I put the subscription into Meteor.autorun but things kept beeing the same.
lists.html (client):
<<template name="lists">
<div class="lists col-md-12" {{!style="border:1px red solid"}}>
<!-- Checklist Adder -->
<form id="list-add-form" class="form-inline" role="form" action="action">
<div class="col-md-6">
<input class="form-control" id="list-name" placeholder="Neue Liste" required="required"/>
</div>
<button type="submit" class="btn btn-primary" id="submit-add">
<span class="glyphicon glyphicon-plus-sign"></span>
Neue Liste
</button>
</form>
<!-- Checklist Ausgabe -->
<ul>
<br/>
{{#each lists}}
<li style="position: relative;" id="{{this._id}}" data-id="{{_id}}" class="clickOnList">
<!--<input type="button" class="deleteLists" id="{{this._id}}" value="-" style="z-index: 999;"/> -->
<span id="{{this._id}}" data-id="{{_id}}" style="padding-left: 10px; vertical-align:middle;">{{this.name}}</span>
<form id="changerForm_{{_id}}" class="changeList-name-form" data-id="{{_id}}" style="visibility: hidden; position: absolute; top:0;">
<input id="changerText_{{_id}}" type="text" class="list_name" data-id="{{_id}}" value="{{this.name}}" />
</form>
{{#if ownerOfList this._id}}
<a data-toggle="modal" class="userForListModal" id="{{this.name}}" data-id="{{this._id}}" data-target="#userForListModal">
<span class="glyphicon glyphicon-user" id="{{this.name}}" style="color:black;"data-id="{{this._id}}"></span><span style="color:black;" id="{{this.name}}" data-id="{{this._id}}" style="font-size: small; vertical-align: super">{{memberCount this._id}}</span></a>
<div class="deleteLists" id="dLBtn_{{_id}}" data-id="{{this._id}}" style="float: right; padding-right: 5px; padding-top: 1px; visibility: hidden;">
<span class="deleteLists glyphicon glyphicon-minus-sign" data-id="{{this._id}}"></span>
</div>
{{else}}
<a class="userForListModal">
<span class="glyphicon glyphicon-user" style="color:black;"></span><span style="color:black;" style="font-size: small; vertical-align: super">{{memberCount this._id}}</span></a>
{{/if}}
<!-- <button type="submit" class="deleteLists btn btn-default btn-xs" id="dLBtn_{{_id}}" data-id="{{this._id}}" style="float: right;" > -->
</button>
</li>
{{/each}}
</ul>
</div>
<div class="modal fade" id="userForListModal" >
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title" id="userForListModalLabel"></h4>
</div></template>
<div class="modal-body col-md-12">
<div id="userForListModalUsers">
</div>
<p>Neuen Benutzer zur Checkliste hinzufügen:</p>
<form id="list-addUser-form" class="form-inline" role="form" action="action">
<div class="col-md-12">
<div class="col-md-6">
{{inputAutocomplete settings id="name-list-addUser" class="input-xlarge" placeholder="Benutzer Name" required="required"}}
</div>
<div class="col-md-6">
<button type="submit" class="btn btn-secondary" id="submit-list-addUser">
<span class="glyphicon glyphicon-plus-sign"></span>
Benutzer hinzufügen
</button>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<div id="userForListModalerrorMessage" style="color:red; display: none; text-align:left"></div><button type="button" class="btn btn-default" data-dismiss="modal">Schließen</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
</template>
<template name="userPill">
<span class="label" style="color:black">{{username}}</span>
lists.js (client):
Template.lists.lists = function(){ return Lists.find(); }
Lists = new Meteor.Collection("lists");
Deps.autorun(function() {
Meteor.subscribe('lists');
})
lists.js
var activeListName = "";
var activeListID = "";
Template.lists.lists = function()
{
return Lists.find();
}
Template.lists.memberCount = function(id)
{
var count = "";
Meteor.call("listMemberCount", id, function(error,result)
{
if (error) {
console.log("List not initialized:" + error.reason);
}
else
{
Session.set("countMember_"+id,result);
}
});
return Session.get("countMember_"+id);
}
Template.lists.ownerOfList = function(id)
{
return ( Meteor.userId() == Lists.findOne({_id : id}).owner);
}
Template.lists.settings = function()
{
return {
position: "top",
limit: 5,
rules: [
{
token: '',
collection: Meteor.users,
field: "username",
template: Template.userPill
}]
}
}
Template.lists.events({
'submit #list-add-form' : function(e, t) {
/* Checklisten ausgeben */
e.preventDefault();
var name = t.find('#list-name').value;
var id = new Meteor.Collection.ObjectID().valueOf();
var id_block = new Meteor.Collection.ObjectID().valueOf();
Lists.insert({_id : id, name : name, owner : Meteor.userId()});
Meteor.users.update({_id : Meteor.userId()}, {$addToSet :{lists : id}});
Listitems.insert({_id : id_block, name : "", items: []});
Lists.update(id, {$addToSet : {items : id_block}});
},
'click .clickOnList' : function(e)
{
/* Eventhandler fuer klick auf Checkliste */
Session.set("activeListId", e.target.id);
$("#"+e.target.id).siblings('li').removeClass("active");
$("#"+e.target.id).addClass("active");
},
'mouseover .clickOnList' : function (e,t) {
$( ".deleteLists" ).each(function( index, item ) {
if ( item.getAttribute("data-id") == e.target.getAttribute("data-id")) {
item.style.visibility = 'visible';
} else {
item.style.visibility = 'hidden';
}
});
},
'mouseleave .clickOnList' : function (e,t) {
$( ".deleteLists" ).each(function( index, item ) {
item.style.visibility = 'hidden';
});
},
'click .deleteLists' : function(e, t)
{
/* Eventhandler zum loeschen einer Checkliste */
var id = e.target.getAttribute("data-id");
Meteor.call("removeList", id);
console.log("test");
},
'click .changeListnameButton' : function(e,t) {
var id = e.target.getAttribute("data-id");
document.getElementById("changerForm_" + id).style.visibility = 'visible';
document.getElementById(id).style.visibility = 'hidden';
document.getElementById("changerText_" + id).focus();
},
'dblclick .clickOnList' : function(e,t){
var id = e.target.getAttribute("data-id");
document.getElementById("changerForm_" + id).style.visibility = 'visible';
document.getElementById(id).style.visibility = 'hidden';
document.getElementById("changerText_" + id).focus();
},
'submit .changeList-name-form' : function(e,t) {
e.preventDefault();
var id = e.target.getAttribute("data-id");
var text = document.getElementById("changerText_" + id).value;
if(text != '') {
Meteor.call("changeListName", id, text);
}
if (Session.get("activeListId", e.target.id) == id ) {
Session.set("activeListName", text);
}
document.getElementById("changerForm_" + id).style.visibility = 'hidden';
document.getElementById(id).style.visibility = 'visible';
},
'blur .list_name' : function(e,t) {
e.preventDefault();
var id = e.target.getAttribute("data-id");
var text = document.getElementById("changerText_" + id).value;
if((text != '') && (document.getElementById(id).style.visibility == 'hidden')) {
Meteor.call("changeListName", id, text);
}
if (Session.get("activeListId", e.target.id) == id ) {
Session.set("activeListName", text);
}
document.getElementById("changerForm_" + id).style.visibility = 'hidden';
document.getElementById(id).style.visibility = 'visible';
},
'click .userForListModal' : function(e,t) {
e.preventDefault();
activeListName = e.target.id;
activeListID = e.target.getAttribute("data-id");
//console.log(activeListID + " " + activeListName);
//console.log("New user for Liste" + Lists.findOne({_id : activeListID}).name);
userForList(activeListID);
$("#userForListModalLabel").html("Benutzer der Liste '"+ activeListName+ "'");
},
'submit #list-addUser-form' : function(e,t) {
e.preventDefault();
var newUser = $('#name-list-addUser').val();
Meteor.call("addUserToList", newUser, activeListID, function(error,result)
{
if (error) {
console.log(error.reason);
}
else
{
if (result == 1) {
$('#userForListModalerrorMessage').fadeIn(1000, function() {$(this).delay(1000).fadeOut(1000);});
$('#userForListModalerrorMessage').html("<div class=\"alert alert-danger\">Benutzer wurde nicht gefunden...</div>");
}
else if (result == 2) {
$('#userForListModalerrorMessage').fadeIn(1000, function() {$(this).delay(1000).fadeOut(1000);});
$('#userForListModalerrorMessage').html("<div class=\"alert alert-warning\">Benutzer ist Besitzer der Liste...</div>");
}
}
});
}
});
function userForList(id)
{
try
{
var owner = Lists.findOne({_id : id}).owner;
var members = Lists.findOne({_id : id}).member;
}
catch(e){
}
output = "<ul>";
output += "<li> Besitzer der Liste: <ul><li>" + owner + "</li></ul></li>";
output += "<li>Mitarbeiter der Liste: <ul>"
if (members != undefined) {
for(i=0; i<members.length; i++)
{
output+= "<li>" + members[i] + "</li>";
}
}
output += "</ul></li></ul>";
$('#userForListModalUsers').html(output);
}
main.js (server):
Lists = new Meteor.Collection("lists");
Meteor.publish("lists", function(){
var ListsOfUser = Meteor.users.findOne({_id : this.userId}).lists;
return Lists.find({_id :{ $in : ListsOfUser}});
});
Lists.allow({
insert : function(userID, list)
{
return (userID && (list.owner === userID));
},
//todo
update : function(userID)
{
return true;
},
//todo
remove : function(userID)
{
return true;
}
});
Thanks in advance!
I believe this is happening because the ListsOfUser variable in your Meteor.publish "lists" function is not a reactive data source. ListsOfUser is an array drawn from your result set, not a reactive cursor. Therefore it is not being invalidated server side when a user adds a new list on the client. From the Meteor docs (note the last sentence especially):
If you call Meteor.subscribe within a reactive computation, for example using
Deps.autorun,the subscription will automatically be cancelled when the computation
is invalidated or stopped; it's not necessary to call stop on subscriptions made
from inside autorun. However, if the next iteration of your run function subscribes
to the same record set (same name and parameters), Meteor is smart enough to skip a
wasteful unsubscribe/resubscribe.
ListsOfUser is not changing when a user adds a new list, so you are not being unsubscribed and resubscribed to the lists publication. (Note also that Meteor.users.findOne() is also not a reactive data source - you might want to switch it to Meteor.users.find() depending on how you go about making ListsOfUser reactive).
There are a few ways you could go about making the user lists reactive.
First, you could publish both the user cursor and the lists cursor, either separately or as an array in the same publish function, place both subscriptions in your Deps.autorun, and then fish out the user lists client side in a helper.
Meteor.publish("userWithLists", function(){
return Meteor.users.find(
{_id: this.userId},
{fields: {'lists': 1}}
);
});
Second, you could publish the static array of user lists as its own Collection and then use cursor.observe or cursor.observeChanges to track when it changes. While my understanding is that this is closest to the "correct" or "Meteor" way of doing it, it is also apparently quite verbose and I have not tried it. This tutorial goes into great detail about how you would tackle something like this: https://www.eventedmind.com/feed/aGHZygsphtTWELpKZ
Third, you could simply stick the user lists into your Session object, which is already reactive, and then publish your Lists.find() based on the Session, i.e.:
Meteor.publish("lists", function(lists){/* find code goes here */});
and
Deps.autorun(function(){
Meteor.subscribe("lists", Session.get("listsOfUser"));
});
This last one is probably an overuse / abuse of the Session object, particularly if your listsOfUser grows large, but should work as a hack.
I know this question is old, but still someone might be seeking for the answer. And the answer is Meteor.publishComposite available with a publish-composite package - https://atmospherejs.com/reywood/publish-composite
And there's an example there
Meteor.publishComposite('topTenPosts', {
find: function() {
// Find top ten highest scoring posts
return Posts.find({}, { sort: { score: -1 }, limit: 10 });
},
children: [
{
find: function(post) {
// Find post author. Even though we only want to return
// one record here, we use "find" instead of "findOne"
// since this function should return a cursor.
return Meteor.users.find(
{ _id: post.authorId },
{ limit: 1, fields: { profile: 1 } });
}
},
{
find: function(post) {
// Find top two comments on post
return Comments.find(
{ postId: post._id },
{ sort: { score: -1 }, limit: 2 });
},
children: [
{
find: function(comment, post) {
// Find user that authored comment.
return Meteor.users.find(
{ _id: comment.authorId },
{ limit: 1, fields: { profile: 1 } });
}
}
]
}
]
});
I am rather new to meteor but, in your server code, should it not be:
var ListsOfUser = Meteor.users.findOne({_id : Meteor.userId}).lists;
rather than:
var ListsOfUser = Meteor.users.findOne({_id : Meteor.userId}).lists;

How do I hide the parent element of one that triggered an action with jQuery?

I have this code in html (notice the numers 1 and 2in the outside and inside elements):
<div class="list_item" id="item_1">
<div class="list_item_int">My first line</div>
<div class="eval_buttons">
<div class="approve" id="1"></div>
<div class="dismiss" id="1"></div>
</div>
<div class="list_item" id="item_2">
<div class="list_item_int">My second line</div>
<div class="eval_buttons">
<div class="approve" id="2"></div>
<div class="dismiss" id="2"></div>
</div>
Then I have this script:
$().ready(function(){
$(".approve").click(function(){
$.post(
"php/judge_work.php",
{action : "1", work_info : this.id},
function(data){
$("#item_" + this.id).hide("slow");
}
);
});
$(".dismiss").click(function(){
$.post(
"php/judge_work.php",
{action : "0", work_info : this.id},
function(data){
alert(data);
}
);
});
});
Is there a way to make $("#item_" + this.id).hide("slow"); work? thanks on your kind response.
You can use .closest(), like this:
$(this).closest(".list_item") //gets the ancestor <div class="list_item">
In your case since this won't be the clicked element in the $.post() callback, it'll look like this:
$(".approve").click(function(){
var li = $(this).closest(".list_item");
$.post(
"php/judge_work.php",
{action : "1", work_info : this.id},
function(data){
li.hide("slow");
}
);
});