How can I update a complex knockout observable programatically? - javascript

I'm using durandal/requirejs/knockout here.
I'm also using the coderenaissance plugin for mapping (ko.viewmodel.updateFromModel(zitem, data).)
I'm getting the following data from my ajax call which I'm mapping into my zitem observable.
{
"itemNumber" : "ABATAH000",
"effectiveDate" : "2015-11-03T15:30:05.7118023-05:00",
"expiryDate" : "2015-05-03T15:30:05.7118023-04:00",
"minimumPremium" : 25,
"zSubItems" : [{
"zSubItemName" : "Mine",
"unitDistance" : 100000,
"zSubSubItems" : [{
"zSubSubItemName" : "CoverageA",
"zSubSubItemPremium" : 100.0,
"id" : 0
}
],
"id" : 1
}
],
"id" : 0
}
And here is the viewmodel I'm using:
define(['plugins/http', 'durandal/app', 'knockout', 'services/datacontext'],
function (http, app, ko, datacontext) {
var zitem = ko.observable();
var activate = function () {
//This is just a wrapper around an ajax call.
return datacontext.getPolicy("value")
.then(function(data) {
ko.viewmodel.updateFromModel(zitem, data);
});
};
var updateMinimumPremium = function (thisItem) {
//This doesn't work
zitem.minimumPremium(thisItem.minimumPremium + 1);
};
return {
displayName: 'zitem example',
zitem: zitem,
updateMinimumPremium: updateMinimumPremium,
activate: activate
};
});
I'm binding the updateMinimumPremium to a click on a button at the same level as the minimumPremium element.
<button data-bind="click: $parent.updateMinimumPremium">Add 1</button>
How can I update [minimumPremium] or [zSubSubItemPremium] programatically?

"minimumPremium" would be observable
zitem.minimumPremium(thisItem.minimumPremium() + 1);

Your zitem is observable as well, so try this:
zitem().minimumPremium(thisItem.minimumPremium + 1);
In real application don't forget to check the value of zitem() call - it can be uninitialized.

Related

Composite Control event is not triggering

i have a control as below
i need to fire the event closed when i click on the close icon press
sap.ui.define(["sap/ui/core/Control",
"sap/m/Carousel",
"sap/m/Panel",
"sap/m/Toolbar",
"sap/ui/core/Icon",
"sap/m/Label",
"sap/m/Button",
"sap/m/ToolbarSpacer"], function (Control,Carousel,Panel,Toolbar,Icon,Label,Button,ToolbarSpacer) {
"use strict";
return Control.extend("com.example.Control", {
metadata : {
aggregations : {
_panel : {
type : "sap.m.Panel",
multiple: false,
visibility:'hiddden'
}
},
events : {
closed : {
}
}
},
renderer : function (oRM, oControl) {
oRM.write("<div");
oRM.writeControlData(oControl);
oRM.addClass("sapUiSizeCompact");
oRM.writeClasses();
oRM.write(">");
oRM.renderControl(oControl.getAggregation("_panel"));
oRM.write("</div>");
},
init : function () {
var that = this;
var _carousel = new Carousel({
pages : [new Label({
text : "Test"
}),
new Label({
text : "Test"
})]
});
var _closeIcon = new Icon({
src : "sap-icon://decline",
press :jQuery.proxy(this.onCloseInfoWindow,this)
});
var _toolBar = new Toolbar({
content : [
new Label({
text :"Information"
}),
new ToolbarSpacer(),
_closeIcon,
]
});
var _panel = new Panel({
headerToolbar : _toolBar
});
_panel.addContent(_carousel);
this.setAggregation('_panel',_panel);
},
onCloseInfoWindow : function(oEvent){
}
});
});
The onCloseInfoWindow is not triggering the press event when click on close icon
do i need to do some add the icon also as aggregation and need to render?
Do you want to fire the closed event that you have created in your custom control ?
Use the below code for calling the closed event from your custom control:
onCloseInfoWindow : function(oEvent){
//console.log('Called');
this.fireClosed(oEvent);
}
View XML: here, Control is my name for your control.
<c:Control closed='onClose'/>
Controller:
onClose:function(oEvent) {
console.log('Closed Called!');
}
Why are you using press :jQuery.proxy(this.onCloseInfoWindow,this) ? I've never used that jQuery.proxy in ui5.
For triggering the close function, you could do
var _closeIcon = new Icon({
src : "sap-icon://decline",
press : function(oEvent){
this.fireClosed(oEvent);
}.bind(this)
});

Meteor: publish dynamically requested range of items

I have huge collection of over 5000+ records. I want to be able to view records 10 at a time. How can I dynamically publish the data that way?
I've tried this so far:
My server.js file :
Meteor.methods({
publishSongs : function (first, last) {
Meteor.publish('adminSongs', function() {
return Songs.find({}, {
skip : first,
limit : last,
sort : {
date : -1
}
});
});
}
});
My client.jsfile :
Template.admin.events({
'click #previous' : function() {
updateSession(-10);
publishSong();
},
'click #next' : function() {
updateSession(10);
publishSong();
}
});
Template.admin.onCreated(function() {
Session.setDefault('limit', {
first : 0,
last : 10
});
publishSong()
})
function publishSong() {
Meteor.call(
'publishSong',
Session.get('limit').first,
Session.get('limit').last
);
}
function updateSession(value) {
Session.set('limit', {
first: Session.get('limit').first + value,
last: Session.get('limit').last + value,
});
}
The server is printing this error message:
Ignoring duplicate publish named 'adminSongs'
It seems like I'm using publications wrong and could use some guidance.
It doesn't look like you're never updating your Session.get('limit'). You'll need to update then you press next/previous otherwise you're always going to get the same records. You'll also need to change the way you're doing publications:
Template.admin.events({
'click #previous' : function() {
updateSession(-10);
},
'click #next' : function() {
updateSession(10);
}
});
Template.admin.onCreated(function() {
Session.setDefault('limit', {
first : 0,
last : 10
});
Template.instance().autorun( function() {
Template.instance().subscribe('adminSongs', Session.get('limit').first, Session.get('limit').last);
});
});
function updateSession(value) {
Session.set('limit', {
first: Session.get('limit').first + value,
last: Session.get('limit').last + value,
});
}
I'm assuming based on your code that you already have a helper defined to return the available songs. The code above makes it so that you have one subscription, and that subscription will update any time your session variable changes.
Your server code will also need to be updated:
Meteor.publish('adminSongs', function(first, last) {
return Songs.find({}, {
skip : first,
limit : last,
sort : {
date : -1
}
});
});
Can be outside of a Meteor.method.

ReferenceError: $firebase is not defined

I am trying to unit test this service using jasmine:-
my unit test is :-
describe('initial configuration for the test user', function () {
beforeEach(function(){
var config = {
'Apache 404' : {
content : {
type : 'tip',
template : 'yesNo',
text : 'Are you searching for status code 404?',
yesText : 'Try our more accurate field search',
attachTo : '#inputBox right',
yesActions : {
0 : {
type : 'replaceSubstring',
target : '#inputBox',
value : 'apache.status:404',
match : '404'
}
}
},
conditions : {
0 : {
type : 'valueChange',
target : '#inputBox',
textMatch : '(^|([\\s]+))404(([\\s]+)|$)',
preventSubmit : true
},
1 : {
type : 'contentPropertyLessThan',
propertyName : 'timesShown',
compareVal : 3
}
}
}
};
var clientName = 'testClient';
var fireRef = new Firebase('https://luminous-inferno-1740.firebaseio.com/' + clientName);
var fireSync = $firebase(fireRef);
fireSync.$set({'config' : config});
log.message = 'Resetting user data';
$log.debug(log);
userData.init(function(done) {
done();
});
});
it('should have a valid config', function () {
expect(Object.keys(userData.getConfig()).length > 1);
});
});
I am receiving an error :-
ReferenceError: $firebase is not defined
at Object.
Can somebody help me providing working example of my code with some explanation?
I also had the problem. I solved my problem by adding $firebaseArray in the parameter of the controller
.controller('ChatsCtrl', ['$scope','$firebaseArray','$rootScope',......
One thing should be noted that firebase has been updated that $firebase is no longer supported. You can only use $firebaseArray or $firebaseObject for retrieving the data.

how to access function in Json

I am able to access the onclick properties function for the printButton property at the end of the block. Although I am unable to initiate the onclick functions under the exportButton property.I have the following code.
B.exporting = {
type : "image/png",
url : "http://export.highcharts.com/",
width : 800,
enableImages : false,
buttons : {
exportButton : {
symbol : "exportIcon",
x : -10,
symbolFill : "#A8BF77",
hoverSymbolFill : "#768F3E",
_titleKey : "exportButtonTitle",
menuItems : [{
textKey : "downloadPNG",
onclick : function() {
this.exportChart()
}
}, {
textKey : "downloadJPEG",
**onclick : function() {
this.exportChart({
type : "image/jpeg"
})**
}
}, {
textKey : "downloadPDF",
onclick : function() {
this.exportChart({
type : "application/pdf"
})
}
}, {
textKey : "downloadSVG",
onclick : function() {
this.exportChart({
type : "image/svg+xml"
})
}
}
}]
},
printButton : {
symbol : "printIcon",
x : -36,
symbolFill : "#B5C9DF",
hoverSymbolFill : "#779ABF",
_titleKey : "printButtonTitle",
onclick : function() {
this.print()
}
}
}
};
I am binding keyboard controls to the click events using the jquery plugin this is what I used to print. This Works!:
Mousetrap.bind('ctrl+s', function(e) { B.exporting.buttons.printButton.onclick(this.print());
});
This code is what I tried to access an individual onclick function under the exportButton property in the json above
Mousetrap.bind('*', function(e) {B.exporting.buttons.exportButton.menuItems[0].onclick;});
The result i get is the value but i want to run the function as the onclick property does.Does anyone know how to run a function under a json property?I Appreciate any help here thanks folks.
Mousetrap.bind('click', B.exporting.buttons.exportButton.menuItems[0].onclick);
Your ctrl-s binding also looks wrong, it should be:
Mousetrap.bind('ctrl+s', B.exporting.buttons.printButton.onclick);
The printButton.onclick function doesn't take an argument. Your binding calls this.print before calling the printButton.onclick function, and then the printButton.onclick function
does it again.

Backbone: Set the parent view's model (different from child model)

I have a Parent and Child view. The Child view extends the Parent events with:
initialize : function() {
// don't overwrite parent's events
this.events = _.extend( {}, ExerciseRowView.prototype.events, this.events);
},
However, the Parent expects a ParentModel and the Child expects a ChildModel, so when an event is passed to the Parent, the model is the ChildModel. How can I set the Parent model to be different from the Child model?
Thanks!
Here's the source, as requested.
ParentView aka ExerciseRowView:
var ExerciseRowView = Parse.View.extend( {
tagName : 'div',
className : 'exerciseWrapper',
template : _.template(exerciseElement),
events : {
'click .icon_delete' : 'confirmDelete',
'click .name' : 'showDetailsPopup'
},
confirmDelete : function() {
var that = this;
if(confirm("Are you sure you want to delete this exercise?")) {
this.destroy({
success: function(exercise) {
// log the action
Log.add(Log.ACTION_EXERCISE_DELETED, exercise.get("name"));
that.$el.fadeOut();
}
});
}
},
showDetailsPopup : function() {
(new ExerciseDetailsView({model: (this.model.constructor == Exercise ? this.model : this.model.get("exercise"))})).render();
},
// accept data as a parameter for workoutexercises
render : function(data) {
_.defaults(data, {
exercise: this.model,
Muscle : Muscle,
Equipment : Equipment,
Exercise : Exercise,
Break : Break,
HTMLHelper : HTMLHelper,
User : User
});
$(this.el).html(this.template(data));
return this;
}
});
ChildView aka WorkoutExerciseRowView:
var WorkoutExerciseRowView = ExerciseRowView.extend( {
events : {
"click .icon_randomize" : "changeToRandomExercise"
},
initialize : function() {
// don't overwrite parent's events
this.events = _.extend( {}, ExerciseRowView.prototype.events, this.events);
},
render: function() {
// override the template data with workout exercise template data
return ExerciseRowView.prototype.render.call(this, {
workoutExercise : this.model,
exercise : this.model.get("exercise"),
workoutSection : this.model.get("section"),
isEditable : true,
number : this.options.number,
WorkoutExercise : WorkoutExercise,
WorkoutSection : WorkoutSection
});
},
changeToRandomExercise : function(e) {
// pick a random alternative exercise
var newExerciseId;
do {
newExerciseId = _.keys(this.model.get("alternativeExercises"))[ Math.floor(Math.random() * _.keys(this.model.get("alternativeExercises")).length) ];
} while(newExerciseId == this.model.get("exercise").id);
// grab it
var that = this;
(new Parse.Query(Exercise)).get(newExerciseId, {
success: function(exercise) {
// update the workout exercise
that.model.set("exercise", exercise);
// render it
that.render();
}
});
}
});
Currently (as you can see), I test to see if this.model.constructor == Exercise inside ExerciseRowView. If it is not, I know that I have a WorkoutExercise, inside which is an Exercise, so I use this.model.get("exercise"):
showDetailsPopup : function() {
(new ExerciseDetailsView({model: (this.model.constructor == Exercise ? this.model : this.model.get("exercise"))})).render();
},
This doesn't seem like the cleanest possible solution, though.
what I could think of is that you define function for each view
ParentView
getExercise: function() {
return this.model;
}
ChildView
getExercise: function() {
return this.model.get('exercise');
}
And then change the function
showDetailsPopup: function() {
(new ExerciseDetailsView({model: this.getExercise()})).render();
}
How about that?

Categories

Resources