Ractive JS global observation - ractivejs

I'm wanting to have multiple Ractive instances bind to the same data.
var data = { text:'Old Text'};
var r1 = new Ractive({ el:.., template:..., data:data })
.observe('text', function(value){
//r1 is not aware the data has been updated
alert(value);
});
var r2 = new Ractive({ el:.., template:..., data:data })
.observe('text', function(){
});
r2.set('text', 'New Text');
Is there a way I can create global key paths so that r1 gets notified?
Thanks.

A few options:
Use .update() to tell the other instance to update:
var r2 = new Ractive({ el:.., template:..., data:data })
r2.set('text', 'New Text');
r1.update(/*'text'*/)
You could use the change events to take this a bit further:
var r2 = new Ractive({ el:.., template:..., data:data })
r2.on('change', function(e){
r1.udpate()
//or to be more specific:
Object.keys(e).forEach(key, function(){
r1.update(key)
})
})
#Rich_Harris took this concept a bit further and created a Ractive adapter:
var r2 = new Ractive({ el:.., template:...,
data: r1,
adapt: ['Ractive'] })
There are still some holes with bidirectional support for arrays, but it's a good start.
If you don't need IE8 support and all your browsers support defineProperty, you can use magic mode:
var data = { text:'Old Text'};
var r1 = new Ractive({ data:data, magic: true })
r1.observe('text', function(value){
console.log('r1 says', value)
});
var r2 = new Ractive({ data:data, magic: true })
r2.observe('text', function(value){
console.log('r2 says', value)
});
r2.set('text', 'New Text')
data.text = 'Yep, this works too'
Lastly, you might consider up-levelling Ractive and have it be a parent of these two views as components:
<div>
<r1-component data="{{.}}"/>
<r2-component data="{{.}}"/>
</div>
(However your layout goes). Oftentimes I've found this works best because there ends up being some top-level interaction that Ractive helps manage anyway.

Related

I wish to show route of 'n' number of drivers (single route for each driver)on Gmap. Its a case of dynamic drivers

Currently i am able to display 3 route of 3 drivers respectively(one for each) on a single Gmap.
Now, I wish to show route of 'n' number of drivers (single route for each driver)on Gmap. Its a case of dynamic drivers. I can get data from db for 'n' number of drivers that i need to display on a single map.
My Code is below for single driver please help me for dynamic craetion of routes:
var lat_lng1 = [];
var latlngbounds1 = "";
lat_lng1 = new Array();
var value1 = markers1.length;
//Intialize the Path Array
var path1 = new google.maps.MVCArray();
//Intialize the Direction Service
var service1 = new google.maps.DirectionsService();
var lineSymbol1 = { path1: google.maps.SymbolPath.CIRCLE };
//Set the Path Stroke Color
for (i = 0; i < markers1.length; i++) {
var data1 = markers1[i]
var myLatlng1 = new google.maps.LatLng(data1.lat, data1.lng);
lat_lng1.push(myLatlng1);
var marker1 = new google.maps.Marker({position: myLatlng1,map: map,icon: icon1});
(function (marker1, data1) {
// Attaching a click event to the current marker
google.maps.event.addListener(marker1, "click", function (e) {
if (i == 0){
infoWindow.setContent(data1.Person);infoWindow.open(map, marker1);}
if(i=(markers2.length -1)){
infoWindow.setContent(data1.Person);infoWindow.open(map, marker1);}
else{
infoWindow.setContent(data1.Title);
infoWindow.open(map, marker1);}
});
})(marker1, data1);
}
poly = new google.maps.Polyline({
// path: lineCoordinates,
strokeColor: '#BC456F',
icons: [{
icon: {
path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW,strokeColor: '#009900',fillColor: '#009900',fillOpacity: 1},
repeat: '100px',
path1: []
}],
map: map
});
well I solved this issue the very next day by calling ajax loops by driver id

Adding Vector Data - GML format with WFS Transport (possible bug?)

I'm trying to add features to my OpenLayers map, by querying a publicly available WFS server which serves GML data.
// initalize the map
var map = new ol.Map({
layers: [
new ol.layer.Tile({
// OpenLayers public map server
source: new ol.source.OSM()
}),
],
target: 'map',
view: new ol.View({
// center on Murica
center: [-10997148, 4569099],
zoom: 4
})
});
var xmlhttp = new XMLHttpRequest();
// execute this once the remote GML xml document has loaded
xmlhttp.onload = function() {
console.log("GML XML document retrieved. executing onload handler:");
var format = new ol.format.GML3();
var xmlDoc = xmlhttp.responseXML;
console.log("you will see multiple features in the xml: ");
console.log(xmlDoc);
// Read and parse all features in XML document
var features = format.readFeatures(xmlDoc, {
featureProjection: 'EPSG:4326',
dataProjection: 'EPSG:3857'
});
console.log("for some reason only a single feature will have been added: ")
console.log(features);
console.log("Why is this?");
var vector = new ol.layer.Vector({
source: new ol.source.Vector({
format: format
})
});
// Add features to the layer's source
vector.getSource().addFeatures(features);
map.addLayer(vector);
};
// configure a GET request
xmlhttp.open("GET", "http://geoint.nrlssc.navy.mil/dnc/wfs/DNC-WORLD/feature/merged?version=1.1.0&request=GetFeature&typename=DNC_APPROACH_LIBRARY_BOUNDARIES&srsname=3857",
true);
// trigger the GET request
xmlhttp.send();
Here is a CodePen with the bug demonstrated.
http://codepen.io/anon/pen/yamOEK
Here you can download it packaged into a single HTML file:
https://drive.google.com/open?id=0B6L3fhx8G3H_cmp1d3hHOXNKNHM
I can successfully download an entire feature collection with multiple features into my variable xmlDoc, using a valid typename. However, when I use format.ReadFeatures(xmlDoc), the OpenLayers GML format parser appears to only be extracting a single feature from the feature collection, whereas it should be extracting many more.
It would be wonderful if someone could take a look and see if they can figure out if I'm doing something stupidly wrong or it's a legitimate bug in OpenLayers3. Thanks so much for anyone who's able to help!
Single feature is added because entire document is read so instead of format.readFeatures(xmlDoc) parse each feature.Here is source code:
var vector;
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
}),
],
target: 'map',
view: new ol.View({
center: [-8197020.761224195,8244563.818176944],
zoom: 4
})
});
var xmlhttp = new XMLHttpRequest();
xmlhttp.onload = function() {
var format = new ol.format.GML3();
var xmlDoc = xmlhttp.responseXML;
vector = new ol.layer.Vector({
source: new ol.source.Vector({
format: format
})
});
for (var i = 1; i < xmlDoc.children[0].children.length; i++) {
var features = format.readFeatures(xmlDoc.children[0].children[i], {
featureProjection: 'EPSG:4326'
});
features.getGeometry().transform('EPSG:4326', 'EPSG:3857');
vector.getSource().addFeature(features);
}
map.addLayer(vector);
map.getView().fit(vector.getSource().getExtent(), map.getSize())
};
xmlhttp.open("GET", "http://geoint.nrlssc.navy.mil/dnc/wfs/DNC-WORLD/feature/merged?version=1.1.0&request=GetFeature&typename=DNC_APPROACH_LIBRARY_BOUNDARIES&srsname=3857",
true);
// trigger the GET request
xmlhttp.send();
Here is a CodePen result.
http://codepen.io/anon/pen/bwXrwJ

How do I access the data context and the template instance in each case (event, helper, hook)?

My brain is hurting because of all the inconsistency. Please have a look at the code below and correct/complete it:
Template.Example.events({
'click #example': function(event, template) {
instance = template; // or = Template.instance();
instance_reactive_data_context = template.currentData(); // or = Template.currentData();
instance_nonreactive_data_context = ???
event_data_context = event.currentTarget;
});
Template.Example.helpers({
example: function() {
instance = Template.instance();
instance_reactive_data_context = this; // or = Template.currentData();
instance_nonreactive_data_context = ???
}
});
Template.Example.onCreated(function () {
instance = this;
instance_reactive_data_context = this.currentData();
instance_nonreactive_data_context = this.data;
});
Here's the answer, which even shows a bit more. It includes creating and accessing a reactive-var or reactive-dictionaries attached to the template. All this is extremely important to understand for Meteor developers:
Template.Example.onCreated(function () {
instance = this; // or = Template.instance();
// instance_reactive_data_context = no point in having a reactive data context since this function is only executed once
instance_nonreactive_data_context = this.data;
// now in order to attach a reactive variable to the template:
let varInitialValue = ...
instance.reactive_variable = new ReactiveVar(varInitialValue);
// and now let's attach two reactive dictionaries to the template:
let dictInitialValue_1 = { ... }
let dictInitialValue_2 = [ ... ]
instance.reactive_dictionaries = new ReactiveDict();
instance.reactive_dictionaries.set('attachedDict_1', dictInitialValue_1);
instance.reactive_dictionaries.set('attachedDict_2', dictInitialValue_2);
});
Template.Example.events({
'click #example': function(event, template) {
instance = template; // or = Template.instance();
instance_reactive_data_context = Template.currentData();
instance_nonreactive_data_context = template.data;
event_data_context = event.currentTarget;
// to access or modify the reactive-var attached to the template:
console.log(template.reactive_variable.get());
template.reactive_variable.set('new value');
// to access or modify one of the reactive-dictionaries attached to the template:
console.log(template.reactive_dictionaries.get('attachedDict_2'));
template.reactive_dictionaries.set('attachedDict_2', { newkey: 'new value', somearray: ['a', 'b'] });
});
Template.Example.helpers({
example: function() {
instance = Template.instance();
instance_reactive_data_context = this; // or = Template.currentData();
// instance_nonreactive_data_context = it can't be accessed as a non-reactive source. When you'll need something like this, most likely because the helper is running too many times, look into the [meteor-computed-field][1] package
// to access or modify the reactive-var attached to the template:
console.log(Template.instance().reactive_variable.get());
Template.instance().reactive_variable.set('new value');
// to access or modify one of the reactive-dictionaries attached to the template:
console.log(Template.instance().reactive_dictionaries.get('attachedDict_2'));
Template.instance().reactive_dictionaries.set('attachedDict_2', 'new value here');
// obviously since you declared instance on the first line, you'd actually use everywhere "instance." instead of "Template.instance()."
}
});

Meteor.js calling a template.helpers function vs global variable

I am using Reactive-table to display paginated data in my meteor.js app as shown below, yet data displayed in Reactive-table is dependent on on specific user event (Selecting client, project, date range and clicking on the submit button). So I was wondering if it is possible to trigger template.helpers >> myCollection function from the 'submit form' event? OR is it better to define a global variable to store data returned from user query based on the user (client, project, date range selection) then make this global variable the return from the myCollection function?
I have tried researching how to call .helpers function from an template.events event but couldn't find any information. So any help on which approach is better and if calling the .events function is better then how to do that, will be highly appreciated. Thanks.
Below is the code I have in my app:
Template.detailedreport.rendered = function() {
Session.set("dreport_customer", "");
Session.set("dreport_project", "");
Session.set("dreport_startDate", new Date());
Session.set("dreport_endDate", new Date());
$('.set-start-date').datetimepicker({
pickTime: false,
defaultDate: new Date()
});
$('.set-end-date').datetimepicker({
pickTime: false,
defaultDate: new Date()
});
$('.set-start-date').on("dp.change",function (e) {
Session.set("dreport_startDate", $('.set-start-date').data('DateTimePicker').getDate().toLocaleString());
});
$('.set-end-date').on("dp.change",function (e) {
Session.set("dreport_endDate", $('.set-end-date').data('DateTimePicker').getDate().toLocaleString());
});
};
Template.detailedreport.helpers({
customerslist: function() {
return Customers.find({}, {sort:{name: -1}});
},
projectslist: function() {
return Projects.find({customerid: Session.get("dreport_customer")}, {sort:{title: -1}});
},
myCollection: function () {
var now = Session.get("dreport_startDate");
var then = Session.get("dreport_endDate");
var custID = Session.get("dreport_customer");
var projID = Session.get("dreport_project");
Meteor.call('logSummary', now, then, projID, custID, function(error, data){
if(error)
return alert(error.reason);
return data;
});
}
},
settings: function () {
return {
rowsPerPage: 10,
showFilter: true,
showColumnToggles: false,
fields: [
{ key: '0._id.day', label: 'Day' },
{ key: '0.totalhours', label: 'Hours Spent'}
]
};
}
});
Template.detailedreport.events({
'submit form': function(e) {
e.preventDefault();
var now = $('.set-start-date').data('DateTimePicker').getDate().toLocaleString();
var then = $('.set-end-date').data('DateTimePicker').getDate().toLocaleString();
var custID = $(e.target).find('[name=customer]').val();
var projID = $(e.target).find('[name=project]').val();
//Here is the problem as I am not sure how to refresh myCollection function in .helpers
},
'change #customer': function(e){
Session.set("dreport_project", "");
Session.set("dreport_customer", e.currentTarget.value);
},
'change #project': function(e){
Session.set("dreport_project", e.currentTarget.value);
}
});
Template:
<div>
{{> reactiveTable class="table table-bordered table-hover" collection=myCollection settings=settings}}
</div>
Server:
Meteor.methods({
logSummary: function(startDate, endDate, projid, custid){
//Left without filtering based on date, proj, cust for testing only...
return Storylog.find({});
}
});
Template helpers are reactive, meaning that they will be recomputed if their dependencies change. So all you need to do is update their dependencies and then the myCollection helper will be recomputed.
Replace your comment // Here is the problem... with:
Session.set('dreport_endDate', then);
Session.set('dreport_startDate', now);
Session.set('dreport_project', projID);
Session.set('dreport_customer', custID);

Google Maps API Marker Clusterer and Ajax

I am running multiple ajax calls to download a large number of google maps icons. When I try to increment the Marker Clusterer, however, the map clears all markers. I believe this is because I am calling var markerCluster = new MarkerCluster(map); in each AJAX call.
Can anyone tell me how to correctly implement this?
var populateMapByIncident = function(incident, page) {
var run_again = false;
$.getJSON(
"/public_map_ajax_handler",
{"shortname" : incident, "page": page},
function(sites_list) {
if (sites_list.length > 2) {
run_again = true;
}
var mapOptions = {
zoom: 8,
center: new google.maps.LatLng(40.6501038, -73.8495823),
mapTypeId: google.maps.MapTypeId.ROADMAP
}
var map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
var markers = [];
var i = 0;
for (var i = 0; i < sites_list.length; i++) {
var latLng = new google.maps.LatLng(sites_list[i].latitude, sites_list[i].longitude);
var marker = new google.maps.Marker({'position': latLng,
'icon': getMarkerIcon(sites_list[i]),
'site_id': sites_list[i].id,
'case_number': sites_list[i].case_number,
'work_type': sites_list[i].work_type,
'floors_affected': sites_list[i].floors_affected,
'status': sites_list[i].status});
markers.push(marker);
var site_id = sites_list[i].id;
google.maps.event.addListener(marker, "click", function() {
new Messi('<p>Name, Address, Phone Number are removed from the public map</p><p>Details: work type: '
+ this.work_type+ ', floors affected: ' + this.floors_affected + '</p>' + '<p>Status: ' + this.status + '</p>',
{title: 'Case Number: ' + this.case_number, titleClass: 'info',
buttons: [
{id: 0, label: 'Printer Friendly', val: "On the live version, this would send all of this site's data to a printer friendly page." },
{id: 1, label: 'Change Status', val: "On the live version, you would be able to change the site's status here."},
{id: 2, label: 'Edit', val: "On the live version, you would be able to edit the site's info, as new details come in."},
{id: 3, label: 'Claim', val: "On the live version, clicking this button would 'Claim' the site for your organization, letting other organizations know that you intend to work on that site"},
{id: 4, label: 'Close', val: 'None'}], callback: function(val) { if (val != "None") {Messi.alert(val);} }});
});
}
var markerCluster = new MarkerClusterer(map);
markerCluster.addMarkers(markers);
if (run_again == true) {
populateMapByIncident(incident, page + 1, markers);
} else {
markerCluster.addMarkers(markers);
}
}
);
}
I am running multiple ajax calls to download a large number of google maps icons. When I try to increment the Marker Clusterer, however, the map clears all markers. I believe this is because I am calling var markerCluster = new MarkerCluster(map); in each AJAX call.
Can anyone tell me how to correctly implement this?
Don't do that. Create the MarkerClusterer one time in the global scope (outside of any function), and add markers to it when you receive them from the server (assuming you aren't sending any duplicates).
See the documentation
Looks like you are already adding arrays of markers to the MarkerClusterer:
addMarkers(markers:Array., opt_nodraw:boolean) | None | Add an array of markers to the clusterer.
All you really need to do is move where you create the MarkerClusterer to the global scope. One suggestion below.
var markerCluster = new MarkerClusterer(map); // <------------- add this
var populateMapByIncident = function(incident, page) {
var run_again = false;
$.getJSON(
// ----- existing code ------- //
// leave as is
// ----- modification -------- //
// var markerCluster = new MarkerClusterer(map); <----------- remove this
markerCluster.addMarkers(markers);
if (run_again == true) {
populateMapByIncident(incident, page + 1, markers);
} else {
markerCluster.addMarkers(markers);
}

Resources