I'm using the Siema carousel on my site with Zepto. I'd like to be able to indicate what slide the user is currently on. How do I do this if there is only an onChange event available?
HTML
<section class="images">
<img/>
<img/>
</section>
<section class="indicators">
<span class="active"></span>
<span></span>
</section>
JS
$(document).ready(function() {
new Siema({
selector: '.images',
onChange: () => {
console.log("swiped");
// change active indicator?
},
});
});
I think I can help (I'm the author of Siema).
// extend a Siema class and add addDots() & updateDots() methods
const mySiemaWithDots = new SiemaWithDots({
// on init trigger method created above
onInit: function(){
this.addDots();
this.updateDots();
},
// on change trigger method created above
onChange: function(){
this.updateDots()
},
});
https://codepen.io/pawelgrzybek/pen/boQQWy
Have a lovely day 🥑
Related
I have input which I use to filter my array of objects in Vue. I'm using Salvattore to build a grid of my filtered elements, but it doesn't work too well. I think I have to call rescanMediaQueries(); function after my v-model changes but can't figure how.
Here is my Vue instance:
var articlesVM = new Vue({
el: '#search',
data: {
articles: [],
searchInput: null
},
ready: function() {
this.$http.get('posts').then(function (response) {
this.articles = response.body;
});
}
});
And here is how I have built my search
<div class="container" id="search">
<div class="input-field col s6 m4">
<input v-model="searchInput" class="center-align" id="searchInput" type="text" >
<label class="center-align" for="searchInput"> search... </label>
</div>
<div id="search-grid" v-show="searchInput" data-columns>
<article v-for="article in articles | filterBy searchInput">
<div class="card">
<div class="card-image" v-if="article.media" v-html="article.media"></div>
<div class="card-content">
<h2 class="card-title center-align">
<a v-bind:href="article.link">{{ article.title }}</a>
</h2>
<div class="card-excerpt" v-html="article.excerpt"></div>
</div>
<div class="card-action">
<a v-bind:href="article.link"><?php _e('Read More', 'sage'); ?></a>
</div>
</div>
</article>
</div>
I did get the grid system working by adding watch option to my Vue, but every time I wrote something to my input and then erase it my filterBy method wouldn't work at all. It didn't populate any data even if I tried to retype the same keyword as earlier. Here is the watch option I used:
watch: {
searchInput: function (){
salvattore.rescanMediaQueries();
}
}
I think your problem is with the scoping of this in your success handler for http. Your articles object in Vue component is not getting any values from your http.get(..) success handler.
Inside your ready function, your http success handler should be as follows:
this.$http.get('posts').then(response => {
this.articles = response.body; // 'this' belongs to outside scope
});`
Alternatively you can also do:
var self = this; // self points to 'this' of Vue component
this.$http.get('posts').then(response => {
self.articles = response.body; // 'self' points to 'this' of outside scope
});`
Another similar issue: https://stackoverflow.com/a/40090728/654825
One more thing - it is preferable to define data as a function, as follows:
var articlesVM = new Vue({
el: '#search',
data: function() {
return {
articles: [],
searchInput: null
}
},
...
}
This ensures that your articles object is unique to this instance of the component (when you use the same component at multiple places within your app).
Edited after comment #1
The following code seems to work alright, the watch function works flawlessly:
var vm = new Vue({
el: '#search',
template: `<input v-model="searchInput" class="center-align" id="searchInput" type="text" >`,
data: {
searchInput: ""
},
watch: {
searchInput: function() {
console.log("searchInput changed to " + this.searchInput);
}
}
})
The input in template is an exact copy of your version - I have even set the id along with v-model, though I do not see the reason to set an id
Vue.js version: 2.0.3
I am unable to see any further, based on details in the question. Can you check if your code matches with the one above and see if you can get the console debugging messages?
Edited after comment #4, #5
Here is another thought which you need to verify:
Role of vue.js: Render the DOM
Role of salvattore plugin: Make the DOM layouts using CSS only
Assuming the above is true for salvattore plugin, and hopefully it does not mess with vue.js observers / getters / setters, then you can do the following: provide a time delay of about 50 ms so that vue.js completes the rendering, and then call the salvattore plugin to perform the layouts.
So your watch function needs to be as follows:
watch: {
searchInput: function (){
setTimeout(function(){
salvattore.rescanMediaQueries();
}, 50);
}
}
Alternatively you may also use Vue.nexttick() as follows:
Vue.nextTick(function () {
// DOM updated
})
The nextTick is documented here: https://vuejs.org/api/#Vue-nextTick
I do not know if you may need to provide a little bit of extra time for salvattore plugin to start the layouts, but one of the above should work out.
Let me know if it works!
In my Angular app, we have a bootstrap carousel (using bootstrap carousel rather than ui bootstrap carousel for some reasons), items structure as follows
<div class="item" analytics-on analytics-event="IMPRESSIONS" analytics-category="{{--}}" analytics-label="{{--}}" ng-repeat="banner in vm.bannerList">
<a ng-href="{{--}}" analytics-on analytics-event="CLICK" analytics-category="{{--}}" analytics-label="{{--}}">
<div class="fill" style="background-image: url({{--}});"></div>
</a>
</div>
The click event working fine. But how to track the IMPRESSIONS. The impression event need to trigger when a carousel item becomes active.
I tried to watch the 'active' class using a custom directive but the watch only worked on load time.
Tried and succeeded,
Following custom directive did the job.
//ng-track-carousel-impressions
angular.module('app').directive('ngTrackCarouselImpressions', ['$analytics',function (analytics) {
return {
restrict: 'A',
link: function (scope, element, attrs, controller) {
// create an observer instance
var observer = new MutationObserver(function (mutations) {
scope.$apply(function () {
if (element.hasClass('active')) {
//console.log(element.attr('analytics-label'));
// emit event track (with category and label properties for GA)
analytics.eventTrack(element.attr('analytics-event'), {
category: element.attr('analytics-category'), label: element.attr('analytics-label')
});
}
});
});
// configuration of the observer:
var config = {
attributes: true
};
// pass in the target node, as well as the observer options
var node = element.get(0);
observer.observe(node, config);
}
}
}]);
Usage
<div class="item" analytics-event="IMPRESSIONS" analytics-category="{{--}}" analytics-label="{{--}}" ng-repeat="banner in vm.bannerList" ng-track-carousel-impressions>
<a ng-href="{{--}}" analytics-on analytics-event="CLICK" analytics-category="{{--}}" analytics-label="{{--}}">
<div class="fill" style="background-image: url({{--}});"></div>
</a>
</div>
I've got a Marionette layout and for demo purposes the html looks like:
<header id="header-region" class="page-title"></header>
<section id="template-content" class="full-section">
<div id="error-messages" class="fade main-section"><!-- errors --></div>
<div id="content-region"> </div>
</section>
Its layout view's regions are:
regions: {
header: "#header-region",
content: "#content-region"
}
Up until now, I've had any given page's modal html inside the page's template html which would be contained in the content region.
I have an idea to now create a separate region for modals to be shown in.
Changing things to look like this:
Template:
<section id="template-content" class="full-section">
<div id="error-messages" class="fade main-section"><!-- errors --></div>
<div id="content-region"> </div>
<div id="modal-region"></div>
</section>
And the region:
regions: {
header: "#header-region",
content: "#content-region",
modal: "#modal-region"
}
So I'd like to be able to do something like this:
// Controller
define([], function(){
initialize: function(){},
showHeaderView: function(){
this.HeaderView = new HeaderView();
this.layout.header.show(this.HeaderView);
},
showContentView: function(){
// this.BodyView's template used to contain the modal html
this.BodyView = new BodyView();
this.layout.content.show(this.BodyView);
},
showModalView: function(){
this.ModalView = new ModalView();
this.layout.modal.show(this.ModalView);
}
});
This works and renders the modal properly but the modal's events are lost because they were originally set by this.BodyView.
The modal has a checkbox that on change runs a function that is on this.BodyView but I want to bind the events for this.ModalView from this.BodyView.
How can I accomplish that? I've tried making this.ModalView's el the same as this.BodyView's but that breaks things. I've tried to use delegateEvents as well but with no luck.
This screencast does exactly what you want: http://www.backbonerails.com/screencasts/building-dialogs-with-custom-regions
Code is here: https://github.com/brian-mann/sc01-dialogs
If you are having the HeaderView as ItemView(or CollectionView/CompositeView) in it, you can instantiate it with passing arguments like
new HeaderView({events:{
"click .x" : function(){} // your function in-line or reference
});
So same applies to ModalView.
I am quite new with Meteor but have really been enjoying it and this is my first reactive app that I am building.
I would like to know a way that I can remove the .main element when the user clicks or maybe a better way would be to remove the existing template (with main content) and then replace with another meteor template? Something like this would be simple and straightforward in html/js app (user clicks-> remove el from dom) but here it is not all that clear.
I am just looking to learn and for some insight on best practice.
//gallery.html
<template name="gallery">
<div class="main">First run info.... Only on first visit should user see this info.</div>
<div id="gallery">
<img src="{{selectedPhoto.url}}">
</div>
</template>
//gallery.js
firstRun = true;
Template.gallery.events({
'click .main' : function(){
$(".main").fadeOut();
firstRun = false;
}
})
if (Meteor.isClient) {
function showSelectedPhoto(photo){
var container = $('#gallery');
container.fadeOut(1000, function(){
Session.set('selectedPhoto', photo);
Template.gallery.rendered = function(){
var $gallery = $(this.lastNode);
if(!firstRun){
$(".main").css({display:"none"});
console.log("not");
}
setTimeout(function(){
$gallery.fadeIn(1000);
}, 1000)
}
});
}
Deps.autorun(function(){
selectedPhoto = Photos.findOne({active : true});
showSelectedPhoto(selectedPhoto);
});
Meteor.setInterval(function(){
selectedPhoto = Session.get('selectedPhoto');
//some selections happen here for getting photos.
Photos.update({_id: selectedPhoto._id}, { $set: { active: false } });
Photos.update({_id: newPhoto._id}, { $set: { active: true } });
}, 10000 );
}
If you want to hide or show an element conditionaly you should use the reactive behavior of Meteor: Add a condition to your template:
<template name="gallery">
{{#if isFirstRun}}
<div class="main">First run info.... Only on first visit should user see this info.</div>
{{/if}}
<div id="gallery">
<img src="{{selectedPhoto.url}}">
</div>
</template>
then add a helper to your template:
Template.gallery.isFirstRun = function(){
// because the Session variable will most probably be undefined the first time
return !Session.get("hasRun");
}
and change the action on click:
Template.gallery.events({
'click .main' : function(){
$(".main").fadeOut();
Session.set("hasRun", true);
}
})
you still get to fade out the element but then instead of hiding it or removing it and having it come back on the next render you ensure that it will never come back.
the render is triggered by changing the Sessionvariable, which is reactive.
I think using conditional templates is a better approach,
{{#if firstRun }}
<div class="main">First run info.... Only on first visit should user see this info.</div>
{{else}}
gallery ...
{{/if}}
You'll have to make firstRun a session variable, so that it'll trigger DOM updates.
Meteor is reactive. You don't need to write the logic for redrawing the DOM when the data changes. Just write the code that when X button is clicked, Y is removed from the database. That's it; you don't need to trouble yourself with any interface/DOM changes or template removal/redrawing or any of that. Whenever the data that underpins a template changes, Meteor automatically rerenders the template with the updated data. This is Meteor’s core feature.
I have been looking at this for quite few hours and I don't think I am able to see the solution.
This is my router.js:
define('router', ['jquery', 'config', 'nav','store'], function ($, config, nav, store) {
var
concepTouch = Sammy('body', function () {
// This says to sammy to use the title plugin
this.use(Sammy.Title);
this.use(Sammy.Mustache);
// Sets the global title prefix
this.setTitle(config.title.prefix);
// So I can access sammy inside private methods
var sammy = this;
function establishRoutes() {
// Defines the main container for content then
var mainConainer = $(config.mainContentContainerId);
// Adds animation loading class to the main container
mainConainer.addClass(config.loadingAnimationCssClass);
// iterates through routes defined in config class then
_.forEach(config.appRoutes, function(obj, key) {
// defines each one as a route
sammy.get(obj.hashV, function(context) {
// Store the requested route as the last viewed route
store.save(config.stateKeys.lastView, context.path);
// Fetches its html template
context.render(obj.tmpltURL, { 'routeData': context.params })
// Appends that htmlo template to the main container and removes loading animation
.then(function(content) {
mainConainer.removeClass(config.loadingAnimationCssClass).html(content);
});
// Finally adds the route title to the prefix
this.title(obj.title);
});
// Overriding sammy's 404
sammy.notFound = function () {
// toast an error about the missing command
toastr.error(sammy.getLocation() + ' Does not exist yet!');
// Go to last visited anf if not
sammy.setLocation(
store.fetch(config.stateKeys.lastView) || config.getDefaultRoute()
);
};
});
}
// Calls for routes to be established
establishRoutes();
}),
// runs concep touch as a sammy App with the initial view of default route
init = function () {
// Try to get today's last visit and if not available then fallback on default
concepTouch.run(store.fetch(config.stateKeys.lastView) || config.getDefaultRoute());
// Make the correct nav item active and add Click handlers for navigation menu
nav.setStartupActiveClass(store.fetch(config.stateKeys.lastView) || sammy.getLocation())
.addActiveClassEventHandlers();
};
return {
init: init,
concepTouch: concepTouch
};
});
This when I submit the search form gets this template for me:
<div id="contacts" class="view animated fadeInLeft">
<h3>Search results for {{routeData}}</h3>
<ul data-bind="template: { name: 'searchresults-template', foreach: searchResults }"></ul>
</div>
<script type="text/html" id="searchresults-template">
<li data-bind="text: type"></li>
</script>
<script>
require(['searchresults'], function (searchresults) {
searchresults.get(to Some how Get routeData.term);
});
</script>
and I can not find the right way to make Mustache pass the data from this line of router.js context.render(obj.tmpltURL, { 'routeData': context.params }) to the {{routeData.term}} inside the template.
{{routeData}} on its own returns `SAMMY.OBJECT: {"TERM": MY SEARCH TERM}`
which I can't navigate to the property i want to from it using . notation. Furthermore even if that worked it can not be passed into Javascript which is what I really need as
searchresults.init(); is waiting for this paramter `searchresults.init(routeData.term);`
Or maybe the answer is to find a way to access sammy's context here? outside of sammy in order to get the params? something like Sammy.Application.context.params['term'] but ofcourse application has no such method so don't know!? :(
Am I going totally the wrong way about it? How Can I easily pass the query string params as accessible objects inside my template so knockout can use it.
Your help is greatly appreciated.
<div id="contacts" class="view animated fadeInLeft">
<h3>Search results for {{#routeData}}{{term}}{{/routeData}}</h3>
<ul data-bind="template: { name: 'searchresults-template', foreach: searchResults }"></ul>
</div>
<script type="text/html" id="searchresults-template">
<li data-bind="text: type"></li>
</script>
<script>
require(['searchresults'], function (searchresults) {
var searchTerm = "{{#routeData}}{{term}}{{/routeData}}";
searchresults.get(searchTerm);
});
</script>