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
I have an ul that loads data from an array using a ng-repeat. The array is pretty big and the load of the list lasts too much.
I want to put a loader so its shown at the begining of the loading and hides just, and this is the important part, when that ul is fully rendered in the html.
My problem is that I can’t find a way to throw a function when the ul is fully rendered.
The way I have this right now is:
Controller:
function renderMatters() {
api_horus.getMatters()
.success(function (data)
{
$scope.matters = data.matters;
}
})
.error(function (error) {
status = 'Unable to load matters data: ' + error.message;
});
}
HTML:
<ul ng-model="matters" class="list clearfix" ng-class="{'floatingList':isHorizontal}">
<li ng-repeat="matter in filtered = (matters | filter:searchText) class="matter_grid_li">
…
I’ve tried these possible solutions already:
1. Using ngRoute and $viewContentLoaded:
In angular-directive.js
$scope.$on('$viewContentLoaded', function() {
App.initComponent();
alert("Loaded");
});
This works, but the alert is not thrown when the is fully loaded, but half way of it more or less.
2. Using a template, ng-include and onload
I put the ul in a different file and call it as a template
<div ng-include src="'template/test-loading.html'" onload="alert(‘ends’);"></div>
It does not work either. The alert shows at the middle of the load.
I even tried putting the ng-repeated li as the template instead of the ul because I read that the onload is thrown only when the PARENT element is loaded. Not success either.
How can I know when an element is fully rendered in HTML?
Tx in advance.
just create one div with
<div ng-if="showMsg">{{msg}}</div>
<div ng-if="!showmMsg">
<ul ng-model="matters" class="list clearfix" ng-class=" {'floatingList':isHorizontal}">
<li ng-repeat="matter in filtered = (matters | filter:searchText) class="matter_grid_li"></div>
In controller :
$scope.showMsg = true;
$scope.msg = "loading...";
function renderMatters() {
api_horus.getMatters()
.success(function (data)
{
$scope.matters = data.matters;
$scope.showMsg = false;
$timeout(angular.noop)
}
})
.error(function (error) {
status = 'Unable to load matters data: ' + error.message;
});
}
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
I am trying to load following js with partial, using $http . my problem is when i run code firest time get controller undefined type error,
where as on second click it works fine.
JAVASCRIPT::
$scope.loadPage = function(testurl) {
$scope.testpage = testurl;
var element = angular.element($("#data_template"));
$http.get(testurl).success(function(data) {
element.html(data);
$compile(element.contents())($rootScope);
$('.div1').slideUp('slow');
$('#data_template').slideDown('slow');
});
};
HTML::
<div class="movie-listing movie-listing-step"
ng-controller="ProductAddController">
<!-- all DOm PArt -->
</div>
mt js comes from s3 bucket, so may be js taking long time to come and $compile starts before js load . how to solve this problem ??
Here I have used $viewContentLoaded
app.controller('ProductAddController', function ($scope, $timeout) {
$scope.loadPage = function(testurl) {
$scope.testpage = testurl;
var element = angular.element($("#data_template"));
$http.get(testurl).success(function (data) {
element.html(data);
$compile(element.contents())($rootScope);
$('.div1').slideUp('slow');
$('#data_template').slideDown('slow');
});
}
$scope.$on('$viewContentLoaded', function(event) {
$timeout(function() {
$scope.loadPage(testurl);
},0);
});
I'm new with AngularJS. I must create customs controls for a video player (HTML5 <video>).
Basically, I would use getElementById('myvideotag'), listen clicks on the video for play/pause.
How I can do this with AngularJS ? Binding the click with ng-click="videoPlayPause()" but then, how I play or pause the video. How I use the classic methods of <video> ?
I guess it's really simple... I didn't get all the AngularJS concepts yet !
Thank you :)
Oh, the code... in the view:
<video autoplay="autoplay" preload="auto" ng-click="video()">
<source src="{{ current.url }}" type="video/mp4" />
</video>
In the right controller:
$scope.video = function() {
this.pause(); // ???
}
For full control, like behaviour and look&feel, I'm using videoJS in angular.
I have a ui-video directive that wraps the video HTML5 element. This is necessary to overcome a problem of integration with AngularJS:
m.directive('uiVideo', function () {
var vp; // video player object to overcome one of the angularjs issues in #1352 (https://github.com/angular/angular.js/issues/1352). when the videojs player is removed from the dom, the player object is not destroyed and can't be reused.
var videoId = Math.floor((Math.random() * 1000) + 100); // in random we trust. you can use a hash of the video uri
return {
template: '<div class="video">' +
'<video ng-src="{{ properties.src }}" id="video-' + videoId + '" class="video-js vjs-default-skin" controls preload="auto" >' +
//'<source type="video/mp4"> '+ /* seems not yet supported in angular */
'Your browser does not support the video tag. ' +
'</video></div>',
link: function (scope, element, attrs) {
scope.properties = 'whatever url';
if (vp) vp.dispose();
vp = videojs('video-' + videoId, {width: 640, height: 480 });
}
};
});
How about this:
In your HTML, set ng-click="video($event)" (don't forget the $event argument), which calls the following function:
$scope.video = function(e) {
var videoElements = angular.element(e.srcElement);
videoElements[0].pause();
}
I believe this is the simplest method.
Documentation for angular.element
Also, this might help you get used to Angular: How do I “think in AngularJS/EmberJS(or other client MVC framework)” if I have a jQuery background?
You could also take a look to my project Videogular.
https://github.com/2fdevs/videogular
It's a video player written in AngularJS, so you will have all the benefits of bindings and scope variables. Also you could write your own themes and plugins.
I also used videojs
bower install videojs --save
I wanted to use my directive in a ng-repeat and with a scope object, so... here's my version of it with props to Eduard above. I didn't seem to have a problem with the video player disposal, but the source tag issue referenced was an actual problem.
I also decided to write this up as an answer, so that I could give an example of how one might want to handle the videojs events.
IMPORTANT! Please note I am using Angular.js with Jinja2 templates, so I had to change my Angular HTML interpolation markers to {[ ]} from {{ }} in case anyone notices that as weird. I'll include that code too, so it's not weird for anyone.
Interpolation tweak
app.config(['$interpolateProvider', function($interpolateProvider) {
$interpolateProvider.startSymbol('{[');
$interpolateProvider.endSymbol(']}');
}]);
Directive
angular.module('myModule').directive('uiVideo', function () {
// Function for logging videojs events, possibly to server
function playLog(player, videoId, action, logToDb) {
action = action || 'view';
var time = player.currentTime().toFixed(3);
if (logToDb) {
// Implement your own server logging here
}
// Paused
if (player.paused()) {
console.log('playLog: ', action + " at " + time + " " + videoId);
// Playing
} else {
console.log('playLog: ', action + ", while playing at " + time + " " + videoId);
if (action === 'play') {
var wrapFn = function () {
playLog(player, videoId, action, logToDb);
};
setTimeout(wrapFn, 1000);
}
}
}
return {
template: [
'<div class="video">',
'<video id="video-{[ video.id ]}" class="video-js vjs-default-skin img-responsive" controls preload="none"',
' ng-src="{[ video.mp4 ]}"',
' poster="{[ video.jpg ]}"',
' width="240" height="120">',
'</video>',
'</div>'
].join(''),
scope: {
video: '=video',
logToDb: '=logToDb'
},
link: function (scope, element, attrs) {
scope.logToDb = scope.logToDb || false;
var videoEl = element[0].children[0].children[0];
var vp = videojs(videoEl, {},
function(){
this.on("firstplay", function(){
playLog(vp, scope.video.id, 'firstplay', scope.logToDb);
});
this.on("play", function(){
playLog(vp, scope.video.id, 'play', scope.logToDb);
});
this.on("pause", function(){
playLog(vp, scope.video.id, 'pause', scope.logToDb);
});
this.on("seeking", function(){
playLog(vp, scope.video.id, 'seeking', scope.logToDb);
});
this.on("seeked", function(){
playLog(vp, scope.video.id, 'seeked', scope.logToDb);
});
this.on("ended", function(){
playLog(vp, scope.video.id, 'ended', scope.logToDb);
});
}
);
}
};
});
Directive HTML usage
<div ng-repeat="row in videos">
<ui-video video="row"></ui-video>
</div>