AngularJs promise in forEach - javascript

I have method for getting data from server.
And I use it in foreach, and after it need bind him to $scope variable.
Like this:
var qualityMix = [];
var engagementMix = [];
angular.forEach(versions, function (versions) {
qualityMix.push(qualityScoreByTimeWithAppVerPromise(versions.version));
engagementMix.push(engagementByTimeWithAppVerPromise(versions.version));
});
$scope.qualityScoreByTimeMix = function () {
return $timeout(function () {
return $q.all(qualityMix).then(function (data) {
return {series: data};
});
});
};
$scope.engagementTimeMix = function () {
return $q.all(engagementMix).then(function (data) {
return {series: data};
});
};
qualityScoreByTimeWithAppVerPromise and engagementByTimeWithAppVerPromise it is functions for getting data from server. Then $scope.engagementTimeMix and $scope.qualityScoreByTimeMix need return functions with promise (is okay).
This code working but not always, some times I catch exceptions $scope.xxx is not a function.
I don't know how to fix it. Help me please. Thanks a lot!
UPD
It is code for build charts.
<div class="section">
<highchart id="mix_quality_score_by_time" type="area" data="qualityScoreByTimeMix"
chart-style="qualityScoreMixChartStyle"></highchart>
</div>
And my directive I invoke in other page, like this:
<compare-versions id="compare_versions_panel" start-day="timeFilter.startDay()"></compare-versions>
$scope.xxx is not a function
I mean what I catch message in chrome console what
$scope.engagementTimeMix and $scope.qualityScoreByTimeMix it not a function

You can use ng-if and draw graph when you already have value
example:
<div class="section" ng-if="qualityScoreByTimeMix">
<highchart id="mix_quality_score_by_time" type="area" data="qualityScoreByTimeMix" chart-style="qualityScoreMixChartStyle"></highchart>
</div>

Related

Call function on html page from controller in AngularJS

Long story short:
I have the following file structure:
class RandomCtrl {
constructor(randomService) {
this.randomService = randomService;
...
}
$onInit() {
getData.call(null, this);
}
...
}
updateLegendChart(){
RandomCtrl.chartStuff.chart.unload("ID-1234");
}
function getData(RandomCtrl) {
RandomCtrl.ChartDataService.getData(DemandCtrl.dataParams).then(result => {
RandomCtrl.result = result.data;
RandomCtrl.siteNames = result.data.map(element => element.SiteName);
RandomCtrl.keys = Object.keys(result.data);
RandomCtrl.chartStuff = getChart(result.data);
RandomCtrl.chartStuff.chart.unload("ID-1234"); ////<-HERE IT WORKS!!!
}).catch((e) => {
console.log(e);
});
}
function getChart(data) {
const chartOptions = getWeekHourlyOptions(data);
const allCols = [].concat(chartOptions.dataColumns);
...
return {allCols, chart};
}
...
RandomCtrl.$inject = ['randomService'];
export const Random = {
bindings: {
data: '<',
siteNames: '<'
},
templateUrl: randomPageHtml,
controller: RandomCtrl
};
I have a chart containing multiple lines each of them representing a site, I want to remove or add them when I click on their name in a legend section.
I do this by using load and unload methods of Billboard.js.
If a write it inside getData(), the line with HERE IT WORKS, it works but it does it every time I run the code, I want to do it only when I click a button.
The problem is that I cannot glue this functionality to an ng-click into an html page.
This is the html page:
<div class="demand page">
<div class="chart-legend-container">
<div ng-repeat="site in $ctrl.keys">
<chart-legend site="$ctrl.siteNames[site]" keys= "$ctrl.keys"></chart-legend>
<button ng-click="$ctrl.updateLegendChart()">CLICK ME</button>
</div>
<div>
</div>
My approach was to use updateLegendChart() which is a method on the controller which should be called when ng-click is triggered.
The method is in the controller and looks like this:
updateLegendChart(){
RandomCtrl.chartStuff.chart.unload("ID-1234");
}
The error says:
TypeError: Cannot read property 'chart' of undefined
Any idea how to call that function properly?
Inside $onInit hook 'this' keyword refers to the $onInit context , not to RandomCtrl
$onInit() {
getData.call(null, this);
}
Probably something you don't want to do, because then you're appending all those properties (result, chartStuff, etc.) to the wrong object.
..
//here RandomCtrl is still $onInit context, and not the the class context
RandomCtrl.chartStuff = getChart(result.data);
As a consequence when you invoke updateLegendChart(), RandomCtrl doesn't have any chartStuff field, thus you get the exception "TypeError: Cannot read property 'chart' of undefined"
updateLegendChart(){
RandomCtrl.chartStuff.chart.unload("ID-1234");
}
if you try passing RandomCtrl directly you should be fine.
$onInit() {
getData.call(null, RandomCtrl);
}
To make it work as expected it should be replaced RandomCtrl with this inside updateLegendChart() method like this:
updateLegendChart(siteNames){
this.chartStuff.chart.unload(siteNames);
}
It doesn't need to modify $onInit() method, it should be let as it is

Why my applyBindings doesn't work? Knockout

Hello I am trying simply to create input and iframe and when I paste the YouTube link the iframe should change with the new src. I have done this so far
<div class="heading">id <input data-bind="text: youtubeLink"/></div>
<iframe id="player" type="text/html" width="444" height="250" frameborder="0" data-bind="attr: { src: linkEmbed }"></iframe>
And in the script:
function MyViewModel() {
this.youtubeLink = ko.observable('https://www.youtube.com/watch?v=4UNkmlCKw9M');
this.linkEmbed = ko.pureComputed({
read: function () {
var extract = this.youtubeLink().replace("/watch?v=", "/embed/");
console.log(extract)
return extract;
},
write: function (value) {
this.youtubeLink();
},
owner: this
});
}
ko.applyBindings(MyViewModel());
This works exactly as I want but the video wont change if I paste another link in the input.
I am using this from knockout documentation: http://knockoutjs.com/documentation/computed-writable.html
You have several problems:
You don't call new on your model, but you wrote it as a constructor
You use text binding instead of value binding for your input
Your computed's write doesn't assign, but you don't need it anyway
Once you correct those, it works.
function MyViewModel() {
var model = {};
model.youtubeLink = ko.observable('https://www.youtube.com/watch?v=4UNkmlCKw9M');
model.linkEmbed = ko.pureComputed(function () {
var result = model.youtubeLink().replace("/watch?v=", "/embed/")
return result;
});
return model;
}
ko.applyBindings(MyViewModel());
http://jsfiddle.net/ueoob7ne/2/
TLDR: jQuery hides knockout bind errors.
Another thing that breaks it....
jQuery is known to catch exceptions and hide them. I had to step through knockout-debug.js AND THEN jquery.js until i got to a part that looks like this (around line 3600)
// Only normal processors (resolve) catch and reject exceptions
process = special ?
mightThrow :
function() {
try {
mightThrow();
} catch ( e ) {
wouldn't you know it... I put a watch on (e) an here was what I found hidden in there:
Error: Unable to process binding "text: function(){return ko.toJSON(vm.model(),null,2) }"
Message: Multiple bindings (if and text) are trying to control descendant bindings of the same element

How do you make the meteorjs reactive-froala editor update when its value changes?

I'm using meteorjs and the froala-reactive editor.
In my router I return the collection data to the template, which works fine.
But I need the ability to update the contents of editor. What is the best way to update _value?
The template code:
{{> froalaReactive _onbeforeSave=doSave inlineMode=false _value=getText}}
The router.js code:
Router.route('admin/pages/:_id', function () {
this.render('Page', {
data: function () {
Session.set('editorContent', 'editor content here');
return Pages.findOne({_id: this.params._id})
}});
});
Helper function:
Template.Page.helpers({
getText: function () {
var self = this;
return function (e, editor, data) {
return Session.get("editorContent");
};
}
});
I expect that when the session variable editorContent changes the displayed content in the editor updates, but this is not working.
Your helper function should simply return the Session value, instead of a function.
Like this:
getText: function () {
return Session.get('editorContent');
}
Here's a working example that you can clone and play around with.

Removing item from observablearray in Knockoutjs

I am trying to learn Knockoutjs and I am having some issues with adding and removing objects to an observablearray.
I have the following viewmodel in which I fetch some data from a webservice and populate some html. This works fine. But what does not work is removing items from the observablearray since it seems the click-event does not call removeEmployee.
function EmployeeViewModel(){
var self=this;
self.employees=ko.observableArray();
self.removeEmployee = function(item) {
self.employees.remove(item);
};
}
function success(data) {
EmployeeViewModel.employees=ko.mapping.fromJS(data);
ko.applyBindings(EmployeeViewModel);
};
ApiCall({
data: {
[get data]
},
onSuccess: function(data){success(data.result)}
});
and the following html:
<div data-bind="foreach: employees">
<h2>Hello, <span data-bind="text: full_name"> </span>!</h2>
<button data-bind="click: $parent.removeEmployee">Remove</button>
</div>
I have tried setting up a jsfiddle here: http://jsfiddle.net/8yX5M/ in which removing items does work. The difference is, that in the jsfiddle the items are not fetched from an external source and that I use removeEmployee rather than $parent.removeEmployee.
Any ideas why the non-jsfiddle version is not working ?
thanks
Thomas
Because your success function isn't setting the value of the observableArray, it is resetting the object's definition -
function success(data) {
EmployeeViewModel.employees(ko.mapping.fromJS(data));
ko.applyBindings(EmployeeViewModel);
};
Use the setter function on EmployeeViewModel.employees by using the () and passing in a value.
Turned out it was because I did not instantiate EmployeeViewModel to a global variable before mapping the data.
The working code is
'use strict';
var employeeViewModel=new EmployeeModel();
function EmployeeModel(){
var self=this;
self.employees=ko.observableArray();
self.removeEmployee = function(item) {
self.employees.remove(item);
};
}
function getEmployeesSuccess(data,controlIds) {
employeeViewModel.employees=ko.mapping.fromJS(data);
var _i=0;
for (var _total=controlIds.length; _i < _total; _i++) {
ko.applyBindings(employeeViewModel,$("#"+controlIds[_i])[0]);
}
};
/* Databinds employeedata to an array of controls */
/* controlIds=Array of controls*/
function DataBindEmployees(controlIds)
{
ApiCall({
data: {
[get data]
},
onSuccess: function(data){getEmployeesSuccess(data.result, controlIds)} });
};

Accessing function from within another with Javascript

I'm trying to get the jquery loadmask addon to work that will mask elements (for loading content). I'm using knockout.js, and when if I mask an element outside of my viewmodel it works, but I want to mask it upon submitting a POST request, and then unmask when I receive it. I'm getting an "object has no method mask" error from this. I'm not quite sure how to go about setting up an object to access it.
This works, but it's not what I want. I noted in the code where I would like to call mask from
<div id = "register_container">
<div data-bind="visible: register()">
<div id = "register_form"> <!--this is the div I want to mask -->>
<button data-bind="click: submitRegistration">Submit</button>
</div>
</div>
</div>
function MyViewModel(){
self.submitRegistration = function(){
//I want to mask here. When I try it says Object[object object] has no method mask
$.post....{
if(data.result == success){
// andunmask here
}
}
}
}
$("#register_form").mask("Waiting..."); //the masking works when I place it here, but it's always enabled and I want it inside the viewmodel where I noted so it only works when the POST request is in process
That's great and all, but I want to mask something from inside the viewmodel where I noted. How can I accomplish this?
I see several things that could be the problem.
Frist, you're doing assignment as opposed to comparison in the if statement. Use this instead:
if(data.result == success){
or even
if(data.result === success){
Second is the fact that I don't quite understand your code self.submitRegistration(){, which typically looks more like this:
var MyViewModel = function () {
var self = this;
self.submitRegistration = function() {
};
};
Then, if I mock the $.post call, it would work like this:
var MyViewModel = function () {
var self = this;
self.register = ko.observable(true);
self.submitRegistration = function() {
$("#register_form").mask("Waiting...");
// Mock $.post
window.setTimeout(function () {
if (1 == 1) {
// andunmask here
$("#register_form").unmask();
}
}, 3000);
}
};
ko.applyBindings(new MyViewModel());
See this fiddle for a demo.
You could even have Knockout help you find the element to look for:
See this updated fiddle for a demo of that.
// Use the "event" parameter to find the element...
self.submitRegistration = function(data, event) {
$(event.target).closest('#register_form').mask("Waiting...");
Hope it helps.

Categories