Meteor template not updating in JQuery callback - javascript

I'm trying to get a div to fade out and then change a session variable which is being used in the template. The session variable is being successfully changed in the callback function but the template is not reactively updating.
The following does not reactively update the template. (These are trigger)
$(event.target.parentNode).find(subclass)
.fadeOut("slow", function() {
Session.set(this.valueOf() + "_show_exercise_fields", set_show_exercise_fields);
The following does reactively update the template.
Session.set(this.valueOf() + "_show_exercise_fields", set_show_exercise_fields);
$(event.target.parentNode).find(subclass)
.fadeOut("slow", function() {
// do nothing
});
Is there a way to force the template to re render or a better way to do what I am trying to do. Thanks
EDIT 1
Below is the entire function
Template.exercise.events({
'click .exercise-name': function(event) {
var subclass = ".exercise-fields-container";
var set_show_exercise_fields = false;
if (!Session.get(this.valueOf() + "_show_exercise_fields")) {
var subclass = ".exercise-options-container";
var set_show_exercise_fields = true;
}
// find the subclass (either the fields container or the options
// container) and fade out
$(event.target.parentNode).find(subclass)
.fadeOut("slow", function() {
Session.set(this.valueOf() + "_show_exercise_fields", set_show_exercise_fields);
});
}
});
Template.exercise.helpers({
show_fields: function() {
Session.setDefault(this.valueOf() + "_show_exercise_fields", true);
return Session.get(this.valueOf() + "_show_exercise_fields");
}
});
Below is the template
<template name="exercise">
<div class="exercise-name">
{{this.name}}
</div>
{{#if show_fields}}
Fields
{{else}}
Options
{{/if}}
</template>

Event handlers aren't reactive contexts. You can create a reactive context using Tracker.autorun().
If you use a session variable within the function you pass to autorun, the entire function will rerun whenever the session variable is changed. In this context you could fade in or out as you desire.

I had a scenario where a collection was being updated, so I had to re-build my select element using Materialize Select
Here's what my on rendered function looks like. The autorun knows that Channels is a reactive data source and re-runs the autorun function when this data source changes.
Channels = new Mongo.Collection("channels");
Template.channelSelectController.onRendered(function(){
var self = this;
this.autorun(function(){
var count = Channels.find().count();
self.$('select').material_select();
});
});

Related

calling vue function from js on change

I'm trying to make a call from a on.("change") event to a vue method and that works fine but trying to give the received data from the change event to a Vue variable, the console log says that the variable has the new data, but it doesn't really change the variable correctly, it changes the last variable when you duplicate the components.
here is some of my code:
Vue.component('text-ceditor',{
props:['id'],
data: function (){
return {
dataText: "this is something for example"
}
},
template: '#text-ceditor',
methods: {
setData: function(data){
console.log(data)
this.dataText = data
console.log(this.dataText)
}
},
mounted: function(){
CKEDITOR.replace(this.$refs.text);
self = this;
CKEDITOR.instances.texteditor.on('change', function() {
self.setData(this.getData())
})
}
})
the component works correctly but the variable just change the last one
here is the fiddle: https://jsfiddle.net/labradors_/3snmcu84/1/
Your problem isn't with Vue but with CKEDITOR and its instances (with the ids you defined in the template and the way you reference them).
First problem is that you're duplicating ids in the text-ceditor component:
<textarea ref="text" v-model="dataText" id="texteditor" rows="10" cols="80"></textarea>
Why do we need to fix this? Because CKEDITOR instances in Javascript are id-based.
So now we need to change the id attribute to use the one passed in the component's props, like this:
<textarea ref="text" v-model="dataText" :id="id" rows="10" cols="80"></textarea>
Once we took care of that, let's reference the correct CKEDITOR instance from within the mounted method in the component.
We want to reference the one that matches with the id in our component.
From:
mounted: function(){
CKEDITOR.replace(this.$refs.text);
self = this;
CKEDITOR.instances.texteditor.on('change', function() {
self.setData(this.getData())
})
}
To:
mounted: function () {
CKEDITOR.replace(this.$refs.text);
var self = this;
var myCKEInstance = CKEDITOR.instances[self.id]
myCKEInstance.on('change', function () {
self.dataText = myCKEInstance.getData()
})
}
Notice that I also removed the call to setData as there is no need to have it and also declared self as a variable avoiding the global scope (which would overwrite it everytime and reference the latest one in all different components).
Now everything is updating correctly, here's the working JSFiddle.

How do you access object properties from a nested polymer element?

I would like to access javascript object data from a custom polymer element nested inside a list. The host page has the following:
<core-list id="eventData">
<template>
<event-card event="{{model}}"></event-card>
</template>
</core-list>
With the following script:
<script type="text/javascript">
var data = [
// server side code to populate the data objects here
];
var theList = document.querySelector('core-list')
theList.data = data;
function navigate(event, detail, sender) {
window.location.href = '/events/show/?eventId=' + event.detail.data.id
}
theList.addEventListener("core-activate", navigate, false);
</script>
Inside event-card.html the following markup in the template achieves the desired result:
<core-toolbar>
<div class="title">{{event.title}}</div>
</core-toolbar>
<div class="responses">{{event.numResponses}} responses</div>
However when I run the following in the template script:
Polymer('event-card', {
ready: function() {
var eventDates = [];
var theEvent = this.getAttribute('event');
console.log(theEvent);
console.log(theEvent.proposedDate);
console.log(theEvent.possibleStartDate);
console.log(theEvent.possibleEndDate);
if (theEvent.proposedDate) {
eventDates[0] == theEvent.proposedDate;
} else {
if (theEvent.possibleStartDate && theEvent.possibleEndDate) {
var startDate = moment(theEvent.possibleStartDate);
var endDate = moment(theEvent.possibleEndDate);
var difference = endDate.diff(startDate, 'days');
for (i = 0; i < difference; i++) {
eventDates[i] = startDate.add(i, days);
}
}
}
console.log(eventDates);
this.dates = eventDates;
},
created: function() {
// hint that event is an object
this.event = {};
}
});
</script>
the log statements print
{{model}}
undefined
undefined
undefined
Array[0]
So I seem to be getting caught by the different ways that properties and attributes are evaluated in different contexts but I am not sure whether it is a bug in my code or even what approach to try next.
Because the "event" attribute is set by Polymer when the template is processed, the ready event handler is the wrong place to do your stuff (by the way, using Polymer you should consider using the "domReady" event handler).
Anyway, to get your scenario working, just add a "eventChanged" method to your Polymer component. Whenever an attribute changes (which is also the case when Polymer executes the template element) a "[propertyName]Changed(oldVal, newVal)" will be called (if existing) to signal the change to your component.
So implement this method and you're done.
One more caveat in your code : You should consider using "this.event" to access the attribute value (or as best solution the "newVal" parameter of your eventChanged(oldVal,newVal) method).
Why not using Polymer's attribution model from the get go?
<polymer-element attributes="event">
<script>
Polymer("event-card", {
ready: function() {
var theEvent = this.event;
}
});
</script>
</polymer-element>

Ember.js - I want an Action event (on="") to trigger when there is a transition to a new Route

I want an Action event (on="") to trigger when there is a transition to a new Route.
I've seen the list of Action event handlers and closest I could find is attaching the action to the largest HTML element on the page and triggering it with 'Mousemove". This is a terribly flawed away of going about what I want to do.
So just to draw it out.
<div {{action 'displayEitherHtml1or2'}} class="largestDOMelement">
{{#if showHtml1}}
// html 1 inside
{{/if}}
{{#if showHtml2}}
// html 2 inside
{{/if}}
</div>
'/objects' is a list of objects and clicking one leads to 'object/somenumber'. The action should automatically trigger when I enter the 'object/somenumber' page.
UPDATE: I've taken the contents from the previous update and dumped them into my DocRoute, but nothing it being triggered when I transition to 'doc' through {{#link-to 'doc' this.docID}} {{docTitle}}{{/link-to}}
VpcYeoman.DocRoute = Ember.Route.extend(VpcYeoman.Authenticated,{
toggleLetterSwitch: false,
togglePermitSwitch: false,
activate: function () {
var docTemplateID = this.get('docTemplateID');
if ( docTemplateID == 2) {
this.set('toggleLetterSwitch', true);
this.set('togglePermitSwitch', false);
console.log('docTemplateID equals 2');
} else {
this.set('toggleLetterSwitch', false);
this.set('togglePermitSwitch', true);
}
}
});
UPDATE DOS: setDocID is set in the DocsController to 1. Here's the whole thing.
VpcYeoman.DocsController = Ember.ArrayController.extend({
tempDocId: 1,
actions: {
addDoc: function (params) {
var docTitle = this.get('docTitle');
var docTemplateID = 1;
var docTemplateID = this.get('tempDocId');
console.log(this.get('tempDocId'));
var store = this.store;
var current_object = this;
var doc = current_object.store.createRecord('doc', {
docTitle:docTitle,
docTemplateID:docTemplateID
});
doc.save();
return true;
},
setDocId: function (param) {
this.set('tempDocId', param);
console.log(this.get('tempDocId'))
},
}
});
As #fanta commented, it seems like you're looking for the activate hook within your Route. This gets called when you enter the route where you define it. If you want to call it on every transition, you might consider defining a base route for your application and extending that instead of Em.Route:
App.BaseRoute = Em.Route.extend(
activate: function () {
// Do your thing
}
);
App.YourRoutes = App.BaseRoute.extend()
It's possible that there's a more appropriate place/time to do this, but without knowing quite what your action does, this is probably the best guess.
ETA: Looking at your edit, you won't want all your routes to extend App.BaseRoute the way I did it above; you should probably just include that activate hook explicitly in the routes which need it.

Create a generic class to bind knockout object with pages

I am bit new to knockout and jquery mobile, There was a question which is already answered, I need to optimize the PageStateManager class to use generic bindings, currently PageStateManager can only use for one binding,I would really appreciate if someone can guide me to create a generic class to manage page states with knockout bindings Heere is the working code,http://jsfiddle.net/Hpyca/14/
PageStateManager = (function () {
var viewModel = {
selectedHospital: ko.observable()
};
var changePage = function (url, viewModel) {
console.log(">>>>>>>>" + viewModel.id());
$.mobile.changePage(url, {viewModel: viewModel});
};
var initPage = function(page, newViewModel) {
viewModel.selectedHospital(newViewModel);
};
var onPageChange = function (e, info) {
initPage(info.toPage, info.options.viewModel);
};
$(document).bind("pagechange", onPageChange);
ko.applyBindings(viewModel, document.getElementById('detailsView'));
return {
changePage: changePage,
initPage: initPage
};
})();
Html
<div data-role="page" data-theme="a" id="dashBoardPage" data-viewModel="dashBoardViewModel">
<button type="button" data-bind="click: goToList">DashBoard!</button>
</div>
New dashboard model
var dashBoardViewModel = function() {
var self = this;
self.userName = ko.observable('Welcome! ' + "UserName");
self.appOnline = ko.observable(true);
self.goToList = function(){
//I would like to use PageStateManager here
// PageStateManager.changePage($("#firstPage"),viewModel);
ko.applyBindings(viewModel,document.getElementById("firstPage"));//If I click Dashbord button multiple times it throws and multiple bind exception
$.mobile.changePage($("#firstPage"));
}
}
ko.applyBindings(dashBoardViewModel,document.getElementById("dashBoardPage"));
update url : http://jsfiddle.net/Hpyca/14/
Thank you in advance
I would probably go for creating a NavigationService which only handles changing the page and let knockout and my view models handle the state of the pages.
An simple example of such a NavigationService could be:
function NavigationService(){
var self = this;
self.navigateTo = function(pageId){
$.mobile.changePage($('#' + pageId));
};
}
You could then, in your view models just call it when you want it to navigate to a new page. One example would be upon selection of a hospital (which could be done either via a selection function or by manually subscribing to changes to the selectedHospital observable):
self.selectHospital = function(hospital){
self.selectedHospital(hospital);
navigationService.navigateTo('detailsView');
};
Other than the call to the navigationService to navigate, it's just ordinary knockout to keep track of which viewmodel should be bound where. A lot easier than having jquery mobile keeping track of which viewmodel goes where, if you ask me.
I have updated your jsfiddle to show a sample of how this could be done, making as few changes as possible to the HTML code. You can find the updated fiddle at http://jsfiddle.net/Hpyca/15/

How do I hide dynamic elements in Meteor?

So I have a bunch of templates that will be iterated with {{#each game}} showing the following template:
<template name="game">
{{#if condition}}
<div class="box">
Page 1
</div>
{{else}}
<div class="box">
Page 2
</div>
{{/if}}
</template>
I want to display "Page 2" when the "Page 1" box is clicked, so I have the following:
Template.game.events({
'click .box': function(e) {
Session.set("condition", true);
}
});
But I do not want all of the other game templates to transition to Page 2, just the one that was clicked. How do I accomplish this?
EDIT: The change should only affect the current user, not all users.
Assuming your games are stored in a Meteor.Collection, and condition is a property on the documents that should reflect for all users, not just the current one, you can do something like this:
Template.game.events({
'click .box': function(event, template) {
Games.update(
{_id: template.data._id},
{$set: {condition: !template.data.condition}}
);
}
});
If it should only affect the current user, you can use a template-instance specific session variable and return it with a helper function called condition:
Template.game.events({
'click .box': function(event, template) {
Session.set("condition-" + template.data._id, true);
}
});
Template.game.condition = function() {
return Session.get("condition-" + this._id);
};
You could achieve similar functionality with a local collection.
Don't use Session variables! The very reason is the problem you have, they're equivalent of the old global variables. Use template's data instead, it's local and can be used to control the behavior like you want in this case.
For the template in your example:
Template.game.created = function() {
this.data.conditionValue = 'something';
this.data.conditionDep = new Deps.Dependency();
};
Template.game.condition = function() {
this.conditionDep.depend();
return this.conditionValue;
};
Template.game.events({
'click .box': function(e, t) {
t.data.conditionValue = 'somethingElse';
t.data.conditionDep.changed();
},
});
I also feel using a session with id doesn't sound like the best idea, and found this answer which seems to be better than using session: Using Meteor sessions to toggle templates

Categories

Resources