Ractive.js: Ractive.extend() global data - ractivejs

I try to add a button to a menu with Ractive.js and on click on this button sidebar should be opened.
js:
var ractive_setup = Ractive.extend({
data: {
data: {
sidebar: false,
}
},
onrender: function ( options ) {
this.on({
sidebar: function () {
this.toggle('sidebar');
console.log ( this.get('sidebar') );
}
});
}
});
var ractive_sidebar_open = new ractive_setup({
template: '#sidebar-open',
el: '[data-ractive=sidebar-open]'
});
var ractive_sidebar = new ractive_setup({
template: '#sidebar',
el: '[data-ractive=sidebar]'
});
html:
<nav data-ractive="sidebar-open"></nav>
<script id="sidebar-open" type="text/ractive">
<button class="open" on-click="sidebar">open sidebar</button>
</script>
<aside data-ractive="sidebar"></aside>
<script id="sidebar" type="text/ractive">
{{ #if sidebar }}
<button on-click="sidebar">close sidebar</button>
<div class="sidebar-content">sidebar content</div>
{{ /if sidebar }}
</script>
On button.open click, data changes only for one instance of ractive_setup—for the first one.
How to modify Ractive data globally, for both ractive_setup instances?

You need to declare your data-object outside, and pass it into both instances of your ractive_setup. With magic mode (docs) option on, both instances will then re-render when your data is modified.
Like this:
var ractive_setup = Ractive.extend({
magic: true, //Magic mode to ensure re-render when data is modified
onrender: function ( options ) {
this.on({
sidebar: function () {
this.toggle('sidebar');
console.log ( this.get('sidebar') );
}
});
}
});
//declaring your data-object outisde, so you can pass it into both instances
var dataObj = {
sidebar: false
};
var ractive_sidebar_open = new ractive_setup({
template: '#sidebar-open',
el: '[data-ractive=sidebar-open]',
data: dataObj //passing in data to the first instance
});
var ractive_sidebar = new ractive_setup({
template: '#sidebar',
el: '[data-ractive=sidebar]',
data: dataObj //passing in data to the second instance
});
I've created a working fiddle of your example here: http://jsfiddle.net/08huhfar/3/

Related

WordPress with select2 and Toastr get previous selected value destroys select2 behavior

I am using WordPress and I have used Select2 and Toastr libraries successfully.
Basically I have a dropdown and if I change, Toastr will ask whether I need to update or not.
If I click on "Yes" then it will update and if I click on "No" then my dropdown should set previous value and nothing will happen.
Currently its selecting previous value but then if I open the same dropdown try to click on it to search then its saying "The results could not be loaded".
Here is my JS code what I have done so far.
var prevSubVarClientId;
jQuery('.mySubscription').select2({
allowClear: true,
placeholder: "",
//minimumInputLength: 3,
ajax: {
type: "POST",
url: '/wp-admin/admin-ajax.php',
dataType: 'json',
delay: 250, // delay in ms while typing when to perform a AJAX search
data: function (params, page) {
return {
action: 'list_posts',
q: params.term,
};
},
processResults: function( data ) {
var options = [];
if ( data ) {
jQuery.each( data, function( index, text ) {
options.push( { id: text['id'], text: text['name'] } );
});
}
return {
results: options
};
},
cache: true
}
});
jQuery('.mySubscription').on('select2:selecting', function (evt) {
prevSubVarClientId = jQuery('select').val();
});
jQuery('.mySubscription').change(function() {
var $this = jQuery(this);
jQuery(this).blur();
alertify.confirm("Are you sure you want to transfer?",
function(e){
var subscriptionId = jQuery($this).data("subscription-id");
var url = jQuery($this).data("ajax-url");
var userId = jQuery($this).val();
jQuery.ajax({
type: "POST",
url: url,
data : {
action : 'update_var_client_user_id',
userId : userId,
subscriptionId : subscriptionId
},
success: function(data)
{
var data = JSON.parse(data);
toastr["success"]("Transferred Successfully." );
}
});
},
function(){
jQuery($this).val(prevSubVarClientId);
jQuery($this).select2().trigger('change');
}).set({title:"Alert!!!"}).set({ labels:{ok:'Yes', cancel: 'No'} });
});
As you can see I have prevSubVarClientId variable and mySubscription dropdown with this class.
jQuery('.mySubscription').change(function() { here you can see I am opening alertify confirm box and if I click on "No" then I am doing below code.
jQuery($this).val(prevSubVarClientId);
jQuery($this).select2().trigger('change');
But then whenever I am trying to open the dropdown again, I am getting the below error.
Can some one guide me, what I am doing wrong here ?
Thanks
"The results could not be loaded". only show when return data is null or not found.
I tested your code below snippet and working fine.
$(".js-data-example-ajax").select2();
jQuery('.js-data-example-ajax').on('select2:selecting', function (evt) {
prevSubVarClientId = jQuery('select').val();
});
jQuery('.js-data-example-ajax').change(function() {
var $this = jQuery(this);
jQuery(this).blur();
alertify.confirm("Are you sure you want to transfer?",
function(e){
console.log('change');
},function(){
console.log('no change');
jQuery($this).val(prevSubVarClientId);
jQuery($this).select2().trigger('change');
}).set({title:"Alert!!!"}).set({ labels:{ok:'Yes', cancel: 'No'} });
});
.select2-container, .select2-container--open .select2-dropdown--below {
width: 200px !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/css/select2.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/AlertifyJS/1.13.1/css/alertify.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/AlertifyJS/1.13.1/alertify.min.js"></script>
<select class="js-data-example-ajax">
<option value="abc">ABC</option>
<option value="bca" selected>BCA</option>
<option value="mnp">MNP</option>
<option value="pqr">PQR</option>
</select>

Destroy/remove a meteor template in iron-router

I've just started using iron router in meteor. How do I hide or delete a template, or replace it with another?
I have two client-side routes. One shows a list of chatrooms a user can join:
Router.route('/', function () {
this.layout('bodyLayout');
this.render('roomList', {
to: 'roomList'
});
});
The other is for the chatrooms:
Router.route('/room/:_id', function () {
this.layout('bodyLayout');
var roomId = this.params._id;
this.render('room', {
to: 'room',
data: () => {
return { roomId }
}
});
});
Both of these have the same layout where they are yielded close together:
<template name="bodyLayout">
<!-- layout stuff -->
{{> yield 'roomList'}}
{{> yield 'room'}}
<!-- layout stuff -->
</template>
Now, if I go to / and click a room, the room renders under it. But what I really want is for this to show either room OR roomList. How do I delete roomList when creating room, or can I replace it with room somehow?
So ultimately I've found several solutions.
Replacing a template
This is useful in a case like mine, if a certain location needs only one of several templates. It turns out that several routes can render to the same yield. we can remove one of the yields:
<template name="bodyLayout">
<!-- layout stuff -->
{{> yield }}
<!-- layout stuff -->
</template>
And remove the to attribute from the object we pass to this.render:
Router.route('/', function () {
this.layout('bodyLayout');
this.render('roomList');
});
Router.route('/room/:_id', function () {
this.layout('bodyLayout');
var roomId = this.params._id;
this.render('room', {
// We don't need this anymore:
// to: 'room',
data: () => {
return { roomId }
}
});
});
Alternatively, do give the yield a name:
<template name="bodyLayout">
<!-- layout stuff -->
{{> yield 'content' }}
<!-- layout stuff -->
</template>
And give both routers the 'to' attribute that refers to the same yield:
Router.route('/', function () {
this.layout('bodyLayout');
this.render('roomList', { to: 'content'});
});
Router.route('/room/:_id', function () {
this.layout('bodyLayout');
var roomId = this.params._id;
this.render('room', {
to: 'content',
data: () => {
return { roomId }
}
});
});
Removing a template from a router
If you do need to remove a template from a router, you can do this by giving the this.render function an empty string instead of a template name, thus telling it to render no template to this yield:
Router.route('/', function () {
this.layout('bodyLayout');
this.render('roomList', {
to: 'roomList'
});
// Remove the room that was shown
this.render('', { to: 'room'});
});

Using Dagre-D3 with Meteor

I'm trying to use Dagre-D3 to create a simple directed graph that adds nodes from a text input.
Unfortunately, the graph just refuses to draw.. and I think it's because my "Nodes.find({}).forEach(function (n) {..." doesn't appear to run.
Any ideas on what might be wrong? Am I using dagre-d3 or meteor wrongly..? Thank you for the help!
main.html:
<body>
<div id = "mapspace">
{{> map}}
</div>
<div id = "gennodespace">
{{>gennode}}
</div>
</body>
<template name="map">
<div>
<svg id="svg-canvas" width=650 height=680></svg>
</div>
</template>
<template name = "gennode">
<form class="node-entry">
<input type="text" name="nodedesc" placeholder="Enter a node title">
</form>
</template>
main.js -- client:
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import './main.html';
Nodes = new Meteor.Collection("nodes");
Edges = new Meteor.Collection("edges");
Template.map.rendered = function(){
var g = new dagreD3.graphlib.Graph()
.setGraph({})
.setDefaultEdgeLabel(function () {
return {};
});
// Establish nodes
Nodes.find({}).forEach(function (n) {
g.setNode(n.nodeid, {
label: n.description
});
});
// Establish edges
Edges.find({}).fetch().forEach(function (e) {
g.setEdge(e.source, e.target, {
lineTension: .8,
lineInterpolate: "bundle"
});
});
var render = new dagreD3.render();
var svg = d3.select("svg"),
svgGroup = svg.append("g");
render(d3.select("svg g"), g);
}
Template.gennode.events = ({
'submit .node-entry': function(event,template){
event.preventDefault();
var desc = event.target.nodedesc.value;
var nodeid = Nodes.find().count();
// Update Nodes
Nodes.insert({
nodeid: nodeid,
description: desc
});
// Update edges
Edges.insert({
source: 0, //placeholder for now
target: nodeid
});
// Reset form
template.find(".node-entry").reset();
return false;
}
});
main.js -- server:
Nodes = new Meteor.Collection("nodes");
Edges = new Meteor.Collection("edges");
import { Meteor } from 'meteor/meteor';
Meteor.startup(() => {
// code to run on server at startup
});
Figured it out... The database simply hasn't loaded on rendered. so I added a find().observe to wrap around it.
Nodes.find().observe({
added: function (){
// Establish nodes
Nodes.find({}).forEach(function (n) {
g.setNode(n.nodeid, {
label: n.description
});
});
// Establish edges
Edges.find({}).fetch().forEach(function (e) {
g.setEdge(e.source, e.target, {
lineTension: .8,
lineInterpolate: "bundle"
});
});
var render = new dagreD3.render();
var svg = d3.select("svg"),
svgGroup = svg.append("g");
render(d3.select("svg g"), g);
}
});

Fullcalendar Meteor can't drop event below 2 first week in month view

I use FullCalendar 2.6.1 as external plugin(just add directory to myapp/client/ with fullcalendar staff) with Meteor 1.2.1. I also use jquery-ui-1.11.4. And in my calendar I have a strange bug: I can't drop event to any day after first 2 (some times 3 or 4) weeks in month view. And it doesn't matter which mont I choose.
I have a reproduction. If you want to see just login with user test#user.com and password 111 to http://85.143.219.249:4000/login and open calendar.
I also try to use package rzymek:fullcalendar but same bug is present.
As I remember I haven't such bug with old version(2.2.0) of fullcalendar.
Here is how I initialize calendar.
Template:
<template name="calendar">
<div class="content">
{{#pageTitle title="Calendar" }}{{/pageTitle}}
<div class="row">
<div class="col-lg-12">
<div class="row">
<div class="col-md-6">
<div class="hpanel">
<div class="panel-body">
<div id="external-events">
<strong>Click, Drop or Resize event on calendar!</strong>
<p>Message from functions:
<br/>
<div id="external-events">
<p>Drag a event and drop into callendar.</p>
{{#each workouts}}
<div class='external-event h-bg-green text-white' id={{this._id}}>{{workoutName}}</div>
{{/each}}
</div>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-12">
{{#panel title="Calendar"}}
<div id="calendar"></div>
{{/panel}}
</div>
</div>
</div>
And JS:
Template.calendar.onRendered(function() {
// Initialize i-check plugin
$('.i-checks').iCheck({
checkboxClass: 'icheckbox_square-green',
radioClass: 'iradio_square-green'
});
// Initialize the external events
$('#external-events div.external-event').each(function() {
// store data so the calendar knows to render an event upon drop
$(this).data('event', {
title: $.trim($(this).text()), // use the element's text as the event title
stick: true // maintain when user navigates (see docs on the renderEvent method)
});
// make the event draggable using jQuery UI
$(this).draggable({
zIndex: 1111999,
revert: true, // will cause the event to go back to its
revertDuration: 0 // original position after the drag
});
});
// Re-rendering calendar events when collection changed
//var calendar = this.$('.fc');
this.autorun(function() {
$('#calendar').fullCalendar('refetchEvents');
});
// Initialize the calendar
var date = new Date();
var d = date.getDate();
var m = date.getMonth();
var y = date.getFullYear();
$('#calendar').fullCalendar({
header: {
left: 'title',
right: 'today prev,next'
},
lang: 'ru',
timezone: 'local',
fixedWeekCount: false,
editable: true,
droppable: true,
displayEventTime: false,
allDayDefault: true,
color: '#62cb31',
drop: function(date) {
var newCalendarEvent = {};
newCalendarEvent.start = date.format();
newCalendarEvent.eventSourceId = this.id;
newCalendarEvent.eventSourceType = 'workout';
newCalendarEvent.title = $(this).text();
newCalendarEvent.color = '#62cb31';
Meteor.call('calendarEventAdd', newCalendarEvent);
},
eventReceive: function(event) {
var calendarEventId = event._id;
var calendarEventDate = event.start.format();
var calendarEventName = event.title;
var calendarEventColor = '#62cb31';
//Meteor.call('calendarEventAdd', calendarEventDate, calendarEventId, calendarEventName, calendarEventColor);
//console.log(calendarEventDate);
},
events: function(start, end, timezone, callback) {
var calendarEvents = [];
_.each(Calendar.find({}, {fields: {start: 1, title: 1, color: 1}}).fetch(), function(value, key, list) {
calendarEvents.push(value);
});
callback(calendarEvents);
},
eventDragStart: function(event, jsEvent, ui, view) {
$(this).qtip().hide();
},
eventDrop: function(event, delta) {
var eventId = event._id;
var newEventDate = event.start.format();
console.log(event);
console.log(delta);
Meteor.call('calendarEventUpdate', eventId, newEventDate);
},
eventRender: function(event, element) {
$(element).css({backgroundColor: '#62cb31', borderColor: '#62cb31'});
var content = '<button class="btn btn-xs btn-default delCalendarEvent" id="' + event._id + '"><i class="fa fa-trash"></i></button>';
element.qtip({
show: {
event: 'click',
solo: true
},
hide: {
event: 'click unfocus'
},
content: content,
style: {
classes: 'qtip-bootstrap'
},
position: {
my: 'bottom center',
at: 'top center',
container: element
}
});
}
});
});
Template.calendar.events({
'click .delCalendarEvent': function(event, template) {
var eventId = event.currentTarget.id;
var calendar = template.$('.fc');
//Meteor.call('calendarEventDel', eventId);
Meteor.call('calendarEventDel', eventId, function(error, result) {
if (error) {
console.log(error);
} else {
calendar.fullCalendar('removeEvents', eventId);
}
});
}
});
This piece of code works for me.
Try to add this to css file:
body .fc {
overflow:auto;
}
I also had this issue and found that it was caused by a parent container being 100% height. I noticed that the week rows that did not allow a drag were not in my initial view on page load.My body tag was being styled as 100% height which after looking at the browser dev tools looked more like a view height of 100. After removing that it worked fine. If you are having the same issue i would open the dev tools, scroll down, and then run through the different tags of any parents to see which ones seem to cut off at the same point you cannot drag your events.

iron:router will not re-render after route change with same template

How can I make Iron:router re-render a template?
I have this html:
<head>
</head>
<body>
</body>
<template name="mainLayout">
list
find
{{> yield}}
</template>
<template name="listTemplate">
<p>list</p>
</template>
and this js:
Router.configure({
layoutTemplate: 'mainLayout'
});
Router.route('/list', {
name: 'list',
template: 'listTemplate'
});
Router.route('/find', {
name: 'find',
template: 'listTemplate',
data: function () {
return this.params.query;
}
});
if (Meteor.isClient) {
Template.listTemplate.rendered = function () {
if (this.data)
console.log('find ' + this.data.q);
else
console.log('list all');
};
}
When I click on the links to switch views (simulated here with console.log), the route does change, but the template is not re-rendered.
Is there a way to force iron:router to re-render?
Setting the router controller state did not work for me. The answer Antônio Augusto Morais gave in this related github issue worked. Using the Session to store the reactive var to trigger the autorun reactiveness. It's a hack, but it works.
## router.coffee
Router.route '/profile/:_id',
name: 'profile'
action: ->
Session.set 'profileId', #params._id
#render 'profile'
## profile.coffee
Template.profile.onCreated ->
#user = new ReactiveVar
template = #
#autorun ->
template.subscription = template.subscribe 'profile', Session.get 'profileId'
if template.subscription.ready()
template.user.set Meteor.users.findOne _id: Session.get 'profileId'
else
console.log 'Profile subscription is not ready'
Template.profile.helpers
user: -> Template.instance().user.get()
## profile.html
<template name="profile">
{{#if user}}
{{#with user.profile}}
<span class="first-name">{{firstName}}</span>
<span class="last-name">{{lastName}}</span>
{{/with}}
{{else}}
<span class="warning">User not found.</span>
{{/if}}
</template>
You can try something like this:
Router.configure({
layoutTemplate: 'mainLayout'
});
Router.route('/list', {
name: 'list',
template: 'listTemplate',
action: function() {
this.state.set('query', this.params.query);
}
});
Router.route('/find', {
name: 'find',
template: 'listTemplate',
data: function() {
return this.params.query;
},
action: function() {
this.state.set('query', this.params.query);
}
});
if (Meteor.isClient) {
Template.listTemplate.rendered = function() {
this.autorun(
function() {
if (this.state.get('query'))
console.log('find ' + this.data.q);
else
console.log('list all');
}
);
};
}
The rendered method isn't reactive, that's why you need an autorun.
The template "this.data" isn't reactive so you're gonna need a reactive var to do that, either a Session variable, a controller state, or some kind of reactive var.
You may need to add the reactive-var package depending on what approach you take.

Resources