How to define service variable as current route? - javascript

I have some code...
spa.service("testService", function($route, $location, $routeParams) {
this.testValue = $location.path();
})
This is pretty straightforward. I want the path after the http://www.whatever.com. The problem with this is that I want that value dynamically. I want it when I enter the page, and I want it when I change routes, etc.
I've tried using $routes.current I've tried using $location.current,path() and nothing works. I have to be missing something. I've read the documentation over and over, and I'm not sure how to achieve this.
The overall goal is to give some links on my page a certain class when their associated route is active. I'm attempting to do this by creating a function in the controller assigned to the links that returns a boolean based on whether or not the route variable I'm trying to declare is equal to the input of the aforementioned function. When the function outputs true,I use the ng-class directive to append the class to the correct link.
How do I assign testValue to the dynamic value of the route?

To expand on #acicali's comment, you can do something like:
app.service('spasvc', function($rootScope, $location){
$rootScope.$on('$routeChangeSuccess', function(e) {
console.log('Route Change:', $location.path());
});
});
Plunk: http://plnkr.co/edit/ov5lxGdEBPcwNeq9lfxT?p=preview
However, this only makes sense if you have a plan of mutating the service's internal state. And you can assign some variable to store value of $location.path(). It doesn't have much utility wherever injected. Of course, you can code around that, but there are better mechanisms to deal with that...

Related

Global variable in AngularJS (good practice)

I'm looking for a pattern in order to have globals constant in my application. But not with a controller or a factory. (so not with app.constant() too)
I just want to set a variable but I didn't find something good.
I wanted to set this var in my rootScoop but without success.
with something like
myApp.run(function($rootScoop){
$rootScoop.global = {};
});
When I use that code, an arror occurs for nothing (transtateFilterProvider). When I delete this code, the translateService works,
I MUST have access in all html view, I don't want to always use a controller (useless in this case), I just want to set a global variable in rootScoop.
Thank you.
You are getting an error because it is :
$rootScope
And not
$rootScoop
Anyway, correct way to do this is to add a constant module to your app like :
angular.module('yourapp', []).constant('Constants', {
foo: 'bar'
});
But you'll have to call Constants in controllers.
If you use $rootScope, remember you will need to call $root.global in templates.

Angular directives that call methods on child directives

I am looking for advice on how to implement a hierarchical structure in Angular, where a directive (<partition>) can call a method on a child directive's controller (<property-value>).
I have put together a detailed example here:
https://jsfiddle.net/95kjjxkh/1/
As you can see, my code contains an outer directive, <partition>, which displays one or more <property-value> directives within.
The <property-value> directive offers an editing method, editItem(), which allows the user to change the value of a single entry. (To keep my example short, I simply assign a random number here, but in my production app, a modal will appear, to query the user for a new value.)
This works fine. However, in the outer directive, <partition>, I would like to add the ability to create a new, blank <property-value> directive and then immediately call its editing method so that the user can enter an initial value. If no initial value is entered, the new item would be discarded.
I have seen examples of inner directives calling methods on enclosing directives, but not the other way around.
Is there a way to do this? Alternatively, is there a better way for me to build this kind of view?
You can always use $broadcast to talk both ways. To your parent as well as to your childrens.
In your Child controller you can do the following
app.directive('propertyValue', function() {
return {
require : '^partition'
restrict: 'E',
scope: {
item: '='
},
with this you will get the parent controller in child directive's link function like this
link:function(scope,element,attrs,partitionCtrl){
partitionCtrl.getChildCtrl(element)
}
in partition controller create getChildCtrl function and with that call "propertyvalue" controller function
controller: function ($scope, ItemFactory) {
// your code
var propValueCtrl =undefined;
this.getChildCtrl =function(elem)
{
propValueCtrl = elem.controller();
}
this.callChildFunction = function()
{
propValueCtrl.Edit();// whatever is the name of function
}
call this function when needed in property link function.
Hope this helps.

Is it possible to change where AngularJS looks for variables?

Now, I know this is an off-the-wall question and that there is probably not going to be any API level access for doing this. I also understand that doing this is completely unnecessary. However, the implementation that I am aiming for requires for me to be able to make {{ variable }} look inside of the $scope.object instead of the $scope itself.
For example:
Controller($scope) {
$scope.active = ...;
}
In this case you can get the active object through {{active}} and any child elements through {{active.broken}} however for the sake of humoring me, lets assume that all of the variables I'm ever going to have to obtain is going to be part of that active object. So I'll be typing things like.. (Data not related)
{{active.title}}
{{active.author}}
{{active.content}}
You could just say "Well why not just move the title/author/content into the $scope and keep it outside of the active object, as that would achieve the desired result of this:
{{title}}
{{author}}
{{content}}
Well, that's where the problem comes in. This is a controller that is not exposed to my end-user, however the end-user does have a completely mutable object (in this example: active) that they can modify. This object has many [optional] listeners and callbacks that are invoked by the application controller when necessary.
The user's that have used my application during testing have commented on what a drag it is to have to type in active. before everything in order to get the data they wanted. Considering data from the $scope is never rendered to the screen, and only data from active is, I was wondering if perhaps there was a way to change where AngularJS looks when parsing/binding data.
If the goal is to evaluate expressions in a different context than $scope, that can be done with the $parse service.
app.controller("myVm", function($scope, $parse) {
var vm = $scope;
vm.active = { a: 5,
b: 3
};
var.myFn = $parse("a+b");
vm.total = myFn(vm.active);
console.log(vm.total);
});
The above example shows how to evaluate the expression a+b using the properties of the $scope.active object.
The DEMO on JSFiddle

Angular ngRoute conditional .when()

I'd like to implement a conditional .when(), like this:
.when('/abc', {
// if MyService.allow == true
template: '<myDirec></myDirec>'
// else
redirectTo: '/'
})
My /abc route shall be like "secured" by a variable hold in one of my services, in a stateful manner. So I want to set this very state somewhere else to true/false and the next time the user tries to get to /abc he will be served conditionally.
How can I achieve this? - with as few 3rd-party dependencies as possible
What I tried and read about:
- Simply injecting my Service in .config, which I learnt is not possible
- Read about using a provider as they can be injected. But can I use them like I use my service?
- template and templateUrl accept a function, but this didn't really help me
Thanks very much in advance!
You could use the $routeChangeStart event. It's fired when the url is changed and takes current and next as arguments something like:
$rootScope.$on('$routeChangeStart', function(event, next, current) {
if (next === 'abc') // do something;
});

Set radio button state based on a value received from REST in AngularJS

What I would like to achieve is set a radio button's state according to the data retrieved from the server in AngularJS.
What makes this more special is that I need to show a div based on a condition (the car has 1 and only 1 assigned to it).
If the car is so-called isSingleUser, the div contains the radio buttons needs to be visible, otherwise we need to hide it.
Finally, after a lot of struggling, I even have the solution to achieve this, but I ran into some interesting obstacles what I want to share with others for reference and I still have some questions regarding the alternative "solutions".
The simplest (and working) solution
We have 3 cars, the Cars service will GET the particular car according to the chosen link.
Only car1 and car2 are isSingleUser so the div should only visible for those cars.
You can see that the radio button's state changes according to the
car.isMonitoringAutoEnablementSet JSON property.
This was my first attempt, but I couldn't see the code working at that time, I used the
value property of the input, wrongly, because the ng-value is better.
More specifically, the value property on input cannot handle boolean values as booleans (and not stringified)
therefore angular couldn't map the boolean value false to "false" and the same goes with the true value.
If I used ng-value, everything is fine.
Note that the plateNumber for each car is visible on the page.
Let's see what I tried afterwards:
Magic with promise
My assumption was that the data for car is not downloaded yet and the template rendered
before the controller received the data and store it to $scope.car
So I added this code for the controller:
$scope.car = Cars.get({carId: $routeParams.carId})
.$promise.then(function(car) {
$scope.automonitoring_state = car.isMonitoringAutoEnablementSet;
$scope.isSingleUser = car.isSingleUser;
});
and modified the view according to the new variables:
<h1 class="title">{{car.plateNumber}}</h1>
<div class='ng-hide' ng-show='isSingleUser'>
<p class='paragraph'>automatic
<form name="myForm" class="toggle">
<input type="radio" name="state" ng-model="automonitoring_state" ng-value="true">on
<input type="radio" name="state" ng-model="automonitoring_state" ng-value="false">off
<button class="button" disabled>save</button>
</form>
</p>
</div>
Note that the plateNumbers are disappeared from the page, clearly showing that some problem
occured while fetching the data for the car variable. Only the new variables had a
value in the scope (automonitoring_state and isSingleUser).
Somehow even if I put a simple console.log() inside the then function on the promise, the
car variable will not hold the data for the car, strange...
This solution also seems like a workaround, because I need to define new variables in the
scope, but if I save the car's state through the service, I need to sync back my new
variables to the $scope.car so this one is definitely a no-go for me.
Resolve with the help of a controller scope function
Then I googled and googled, found some advices on Stackoverflow, and so on.
Hey, there is resolve which is good for passing some variables into controllers before the view is rendered, that's exactly what I want.
Naively, I tried to invoke a controller function from the routeProvider like this :
when('/driver/cars/:carId', {
templateUrl: 'partials/driver_car.html',
controller: 'DriverCarController',
resolve: 'DriverCarController'.resolveCar
})
and of course added the function in question to the controller:
$scope.resolveCar = {
car: ['$routeParams', 'Cars',
function($routeParams, Cars) {
return Cars.get({
carId: $routeParams.carId
}).then(function(car) {
console.log('resolve');
return car;
}, function() {
return [];
});
}
]
};
The result is: nothing, no console.log at all, proving that the function was not invoked.
Another working solution with passing the variable from resolve
This trial is a slightly modified version of the solution above.
The Cars service is only used from the $routeProvider, if the promise returns the value, it is saved to the car variable.
.when('/driver/cars/:carId', {
templateUrl: 'partials/driver_car.html',
controller: 'DriverCarController',
resolve: {
car : function (Cars,$route) {
return Cars.get({carId: $route.current.params.carId})
.$promise.then(function (response) {
return response;
});
}
}
})
The only thing to change in the controller is to add 'car' to the injection list and save
the parameter to the $scope.
controllers.controller('DriverCarController', ['$scope', '$routeParams','car', 'SecurityEvents',
function($scope, $routeParams, car, SecurityEvents) {
$scope.car = car;
console.log(car);
}
]);
Please see my public plunkers, you can try out each solution I described above: plunkers
Any help is appreciated about judging between the most viable approach, providing some information about the intended use cases of each alternative.
Thanks!
Number 2 is the most similar to what I have used.
To fix the bug, just change the code in the controller like this:
Cars.get({
carId: $routeParams.carId
}).$promise.then(function(car) {
$scope.automonitoring_state = car.isMonitoringAutoEnablementSet;
$scope.isSingleUser = car.isSingleUser;
$scope.car = car;
});
The biggest problem that I see with calling the service from the resolve is that it tightly couples the controller with the app router, which means your controller is less re-useable.

Categories

Resources