Hey everyone I am currently building a mobile app for my company and I have a angularjs function that is currently adding a css class based on whether it's day or night. It does what it's supposed to do perfectly by putting a day or night image based on whether it's day or night.
The issue I am running into is that when testing the app right before the clock let's say goes from 1:59 to 2 it doesn't change the background image until you refresh the page. I want it to automatically change the background image without having to refresh and can't seem to find a way to do it. I will link the code here!
Any help is appreciated as it has me completely stumped...
Here is what I have in my html.
<div class="dashboard-welcome" ng-class="{'hide-homescreen-image-landscape'
: isLandscape, 'homescreenImageDay' : isDay, 'homescreenImageNight' :
isNight }">
Here is where the function is being called
angular.module('starter').controller('homeCtrl', ['$scope', '$interval',
'jsBridge', 'authService', 'tierService', '$window', fnc]);
function fnc($scope, $interval, jsBridge, authService, tierService, $window)
{
$scope.$on('$ionicView.afterEnter', function (event, viewData) {
setOrientation();
$scope.isDay = !isDayTime(1000);
$scope.isNight = isDayTime(1000);
});
Here is where the function is instantiated. Basically checking what time it is.
var isDayTime = function () {
var h = new Date().getHours();
if (h >= 14) {
return true;
} else {
return false;
}
}
I can't supply all the code since this application is thousands of lines long but this is the working function as of now. Just need it to switch background images without refreshing using angularjs...
Assuming that inside your div you are using an background-image: url("...")
I suggest you set up an object to hold a single $scope.isDay value instead of doing the calculation twice.
Also use the $interval service to check every nth millisecond to update your $scope.isDay value.
The code below works fine in dynamically changing a background image on a page using its CSS class.
HTML:
<div ng-class="data.isDay ? 'back1' : 'back2'"></div>
JS:
var exampleApp = angular.module('exampleApp', []);
exampleApp.controller('exampleController', function($scope, $interval) {
$scope.data = {
isDay: true,
classVal: {
one: 'text-danger',
two: 'text-success'
}
};
$interval(function() {
$scope.toggleImage();
}, 600);
$scope.toggleImage = function() {
$scope.data.isDay = ($scope.data.isDay ? false : true)
};
$scope.toggleImage();
});
Here is also a plnkr demo
Related
I have a custom directive which is very similar to a drop down. Items in the drop down menu are associated with file names of certain videos. A div below the drop down displays a default video file (I have done this via Videoangular).
Whenever I make a selection from the drop down menu, I am changing the default variable containing filename (String) to the one I want. But, the same is not reflected in the div.
My objective is to refresh div containing the video with appropriate video whenever a selection is made from the drop down menu.
This is my controller:
angular.module('myApp',
[
"ngSanitize",
"com.2fdevs.videogular",
"com.2fdevs.videogular.plugins.controls",
"com.2fdevs.videogular.plugins.overlayplay"
]
)
.controller('ctrl',
["$rootScope", "$scope", "$state", "$log", "Restangular",
function ($rootScope, $scope, $state, $log, Restangular) {
'use strict';
var vm = this;
//DEFAULT VIDEO FILE NAME
vm.fields = "WaterCool1";
this.config = {
sources: [
{src: "assets/data/"+vm.fields+".mp4", type: "video/mp4"}
]
};
vm.loadPage = loadPage;
vm.coolingSystemTypeSelector = {coolingSystemTypeSelector:{}};
getAll('cooling-system-type').then(
function(objs) {
$log.debug("get Cooling System Type", objs);
vm.coolingSystemTypeSelector = objs.selector;
vm.fields = "WaterCool1";
vm.coolingSystemTypeSelector.onSelect = function (selection) {
if(!selection){
return;
}
$log.debug("Cooling System Type Selection == ", selection);
if(selection.label==="ACC"){
vm.fields = "AirCool";
}else if(selection.label === "WCC-CT"){
vm.fields = "WaterCool1";
}else if(selection.label === "WCC-DC"){
vm.fields = "WaterCool2";
}
};
}
);
///.....
}
]
);
This is my HTML:
<div>
<selector form="form" columns=vm.columns target="vm.coolingSystemTypeSelector"></selector>
</div>
<hr>
<div id="refreshThisDiv">
<!--I want to refresh this div-->
<videogular vg-theme="vm.config.theme">
<!--VIDEOGULAR CODE-->
</videogular>
</div>
What you need is not to refresh the div. You need angular to refresh the div based on you modifying bound properties.
Your declaration of this.config is actually static and you are never modifying the value of this.config.sources src after instantiation. As that code is running only once it will forever remain as "assets/data/WaterCool1.mp4".
What you need to do instead at least, is to modify this value upon selection of an option in the drop-down. Something like:
// ...
var that = this;
getAll('cooling-system-type').then(
// ... inside onSelect ...
if(selection.label==="ACC") {
that.config.sources = [{src: "assets/data/AirCool.mp4", type: "video/mp4"}];
}
// ...
Even then, with this code, you might need to trigger a manual $apply as angular may not be aware of your change to the field via this onSelect event handling. Ideally you will be able to bind the event to the function directly in HTML by using ng-change and avoid the need for that.
If you provide a full sample (https://plnkr.co/edit/), it's easier to guide you to a solution and explain in without the need to rewrite your original code.
I developed a re-usable AngularJS service that allows the user to start, stop, and re-set a countdown timer from a view by clicking on buttons in the view. The re-usable service can be accessed through any controller that includes it. The working code for the minimal example countdown app can be viewed by clicking the link to this plnkr.
But I want the timer to be re-set to its max default value every time a user moves the mouse anywhere in the browser window. This means adding $window.addEventListener(...) somewhere in the service because the service has to be re-usable across any controller, while also responding to any movement of the mouse anywhere over the browser window, even the areas not contained within an HTML element linked to a controller. Thus, I cannot simply resort to adding ng-mousemove="somemethod()" in the html body tag the way this other example does. I would also prefer to avoid the $rootScope.broadcast approach taken by this other posting because I would like to keep the code isolated in the service as much as possible.
What specific changes need to be made to the code below so that the timer will be re-set to its default value any time the user moves the mouse anywhere in the browser window?
Though all of the code is in the plnkr for easy editing, I am also including it here.
index.html is:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Timer</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<script src="myTimer.js" type="text/javascript"></script>
<script src="exampleController.js" type="text/javascript"></script>
<script src="app.js" type="text/javascript"></script>
</head>
<body ng-app="intervalExample">
<div>
<div ng-controller="ExampleController">
Test variable: {{ mytimer.testvariable }}<br>
Time Remaining : <font color='red'>{{mytimer.timeleft}}</font>
<br>
<button type="button" data-ng-click="mytimer.startCountdown()">Start Countdown</button>
<button type="button" data-ng-click="mytimer.stopCountdown()">Stop Countdown</button>
<button type="button" data-ng-click="mytimer.resetCountdown()">Reset Timer</button>
</div>
</div>
</body>
</html>
app.js is:
angular.('intervalExample',['ExampleController']);
exampleController.js is:
angular
.module('ExampleController', ['mytimer'])
.controller('ExampleController', ['$scope', 'mytimer', function($scope, mytimer) {
$scope.mytimer = mytimer;
}]);
myTimer.js is:
angular
.module('mytimer', [])
.service('mytimer', ['$rootScope', '$interval', function($rootScope, $interval) {
var $this = this;
this.testvariable = "some value. ";
this.timeleft = 15;
var stop;
this.startCountdown = function() {
// Don't start a new countdown if we are already counting down
if ( angular.isDefined(stop) ) return;
stop = $interval(function() {
if ($this.timeleft > 0 ) {
$this.timeleft = $this.timeleft - 1;
} else {
$this.stopCountdown();
}
}, 1000);
};
this.stopCountdown = function() {
if (angular.isDefined(stop)) {
$interval.cancel(stop);
stop = undefined;
}
};
this.resetCountdown = function() {
this.timeleft = 15;
};
// this.$on('$destroy', function() {
// Make sure that the interval is destroyed too
// $this.stopCountdown();
// });
function subsFunc() {
$window.addEventListener('mousemove', function(e) {
$this.timeleft = 15;
})
}
}]);
Issues to consider:
You are never calling subsFunc() and when you do will see that
$window is not injected in service
You will need to debounce the mousemove callback since the event
triggers about every pixel. Resetting your timer every pixel makes no
sense and would cause significant needless digests.
Use of directive for buttons would negate needing to inject in numerous controllers
Same for timer display ... can be directive and depending on UI combined with buttons
http://plnkr.co/edit/39FGMocKB5GtQWnI1TFw?p=preview
I have sidebar which contains a list of tags, when you click on a tag I use the TagDetailsFactory to send a tag into the scope of the view controller.
Everything works great except for when you hover over a tag in the TagDetailsFactory scope.
The tagDetails template does not show up, however if you hover over the same tag in the sidebar scope, the tagDetails shows up in both. This is wrong.
Hovering over a tag in the sidebar should only show the tag details for that tag and the same for the tags inside the view scope.
Hovering over a tag in the view scope, doesn't display it's details
Hovering over a tag inside of the sidebar scope, should only show details for it's tag, and not the tag in the view scope, like it does here:
Steps:
- The first tags Array is in the cnt controller
- When you click on a tag, it gets stored in the TagDetailsFactory service
- I then broadcast an event to the view controller to then call the getTagDetails function in TagDetailsFactory to retrieve the saved tags and store them into the viewTags array in the view controller.
// Code goes here
angular.module('app', [])
.directive('tagDetails', function() {
return {
restrict: "E",
link: function($scope, el, attrs) {
// console.debug($scope, attrs);
},
scope:{
tag:'='
},
template: '<div ng-show="tag.showDetails">{{tag.details}}</div>'
};
})
.factory('TagDetailsFactory', function() {
var savedTags = [];
var saveTagDetails = function(tag) {
savedTags.push(tag);
}
var getTagDetails = function() {
return savedTags;
}
return {
saveTagDetails : saveTagDetails,
getTagDetails : getTagDetails
};
})
.controller('sidebar', function($scope,
$rootScope,
TagDetailsFactory) {
$scope.tags = [];
for(var i = 0; i < 10; i++) {
$scope.tags.push(
{ name: 'Foo Bar ' + i, details: 'Details' + i }
);
}
$scope.showTagDetails = function(t) {
t.showDetails = true;
}
$scope.leaveTag = function(t) {
t.showDetails = false;
}
$scope.sendTag = function(t) {
TagDetailsFactory.saveTagDetails(t);
$rootScope.$broadcast('updateView');
}
})
.controller('view', function($scope,
$rootScope,
TagDetailsFactory) {
$scope.viewTags = [];
$scope.$on('updateView', function() {
$scope.viewTags = TagDetailsFactory.getTagDetails();
});
$scope.showTagDetails = function(v) {
v.showDetails = true;
}
$scope.leaveTag = function(v) {
v.showDetails = false;
}
});
Do I have to create a 2nd directive here? To be the template for the tag details in the view scope? Or can my current tagDetails directive be repurposed somehow in an Angular way?
I forked your Plunker with a working copy, and I'll explain the changes I made.
You have two issues with the code here. The first is a simple typo, which is causing your header to not reference the correct function for mouseover. Your functions are calling showTagDetailsView(v)and leaveTagView(v), but they are named showTagDetails and leaveTag on the controller.
The second issue is with the way that the items are added to the savedTags[]. In JavaScript, objects are passed by reference. when you call savedTags.push(tag);, you are pushing a reference to the same object into the new array. Any changes made to the object in one array will be reflected in the other array.
Instead, what you want is a separate copy of the object in the savedTags[]. This can be accomplished by using angular.copy. Note that I also reset tag.showDetails = false; before making the copy, else the new copy will have it set to true, and the details will be showing the instant the copy appears, even though you are hovering over the other element when you click it.
var saveTagDetails = function(tag) {
tag.showDetails = false;
savedTags.push(angular.copy(tag));
}
Just a side note, you might also have an issue with CSS here, as hovering seems to change the position of the lists, and in some cases the hover actually causes the tag to move itself out of the hover, causing a bounce effect.
I have a webpage with a very simple structure, imagine left navigation, header bar and content area. If I have to describe it with code it looks like this
<body>
<aside ng-controller="SideNavCtrl">Left Nav data</aside>
<header ng-controller="HeaderCtrl">Page Title</header>
<section>
<article ng-view>Page Content</article>
</section>
I'm using ngRoute and change my routes left and right. What I want is to update "Page Title" from the controller that accepted the route. I've tried hundreds of different ways but changing variable never forces header data update. Here's my latest variable share between controllers (which doesn't work)
app.controller("HeaderCtrl", ["$scope", "HeaderData", function($scope, HeaderData) {
$scope.title = HeaderData.title();
}]);
app.factory("HeaderData", function() {
var title = 'Default';
return {
title: function() { return title; },
setTitle: function(newTitle) { title = newTitle; }
}
});
Later on in the routed controller I go like this
app.controller("PreviewBuildCtrl", ["$scope", "$routeParams", "$location", "BuildsAPIService", "HeaderData", function($scope, $routeParams, $location, BuildsAPIService, HeaderData) {
$scope.build = BuildsAPIService.getBuildById($routeParams.buildId);
HeaderData.setTitle("Previewing Build");
console.log(HeaderData);
if (!$scope.build) {
$location.url('/builds/' + $routeParams.profession);
}
}]);
The problem is that using HeaderData.setTitle() doesn't update the header contents. I really don't want to write jQuery code for this task. There must be a smarter, more angular way of doing this.
All I want to do is change the header title to "Page B" when you move to page b from page a and I consider adding breadcrumbs plugins an overkill for the task at hand :(
You break the data binding via title = newTitle. The controllers are pointing to the old title, but HeaderData is not pointing to it anymore so it is lost. You need another dot.
this.title = newTitle;
The title method should also return this.title, of course.
I'm creating an AngularJS and HTML5-based game that consists of users finding and clicking on birds. I want birds to appear at random places on the screen. If you click a bird - or if you wait more than 5 seconds - the bird automatically disappears.
I'm new to Angular, but I thought I would approach this problem by...
Creating a controller that is responsible for managing game
sessions (time limit, difficulty, etc.)
Using that
controller to create Bird objects (sub-controllers?) at a regular
interval.
Putting logic into each of the Bird objects so
that they automatically destroy themselves after 5 seconds or if
they are clicked.
Here's the main controller that created for part 1 of my problem:
myGameModule.controller( 'BirdActivityCtrl', function BirdActivityCtrl($scope) {
$scope.difficulty = 'Easy';
$scope.reward = 250;
$scope.spawn_interval = 1000;
$scope.status = 'starting';
$scope.birds_required = 30;
$scope.birds_clicked = 0;
$scope.time_left = 60;
$scope.start = function(){
location = '#/birds_in_progress';
$scope.status = 'in_progress';
}
$scope.cancel = function(){
location = '#/cafeteria';
}
});
Specifically, I am asking for help with Parts 2 and 3 of my question (mentioned above). I know that Angular has strict conventions for separating DOM elements from controllers. What is the correct way to spawn bird objects (which will be tied to DIVs on the page) and to destroy them after 5 seconds? Thank you for reading. Any help will be greatly appreciated!
Your DOM-related work should be in directives. Any directive can have it's own controller, it will be your BirdController. For any new bird instance (div with directive) new controller and scope will be created.
I suggest you put all your birds in some data structire in a service accessible from any part of your app with DI. Then you can simply use ng-repeat for you birds!
Something like this pseudocode should work:
game.factory('BirdStorage', ['in', 'ject', 'ables', function(){
var birds = [];
return {
addBird : function(){
birds.push({...})
},
deleteBird : function(id){
...
},
...
}
}]);
game.directive('bird', ['in', 'ject', 'ables', function(){
return {
restrict: 'EA',
template: '<div>...</div>',
replace: true,
scope: {
...
}
controller: function($scope, $element, $attrs){
...
}
link: function(){
...
}
}
}]);
Then you can use it in HTML like an element:
<ul>
<li data-ng-repeat="bird in birds">
<bird attrs=...>
</li>
</ul>
birds will come from a service you defined earlier.
And a logic to remove bird will come to BirdStorage. Just create a timeout that will delete specified bird:
setTimeout(function(){
this.deleteBird(id);
}, 5000)