How to add programatically add new div using Enyojs and attach to the subscriber of opentok - enyo

This is an extention to stackflow qustion. I want to create div programmatically and attach the opentok subscriber cam to it and all the subscriber cam need to come under parent div "#subscription". Please let me know what I'm doing wrong in my code.
Jsfiddle
http://jsfiddle.net/6z9n65ty/55/
Updated Jsfiddle
http://jsfiddle.net/6z9n65ty/59/
Code for subscription div
enyo.kind({
name : "blanc.SubscriberWrapper",
showing: true,
stream: null,
session: null,
create: function(){
this.inherited(arguments);
if(this.stream != null && this.session != null){
this.subscribe();
}
},
subscribe: function(){
this.session.subscribe(this.stream, this.hasNode());
}
});
Update
I want to attach the stream to subscribeButton, the below code will add the first subscriber stream to "#subscribeButton". If second subscribe stream exist, then it will overwrite previous subscribe cam video with second stream. I want to have all the subscribers to be viewed in the screen. So I was trying to create new div using create function.
streamCreated: function (event) {
this.session.subscribe(event.stream, subscribeButton);
// Add subscriber stream to a new div
// this.createComponent({kind: "blanc.SubscriberWrapper", name : event.stream.id, stream: event.stream, session: this.session});
// this.render();
},
JSFIDDLE
http://jsfiddle.net/6z9n65ty/62/

I believe the problem with your code is that hasNode() will return false (or is it null?) because the node isn't rendered yet. Change create: to rendered: and it may work.
The token in your fiddle had expired so I wasn't able to test it further but couldn't. Your code that creates the new component looks correct at first blush.
You didn't say what -did- happen when the code is executed. If you're just getting blank divs, then my assumption is correct.

Related

Displaying form on first login

I'm trying to work out how I can display a form to a user upon their first login to my app ( to fill in profile information) after which they can proceed to the regular site.
Could anyone point me in the right direction?
Thanks
You can make the trick using app startup script:
https://devsite.googleplex.com/appmaker/settings#app_start
Assuming that you have Profile model/datasource, code in your startup script will look similar to this:
loader.suspendLoad();
var profileDs = app.datasources.Profile;
// It would be more secure to move this filtering to the server side
profileDs.query.filters.UserEmail._equals = app.user.email;
profileDs.load({
success: function() {
if (profileDs.item === null) {
app.showPage(app.pages.CreateProfile);
} else {
app.showPage(app.pages.HomePage);
}
loader.resumeLoad();
},
failure: function() {
loader.resumeLoad();
// your fallback code goes here
}
});
If profile is absolute must, I would also recommend to enforce the check in onAttach event for every page but CreateProfile (to prevent navigation by direct link):
// Profile datasource should be already loaded by startup script
// when onAttach event is fired
if (app.datasources.Profile.item === null) {
throw new Error('Invalid operation!');
}
I suggest checking the user profile upon login. If the profile is not present, display the profile form, otherwise, proceed to the regular site.

VueJs child component props not updating instantly

I have a parent/child component setup where the parent is loading data from the server and passing it down to children via props. In the child I would like to instantiate a jQuery calendar with some of the data it receives from the parent.
In order to wait for the data before setting up the calendar, I broadcast an event in the parent that I have an event listener setup for in the child.
The listener is being fired in the child but if I this.$log('theProp'), it's undefined. However, if I inspect the components with the VueJs devtools, the parent/child relationship is there and the child has received the prop in the meantime.
The prop is defined on the child as a dynamic prop :the-prop="theProp". Since the child does receive the prop in the end, I'm assuming my setup is correct but there seems to be some sort of delay. The parent sets the props in the return function of the ajax call and again: it's working, just with a slight delay it seems.
I also tried registering a watch listener on the prop in the child so I could setup the calendar then and be sure that the prop is there. However, the watch listener fires, but this.$log('theProp') is still undefined.
If I pass the data along with the the broadcast call, like this.$broadcast('dataLoaded', theData) the child receives it just fine. But it seems wrong to do it that way as I'm basically building my own prop handler.
I'm not posting any code because the components are rather large and the VueJs devtools are telling me the parent/child situation is working.
Am I missing some information? Is there a slight delay between setting a value in the parent and the child receiving it? What would be the proper way to wait for parent data in the child?
Normally, when you're just rendering the data out into the template, the timing doesn't matter so much since the data is bound to the template. But in this case, I really need the data to be there to setup the calendar or it will be wrong.
Thanks.
edit 1: here's a jsfiddle: https://jsfiddle.net/dr3djo0u/1/
It seems to confirm that the data is not available immediately after the broadcast. However, the watcher does work, though I could almost swear that sometimes this.$log('someData') returned undefined when I setup that testcase.
But I guess my problem might be somewhere else, I'll have a look tonight, don't have the project with me right now.
edit 2: did some more tests. My problem was that a) event listeners do not seem to receive the data instantly and b) I was also trying to init the calendar in the route.data callback if someData was already around (e.g. when coming from parent), but that route callback is called before the component is ready, so it wasn't working there either.
My solution is now this:
// works when the child route is loaded directly and parent finishes loading someData
watch: {
someData() {
this.initCalendar();
}
},
// works when navigating from parent (data already loaded)
ready() {
if (this.someData && this.someData.length) {
this.initCalendar()
}
}
As far as I know, you should not need events to pass data from parent to child.
All you need is, in the child component: props: ['theProp']
And when using the child component in the parent: <child :theProp="someData"></child>
Now, wherever in the parent you change someData, the child component will react accordingly.
You don't need events, you don't need "watch", you don't need "ready".
For example: after an AJAX call, in the parent's "ready", you load some data:
// at the parent component
data: function () {
return {
someData: {}
}
},
ready: function () {
var vm = this;
$.get(url, function(response) {
vm.someData = response;
});
}
Now, you do not need anything else to pass the data to the child. It is already in the child as theProp!
What you really need to do is to have, in the child, something which reacts to data changes on its own theProp property.
Either in the interface:
<div v-if="theProp.id > 0">
Loaded!
</div>
Or in JavaScript code:
// at the child component
computed: {
// using a computed property based on theProp's value
awesomeDate: function() {
if (!this.theProp || (this.theProp.length === 0)) {
return false;
}
if (!this.initialized) {
this.initCalendar();
}
return this.theProp.someThing;
}
}
Update 1
You can also, in the parent, render the child conditionally:
<child v-if="dataLoaded" :theProp="someData"></child>
Only set dataLoaded to true when the data is available.
Update 2
Or maybe your issue is related to a change detection caveat
Maybe you're creating a new property in an object...
vm.someObject.someProperty = someValue
...when you should do...
vm.$set('someObject.someProperty', someValue)
...among other "caveats".
Update 3
In VueJS 2 you are not restricted to templates. You can use a render function and code the most complex rendering logic you want.
Update 4 (regarding OP's edit 2)
Maybe you can drop ready and use immediate option, so your initialization is in a single place:
watch: {
someData: {
handler: function (someData) {
// check someData and eventually call
this.initCalendar();
},
immediate: true
}
}
It's because tricky behavior in Vue Parent and Child lifecycle hooks.
Usually parent component fire created() hook and then mount() hook, but when there are child components it's not exactly that way: Parent fires created() and then his childs fire created(), then mount() and only after child's mount() hooks are loaded, parent loads his mount() as explained here. And that's why the prop in child component isn't loaded.
Use mounted() hook instead created()
like that https://jsfiddle.net/stanimirsp5/xnwcvL59/1/
Vue 3
Ok so I've spent like 1.5h trying to find out how to pass prop from parent to child:
Child
<!-- Template -->
<template>
<input type="hidden" name="_csrf_token" :value="csrfToken">
<span>
{{ csrfToken }}
</span>
</template>
<!-- Script -->
<script>
export default {
props: [
"csrfToken"
]
}
</script>
Parent
<!-- Template -->
<template>
<form #submit.prevent="submitTestMailForm" v-bind:action="formActionUrl" ref="form" method="POST">
...
<CsrfTokenInputComponent :csrf-token="csrfToken"/>
...
</form>
</template>
<!-- Script -->
<script>
...
export default {
data(){
return {
...
csrfToken : "",
}
},
methods: {
/**
* #description will handle submission of the form
*/
submitTestMailForm(){
let csrfRequestPromise = this.getCsrfToken();
let ajaxFormData = {
receiver : this.emailInput,
messageTitle : this.titleInput,
messageBody : this.bodyTextArea,
_csrf_token : this.csrfToken,
};
csrfRequestPromise.then( (response) => {
let csrfTokenResponseDto = CsrfTokenResponseDto.fromAxiosResponse(response);
this.csrfToken = csrfTokenResponseDto.csrToken;
this.axios({
method : "POST",
url : SymfonyRoutes.SEND_TEST_MAIL,
data : ajaxFormData,
}).then( (response) => {
// handle with some popover
})
});
},
/**
* #description will return the csrf token which is required upon submitting the form (Internal Symfony Validation Logic)
*/
getCsrfToken(){
...
return promise;
}
},
components: {
CsrfTokenInputComponent
}
}
</script>
Long story short
This is how You need to pass down the prop to child
<CsrfTokenInputComponent :csrf-token="csrfToken"/>
NOT like this
<CsrfTokenInputComponent csrf-token="csrfToken"/>
Even if my IDE keep me telling me yeap i can navigate with that prop to child - vue could not bind it.
solution (testing ok)
In child component just using the props data, no need to re-assignment props's values to data, it will be cause update bug!
vue child component props update bug & solution
https://forum.vuejs.org/t/child-component-is-not-updated-when-parent-component-model-changes/18283?u=xgqfrms
The problem is not how to pass data with props, but rather how to do two things at almost the same time.
I have an user account component that can edit users (with an user id) and add users (without id).
A child component shows checkboxes for user<->company assignments, and needs the user id to prepare API calls when the user account is saved.
It is important that the child component shows before saving the user account, so that things can be selected before the user is saved and gets an id.
So it has no user id at first: the id is passed to the child component as 'null'.
It updates when the user is stored and gets an id.
But at this point, it takes a very short time for the child to get the new id into its model.
If you call a function in the child component that relies on data that was just changing, it might happen that the function executes before the data is updated.
For cases like this, nextTick() is your friend.
import { nextTick } from 'vue';
...
saveAccount() {
axios.post(URL, this.userModel).then((result)) {
// our model gets an id when persisted
this.userModel.id=result.data.id;
nextTick( () => {
this.$refs.childComponent.doSomething();
});
}
}

Bootboxjs: how to render a Meteor template as dialog body

I have the following template:
<template name="modalTest">
{{session "modalTestNumber"}} <button id="modalTestIncrement">Increment</button>
</template>
That session helper simply is a go-between with the Session object. I have that modalTestNumber initialized to 0.
I want this template to be rendered, with all of it's reactivity, into a bootbox modal dialog. I have the following event handler declared for this template:
Template.modalTest.events({
'click #modalTestIncrement': function(e, t) {
console.log('click');
Session.set('modalTestNumber', Session.get('modalTestNumber') + 1);
}
});
Here are all of the things I have tried, and what they result in:
bootbox.dialog({
message: Template.modalTest()
});
This renders the template, which appears more or less like 0 Increment (in a button). However, when I change the Session variable from the console, it doesn't change, and the event handler isn't called when I click the button (the console.log doesn't even happen).
message: Meteor.render(Template.modalTest())
message: Meteor.render(function() { return Template.modalTest(); })
These both do exactly the same thing as the Template call by itself.
message: new Handlebars.SafeString(Template.modalTest())
This just renders the modal body as empty. The modal still pops up though.
message: Meteor.render(new Handlebars.SafeString(Template.modalTest()))
Exactly the same as the Template and pure Meteor.render calls; the template is there, but it has no reactivity or event response.
Is it maybe that I'm using this less packaging of bootstrap rather than a standard package?
How can I get this to render in appropriately reactive Meteor style?
Hacking into Bootbox?
I just tried hacked into the bootbox.js file itself to see if I could take over. I changed things so that at the bootbox.dialog({}) layer I would simply pass the name of the Template I wanted rendered:
// in bootbox.js::exports.dialog
console.log(options.message); // I'm passing the template name now, so this yields 'modalTest'
body.find(".bootbox-body").html(Meteor.render(Template[options.message]));
body.find(".bootbox-body").html(Meteor.render(function() { return Template[options.message](); }));
These two different versions (don't worry they're two different attempts, not at the same time) these both render the template non-reactively, just like they did before.
Will hacking into bootbox make any difference?
Thanks in advance!
I am giving an answer working with the current 0.9.3.1 version of Meteor.
If you want to render a template and keep reactivity, you have to :
Render template in a parent node
Have the parent already in the DOM
So this very short function is the answer to do that :
renderTmp = function (template, data) {
var node = document.createElement("div");
document.body.appendChild(node);
UI.renderWithData(template, data, node);
return node;
};
In your case, you would do :
bootbox.dialog({
message: renderTmp(Template.modalTest)
});
Answer for Meteor 1.0+:
Use Blaze.render or Blaze.renderWithData to render the template into the bootbox dialog after the bootbox dialog has been created.
function openMyDialog(fs){ // this can be tied to an event handler in another template
<! do some stuff here, like setting the data context !>
bootbox.dialog({
title: 'This will populate with content from the "myDialog" template',
message: "<div id='dialogNode'></div>",
buttons: {
do: {
label: "ok",
className: "btn btn-primary",
callback: function() {
<! take some actions !>
}
}
}
});
Blaze.render(Template.myDialog,$("#dialogNode")[0]);
};
This assumes you have a template defined:
<template name="myDialog">
Content for my dialog box
</template>
Template.myDialog is created for every template you're using.
$("#dialogNode")[0] selects the DOM node you setup in
message: "<div id='dialogNode'></div>"
Alternatively you can leave message blank and use $(".bootbox-body") to select the parent node.
As you can imagine, this also allows you to change the message section of a bootbox dialog dynamically.
Using the latest version of Meteor, here is a simple way to render a doc into a bootbox
let box = bootbox.dialog({title:'',message:''});
box.find('.bootbox-body').remove();
Blaze.renderWithData(template,MyCollection.findOne({_id}),box.find(".modal-body")[0]);
If you want the dialog to be reactive use
let box = bootbox.dialog({title:'',message:''});
box.find('.bootbox-body').remove();
Blaze.renderWithData(template,function() {return MyCollection.findOne({_id})},box.find(".modal-body")[0]);
In order to render Meteor templates programmatically while retaining their reactivity you'll want to use Meteor.render(). They address this issue in their docs under templates.
So for your handlers, etc. to work you'd use:
bootbox.dialog({
message: Meteor.render(function() { return Template.modalTest(); })
});
This was a major gotcha for me too!
I see that you were really close with the Meteor.render()'s. Let me know if it still doesn't work.
This works for Meteor 1.1.0.2
Assuming we have a template called changePassword that has two fields named oldPassword and newPassword, here's some code to pop up a dialog box using the template and then get the results.
bootbox.dialog({
title: 'Change Password',
message: '<span/>', // Message can't be empty, but we're going to replace the contents
buttons: {
success: {
label: 'Change',
className: 'btn-primary',
callback: function(event) {
var oldPassword = this.find('input[name=oldPassword]').val();
var newPassword = this.find('input[name=newPassword]').val();
console.log("Change password from " + oldPassword + " to " + newPassword);
return false; // Close the dialog
}
},
'Cancel': {
className: 'btn-default'
}
}
});
// .bootbox-body is the parent of the span, so we can replace the contents
// with our template
// Using UI.renderWithData means we can pass data in to the template too.
UI.insert(UI.renderWithData(Template.changePassword, {
name: "Harry"
}), $('.bootbox-body')[0]);

AngularFire update single object

How to update a single object within node.
{
foo:
{
title: 'hello world',
time: '1000'
}
}
As above, I just want update title.
$firebase(new Firebase(ref).child('foo')).$save(); will update the entire node. Also tried $save('title') but not work.
The reason I just want to update a single object, because some of the ng-model doesn't need to update to firebase.
Heres an example setting the title to "whatever"
$firebase(new Firebase(ref)).$child('foo').$child('title').$set("whatever")
I have recently been working with angularfire and firebase. I am not sure if this is a fix but i dont think you dont need to explicitly select the item property you want to update when you are using the $save() method.
For instance, in my use case i was selecting a user and had an init function that got that whole object
html
<ul ng-repeat="user in vm.users>
<button ng-click="vm.updateUserInit(user)">Edit</button>
</ul>
This then opened up the update form with the users properties. In the controller i took that user and assigned it into a $firebaseObject.
controller
var selectedUser;
vm.updateUserInit = function (user) {
var ref = new Firebase("https://<foo>.firebaseio.com/users/" + user.$id);
selectedUser = $firebaseObject(ref);
}
It justs puts the user into a firebase object as the variable selectedUser to use in the $save.
Then when the user updates the details
html
<button type="button" ng-click="vm.updateUserDetails()">Update User Details</button>
the function already has the selected user as an object to use and anything added will be updated. Anything omitted will not.
controller
vm.updateUserDetails = function () {
selectedUser.firstName = vm.firstName;
selectedUser.$save();
}
The selectedUser.$save(); changes only the first name even though the user has 6 other properties
Not sure if this helps, trying to wrap my head around all of this as well. Check out valcon if you dont already have it. Really nice extension on the chrome inspector for firebase
UPDATE FOR ANGULARFIRE 2
if you create a service for your calls , which could serve all update calls
constructor(private db: AngularFireDatabase) {}
update(fbPath: string, data: any) {
return this.db.object(fbPath).update(data);
}
And then in the page component
this.api.update(`foo`, ({ title: 'foo Title' }) );
Much simpler

How to detect when a reactive element updates in Meteor

I have a standard template that does an {{#each}} with a list of reactive elements from a collection.
The problem I am having is, I want to do a font property change on a particular element whenever it is updated in the collection, just to draw attention to the fact that it was updated.
I haven't been able to find a good / elegant way to do this. Can anyone point me to the right direction? Thanks!
you might want to check cursor observe, when some document updates or changed, you can do anything like if event called.
li#model-1273927381273 model-1273927381273
Modal.find().observe
changed: (newDocument, oldDocument) ->
($ '#model-'+ newDocument._id).action()
http://docs.meteor.com/#observe
i use similar code currently but dont know how to prevent init documents load.
Similar to your question, I wanted to 'flash' a div whenever a particular session variable (a GPS location) changed. My solution below.
in /client/main.js:
Session.setDefault('lat', 0);
Session.setDefault('lng', 0);
Session.setDefault('locationChange', 1);
And:
Meteor.setInterval(function() {
getLocation();
}, 5000);
function getLocation() {
Location.locate(function(pos){
var oldLat = Session.get("lat");
var oldLng = Session.get("lng");
Session.set('lat', pos.latitude);
Session.set('lng', pos.longitude);
if (oldLat != Session.get("lat") || oldLng != Session.get("lng"))
Session.set("locationChange", -1 * Session.get("locationChange"));
}, function(err){
console.log("Location not obtained: ", err);
});
}
In the template, say /client/templates/someTemplate.html, where I wanted to alert the user to the change:
<span class="glyphicon glyphicon-screenshot" aria-hidden="true" data-locationChange='{{locationChange}}'></span>
In the associated javascript file, say /client/templates/someTemplate.js:
Template.appBody.helpers({
locationChange: function() {
$("#locationIcon").fadeOut(100).fadeIn(100);
return Session.get("locationChange");
},
});
So, now, whenever the GPS coordinates change (not necessarily when they are refreshed), the value for 'locationChange' is flipped from 1 to -1 or vice versa. This triggers the 'locationChange' helper, which updates the (meaningless) attribute 'data-locationChange', but also triggers some jQuery script that flashes the span in the template.

Resources