AngularJS socket.IO Bootstrap Modals only once - node.js

I have a problem with bootstrap modal window when using AngularJS together with NodeJS and socket.io. I have been googling and it seems like it is issue that has a solution, but for some reason it doesn't work when I am trying to implement it together with Socket.io. I used modals on two different places - when I click on a static div (works perfectly), when I receive a message from webSockets (opens only once and then nothing). I guess I might have a problem in my JS code since the modal when I click on a static div works fine, but I don't know.
I have an address and I am sending some data via WebSockets to the client when this link is visited. The client event looks like this:
socket.on('patient', function(data){
modalInstance = $modal.open({
templateUrl: 'templates/patient.js',
controller: 'patientModalCtrl',
resolve: {
details: function(){return data;}
}
});
});
and:
socket.on('alergy',function(data){
modalInstance = $modal.open({
templateUrl: "templates/alergy.js",
controller: 'alergyModalCtrl'
});
});
Both of these work only once and then the modal window stops to appear. Interesting is, that when I emit "alergy", then again and then "patient" I get an "alergy" window and then patient window the second "alergy" window under it.
emiting looks like this:
app.get('/api/socket/hash/:hash', function(req, res){
var hash = req.params.hash;
//allergy
if(hash === "3fDecCD"){
connected_sockets[0].emit('alergy', {alergy: true});
res.json({status: true});
}
//patient detail
else if(hash === "Vc43Sf"){
connected_sockets[0].emit('patient', {name: 'Jan', surname: 'Bjornstad'});
res.json({status: true});
}
else{
res.json({status: false});
}
});
My template looks like this:
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header"></div>
<div class="modal-body" style="background-color: #FFD1D1;">
<h1 style="text-align: center; color: red;">Allergy!</h1>
<h2 style="text-align: center; color: red;">The patient is allergic to opium!</h2>
</div>
<div class="modal-footer">
<button class="btn btn-warning" ng-click="cancel()">Close</button>
</div>
</div>
</div>
I am using AngularJS 1.0.7, Bootstrap CSS 2.3.1

I would say that your socket-event listeners are firing "outside of AngularJS world" and as such AngularJS machinery is not kicking-in to do its 2-way data binding "magic". In precise terms, you are not entering AngularJS $digest loop so bindings are not updated, promises not resolved etc.
The easy fix is to wrap calls to AngularJS-specific code (here - call to the $modal service) into Scope.$apply method, ex.:
socket.on('alergy',function(data){
$scope.$apply(function(){
modalInstance = $modal.open({
templateUrl: "templates/alergy.js",
controller: 'alergyModalCtrl'
});
});
});

Related

Angularjs Material md-datepicker within Formly template will not open calendar pane

I'm trying to create a Formly template using md-datepicker. Unfortunately, when I click on the md-datepicker control within my form the calendar panel does not open.
controller code:
{
className: 'col-xs-6',
key: 'dateCreated',
type: 'materialdatepicker',
templateOptions: {
label: 'Created'
},
expressionProperties: {
'templateOptions.disabled': function () {
return !vm.options.editMode;
},
'templateOptions.required': function () {
return vm.options.editMode;
}
}
}
template:
<script type="text/ng-template" id="materialdatepicker.html">
<div layout="column">
<div flex="100">
<p class="input-group" style="display: block; margin: 0px;">
<md-datepicker id="{{::id}}" name="{{::id}}" ng-model="model[options.key]"></md-datepicker>
</p>
<div class="formlyMessages" ng-messages="fc.$error" ng-if="fc.$touched">
<div class="formlyMessage" ng-message="{{::name}}" ng-repeat="(name, message) in ::options.validation.messages">
{{message(fc.$viewValue, fc.$modelValue, this)}}
</div>
</div>
</div>
</div>
</script>
formly config:
formlyConfigProvider.setType({
name: 'materialdatepicker',
templateUrl: 'materialdatepicker.html',
wrapper: ['bootstrapLabel', 'bootstrapHasError'],
defaultOptions: {
ngModelAttrs: ngModelAttrs
},
controller: ['$scope', function ($scope) {
$scope.materialdatepicker = {};
}]
});
I can't seem to figure out how to get the calendar panel to open. I'm not getting any errors in the console and the control does get populated with my initial value.
Any ideas?
What I forgot to mention in my original post was that this form is contained within a modal window ($uibModal). As such, the calendar pane was popping up behind my modal window.
The solution found here worked for me: Angular Material DatePicker Calendar Shows Behind Angular Modal
You need to tell your calendar pane to open with a high z-index so it renders above the modal. Place this style sheet code into your modal html:
<style>
.md-datepicker-calendar-pane {
z-index: 1200;
}
</style>

Foundation 6 reveal modal events not firing for me

I want videos playing in the reveal modal window to stop playing when the modal window closes (who doesn't?). This is easily done with jQuery by setting the iframe source to empty.
But I can't figure out how to make it work in a callback. The modal window itself functions as expected. And this works:
$('.close-button', '#video-reveal').on('click', function() {
$('#video-player').attr("src", "");
console.log("button event fired");
});
However, neither of the following has any effect:
// from documentation
$(document).on('close.fndtn.reveal', '[data-reveal]', function() {
var modal = $(this);
console.log("closing reveal event fired");
});
// my attempt to do it programmatically
$('[data-reveal]').on ('opened.fndtn.reveal', function() {
var modal = jQuery(this);
console.log("opened reveal");
});
So it feels like the event is not firing. I'm sure it is, but how to capture it?
The magic of Foundation 6 is not all obvious without some digging. Working with version 6.2.3
$(document).on(
'open.zf.reveal', '[data-reveal]', function () {
console.log("'open.zf.Reveal' fired.");
}
);
It appears as though you are using Foundation 5's callbacks, rather than Foundation 6...
For your callbacks, I'd suggest using 'closed.zf.reveal', 'open.zf.reveal' or 'closeme.zf.reveal' as mentioned here:
http://foundation.zurb.com/sites/docs/reveal.html
In HTML
<!--the button -->
<a class="button" data-open="videoModal" href="#">Example Video Modal</a>
<!--Modal Video -->
<div class="row" id="theVideo">
<div id="videoModal" class="reveal" data-reveal data-close-on-click="true">
<h2>This modal has video</h2>
<div class="flex-video">
<iframe id="youtubePlayer" width="854" height="480" src="https://www.youtube.com/embed/4z6aSO05YHg" frameborder="0" allowfullscreen allowscriptaccess="always"></iframe>
</div>
<button class="close-button" data-close aria-label="Close reveal" type="button" onClick="destroyVideo()">
<span aria-hidden="true">×</span>
</button>
</div>
</div>
<!--in apps.js-->
function destroyVideo(){
var url = $('#youtubePlayer').attr('src');
$('#youtubePlayer').attr('src', '');
$('#youtubePlayer').attr('src', url);
}

When should we use Vue.js's component

When I study the feature of Vue.js's component system. I feel confused when and where should we use this? In Vue.js's doc they said
Vue.js allows you to treat extended Vue subclasses as reusable
components that are conceptually similar to Web Components, without
requiring any polyfills.
But based on their example it doesn't clear to me how does it help to reuse. I even think it complex the logic flow.
For example, you use "alerts" a lot in your app. If you have experienced bootstrap, the alert would be like:
<div class="alert alert-danger">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<strong>Title!</strong> Alert body ...
</div>
Instead of writing it over and over again, you can actually make it into a component in Vue:
Vue.component('alert', {
props: ['type','bold','msg'],
data : function() { return { isShown: true }; },
methods : {
closeAlert : function() {
this.isShown = false;
}
}
});
And the HTML template (just to make it clear, I separate this from the Vue Comp above):
<div class="alert alert-{{ type }}" v-show="isShown">
<button type="button" class="close" v-on="click: closeAlert()">×</button>
<strong>{{ bold }}</strong> {{ msg }}
</div>
Then you can just call it like this:
<alert type="success|danger|warning|success" bold="Oops!" msg="This is the message"></alert>
Note that this is just a 4-lines of template code, imagine when your app uses lot of "widgets" with 100++ lines of code
Hope this answers..

meteor get no data from second mongo database

iam total new at meteor and i try to build an Meteor application that should show the data another Mongo Database. The app it self can use its own metor database. So i found, that with MongoInternals.RemoteCollectionDriver() its to connect with my second database.
Next step is to make it work in the meteor tutorial. But i dont get back any data from the second database. For a test, simple arrays are returned correct from my function and placed right into the webapp. And the expression in .find() should be also ok. I tested it in the Mongo console.
If the connection to the second database is placed in if (Meteor.isClient) or outside of the client/server parts, the error "ReferenceError: MongoInternals is not defined" appears. If its set inside of if (Meteor.isServer) sometimes an exception appears in the console:
Exception in template helper: .ris_sessions#http://localhost:3000
/risdd_mongo.js?4fc7111851b4ed2182782e0a368b366cc4e89745:15:17
bindDataContext/<#http://localhost:3000/packages
/blaze.js?77c0809654ee3a10dcd5a4f961fb1437e7957d33:2693:14
...... and a lot more messages
I cant see, why dont getting back any data from the second database.
Here are the sources:
Tasks = new Mongo.Collection("tasks");
//////////////////////
if (Meteor.isClient) {
Template.body.helpers({
tasks: function() {
return Tasks.find({}, {sort: {createdAt: -1 }});
}
,
s_sessions: function() {
return ris_sess.find( {},{description:1} ).fetch();
}
});
}
//////////////////////
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
var risdb_drv = new MongoInternals.RemoteCollectionDriver("mongodb://172.0.0.1:27017/ris");
var ris_sess = new Mongo.Collection("sessions", { _driver: risdb_drv });
});
}
<head>
<title>foo</title>
</head>
<body>
<div class="container">
<header>
<h1>todo list</h1>
<!-- add a FORM !-->
<form class="new-task">
<input type="text" name="text" placeholder="ad your task here" />
</form>
</header>
<ul>
{{#each tasks}}
{{>task}}
{{/each}}
{{#each ris_sessions}}
{{>ris_session}}
{{/each}}
</ul>
</div>
</body>
<template name="task">
<li>{{text}}</li>
</template>
<template name="ris_session">
<li>{{description}}</li>
</template>
If you use local MongoDB server try change
mongodb://172.0.0.1:27017/ris
to
mongodb://127.0.0.1:27017/ris
Typo in IP: 127

Rendering new div created dynamically with Meteor

I have a the classic "Thread->Posts" model.
In the application, I have a left sidebar with the "Thread" collections. When the user click in a Thread, I want to create another div with the Thread->Posts elements.
Is there any way to do this, conservating the reactivity?
For now, I've got this:
// In the client
Template.threadlist.events({
'click tr': function(event){
Session.set("selectedThread",this);
$("#posts").html( Meteor.render(Template["datathread"]) );
}
})
[...]
Template.datathread.events({
'click input.add-post' : function(event){
var t = Session.get("selectedThread");
Meteor.call("addPost", {"thread":t,"text":"foo","user":"var"},callback})
}
})
[...]
// In the server
addPost: function(param){
var id = Threads.update(param.thread,{ $addToSet: {"posts": {"text":param.text, "user": param.user}}});
return id;
}
The template with the posts is something like this:
<template name="datathread">
{{#each thread.posts}}
{{user}} says: {{text}}
<br />
{{/each}}
</template>
(The "user" and "text" propertis are from the "thread.posts" elements)
With this code, I only get the new values refreshing (F5) the webpage (or executing the 'click tr'event). What I'm doing wrong?
Thank you!
== Edit ==
Ok... Now, with the recomendation of Chistian Fritz, my code its something like this:
// In the client
Template.datathread.thread = function(){
return Session.get("selectedThread");
}
[...]
Template.threadlist.events({
'click tr': function(event){
Session.set("selectedThread",this);
}
});
//In the html
<div class="span5" id="threads">
{{> datathread}}
</div>
<template name="datathread">
{{#if thread}}
<hr />
{{#each thread.posts}}
{{user}} says: {{text}}
<br />
{{/each}}
{{/if}}
</template>
The changes are great (it's so simple!), but the reactivity still doesn't work :(....
The problem is that you are using jQuery to fill your DOM:
$("#posts").html( Meteor.render(Template["datathread"]) );
This breaks the reactivity chain.
You are only showing part of your code, so I can't give you the full solution, but it seems that the datathread template is already using the selectedThread session variable -- which is good. Hence, it might be as easy as using {{> datathread}} in place of the #posts element in your HTML.
EDIT:
One thing that you need to change is the scope you are assuming datathread: it's already the thread itself, if I understand correctly:
<template name="datathread">
<hr />
{{#each posts}}
{{user}} says: {{text}}
<br />
{{/each}}
</template>
Also, the this in the threadlist event handler most certainly won't be the thread. I don't know the data of the tr in the threadlist (can you show that code?), but you will most probably do something like the following:
Session.set("selectedThread", this.id);
And for datathread:
Template.datathread.thread = function(){
return Threads.find({_id: Session.get("selectedThread")});
}
or similar.

Resources