I am new to Angular and trying to figure out how to adapt an ASP.NET app into Angular. I need to display a different link to the user depending on the group the user belongs to. I have a Web API (ASP.NET Web API) that I can call to determine the user. I am using the following Angular code to call the Web API, but what I am unsure of is what to do next. If $scope.userGroupInfo contains the group the user belongs to how do I then display a different link in HTML depending on the group?
AngularJS
(function() {
var app = angular.module("linkSwitcher", []);
var MainController = function($scope, $http) {
var onApiCallComplete= function(response) {
$scope.userGroupInfo = response.data;
};
var onError = function(reason) {
$scope.error = "There was a problem calling the API";
};
$scope.getUserGroup = function(userId) {
$http.get("https://myapi.mysite.com/api/clients/getUserGroup/" + userId)
.then(onApiCallComplete, onError);
};
};
app.controller("MainController", MainController);
}());
HTML
<body ng-controller="MainController">
<form name="GetGroup" ng-submit="getUserGroup()">
<input type="submit" value="Lookup User Group Link" />
</form>
</body>
Please assume I have referenced the Angular library properly and that I am just displaying the portion of HTML that calls the Angular script.
well, what do you want to change in the display? you can try using ng-if="" inside a tag to show it, or you can use ng-class="someObjectMappingClassNameToBoolean" to modify the class depending on some flag.
Ex: (I don't know the structure of your response)
<div ng-if="userGroupInfo.groupId=== 7" > <a>Show me if userGroupInfo.groupId equals to 7 is true!</a> </div>
or
<div ng-class="{'blue-class': userGroupInfo.isBlue === true, 'error': userGroupInfo.isError === true }" > <a>Blue class added if isBlue is true, error class if isError is true</a></div>
You can also use ng-href to generate a calculated hyperlink, either as a whole or just as part, e.g. ng-href="http://path.to/{{groupName}}"
Related
I have a html page with a link as follows:
<div ng-if="!adminCtrl.valid">
<div><a target="_blank" ng-href="https://api.instagram.com/oauth/authorize/?client_id=xxx&redirect_uri=http://localhost:8888/igSuccess.html&response_type=token">Authorize to Instagram</a><br/></div>
</div>
This goes to redirect page on success where the code is
<div ng-controller="AdminController">
<h2>You can close this tab/window</h2>
</div>
The control is same for both pages as follows:
app.controller('AdminController', ['$scope','$routeParams','$location', function($scope,$routeParams,$location){
var actrl = this;
actrl.valid = false;
var token = $location.absUrl();
if(token.indexOf('access_token') > -1){
console.log('found token so will do special');
actrl.valid = true;
$scope.$apply();
}
}}
I am expecting the link to disappear once the new page opens as i am updating the valid variable value.
i know the flaw seems to be the cross page communication. so how to deal with it?
Controllers are 'flushed' when you change views. To keep data from a view/controller to another, store your data within a Service.
UPDATE
controller:
app.controller('AdminController', [
'$scope', '$routeParams', '$location', 'ExampleService', function ($scope, $routeParams, $location, ExampleService) {
var actrl = this;
// Watches the service's value for changes and applies it to the controller
$scope.$watch(function(){return ExampleService.valid}, function(newValidValue){
actrl.valid = ExampleService.valid;
});
var token = $location.absUrl();
if (token.indexOf('access_token') > -1) {
console.log('found token so will do special');
ExampleService.valid = true;
// No need for this
// $scope.$apply();
}
}
}
Service:
app.service('ExampleService', [
function () {
//All properties here are kept through-out your app's life time
this.valid = false; // Init to false
}
}
To share data between Controllers in Angular JS, use a named Service to encapsulate the data. In your case, I would typically define an Auth service that provides a few methods for getting and setting the access_token for a user:
module.factory('Auth', function(){
return {
isValid: function(){ /* Check that a User is authenticated... */ },
setToken: function(token){ /* Store the token somewhere... */ },
getToken: function(){ /* Fetch the token from somewhere... */ }
};
});
To share data across "pages" -- tabs or windows in your browser -- even in a Single Page Application (SPA) like this, store the data in cookies or localStorage. You can use angular-local-storage by grevory (GitHub) to abstract the details of using localStorage with a cookie fall-back in non-compatible browsers.
The reason that one page cannot see the valid value defined in the other is because each page gets a separate instance of AdminController, each of which get their own separate instance of $scope tied to their respective DOM elements. Setting valid on the $scope of the redirect landing page has not effect on the completely detached $scope instance in the originating page.
You'd encounter similar difficulties with a trivial same-page example (CodePen):
angular.module('scope-example', [])
.controller('ExampleCtrl', function($scope) {
$scope.value = 'Initial Value';
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<form class="pure-form" ng-app="scope-example">
<fieldset ng-controller="ExampleCtrl">
First instance of <code>ExampleCtrl</code>:
<br>
<input ng-model="value">
<label>{{value}}</label>
</fieldset>
<fieldset ng-controller="ExampleCtrl">
Second instance of <code>ExampleCtrl</code>:
<br>
<input ng-model="value">
<label>{{value}}</label>
</fieldset>
<fieldset ng-controller="ExampleCtrl">
Third instance of <code>ExampleCtrl</code>:
<br>
<input ng-model="value">
<label>{{value}}</label>
</fieldset>
</form>
Even though each of the <fieldset> elements have identical ng-controller directives associated, each gets its own instance of ExampleCtrl and $scope, so the value property isn't shared between them. This holds true for any directive.
I've been trying to solve this for hours, and have tried to find a working solution on stack overflow and other sites, but none worked so far.
The Issue
I am building a travelogue web app that allows users to log and view their journeys (e.g. a road trip). At the moment I am implementing the feature that lets users view a particular journey in a separate view which they have selected from a list of journeys. I pass down the id of the selected journey and retrieve an Object from MongoDB. I implemented this using POST. It works in that the _id of the selected journey is passed in the request, then used to identify the document with Model.findById - then the response yields the data. The data is bound to $scope.selection.
But while $scope.selection contains the data (when logged to console), I cannot seem to bind it to the view (called view_journey). Meaning, whenever I want to access, e.g. selection.name in my view_journey.html, the expression or ng-bind is left empty.
app.js
$scope.viewJourneybyId = function(id) {
var selectOne = { _id : id };
$http.post('http://localhost:8080/view_journey', selectOne).
success(function(data) {
$scope.selection = data;
$scope.$apply();
console.log("POST found the right Journey");
console.log($scope.selection);
}).error(function(data) {
console.error("POST encountered an error");
})
}
server.js
app.post("/view_journey", function(request, response, next) {
Journeys.findById(request.body._id, function(error, selection) {
if (error)
response.send(error)
response.json({ message: 'Journey found!', selection });
});
});
index.html
<tr ng-repeat="journey in journeys">
<td>
<a href="#/view_journey" ng-click="viewJourneybyId(journey._id)">
{{journey.name}}</a>
</td>
<td>...</td>
</tr>
view_journey.html
<div class="panel panel-default">
<div class="panel-heading">
<h2 ng-bind="selection.name"></h2>
<!-- For Debugging -->
ID <span ng-bind="selection._id">
</div>
<div class="panel-body">
<table class=table>
<caption>{{selection.desc}}</caption>
...
</table>
</div>
</div>
Feedback
This is my first question on stack overflow, so please also tell me if I phrased my question in a way that could be misunderstood, and whether or not I should supply more details, e.g. console output. Thank you ;)
After fiddling with your code I can confirm that when triggering the route you are getting a new instance of the controller that has a new, clean scope. This is the expected behavior with AngularJS.
You can verify this by adding a simple log message as the first line of your controller:
console.log($scope.selected);
You will notice that it always logs out "undefined" because the variable has never been set (within viewJourneyById). If you leave that logging in and test the code you will see the logging fire in viewJourneyById but then immediately the "undefined" as it loads the view_journey.html template into ng-view and creates the new instance of mainCtrl. The presence of the "undefined" after the new view loads shows that the controller function is being executed again on the route change.
There are a couple of ways to address this. First you could create a factory or service, inject it into your controller, and have it store the data for you. That is actually one of the reasons they exist, to share data between controllers.
Factory:
travelogueApp.factory('myFactory',function() {
return {
selected: null,
journeys: []
};
});
Controller:
travelogueApp.controller('mainCtrl', ['$scope','$http','$location','myFactory', function ($scope, $http, $location, myFactory) {
// put it into the scope so the template can see it.
$scope.factory = myFactory;
// do other stuff
$scope.viewJourneybyId = function(id) {
var selectOne = { _id : id };
$http.post('http://localhost:8080/view_journey', selectOne)
.success(function(data) {
$scope.factory.selection = data;
console.log("POST found the right Journey");
console.log($scope.factory.selection);
})
.error(function(data) {
console.error("POST encountered an error");
})
}
}]); // end controller
Template:
<div class="panel panel-default">
<div class="panel-heading">
<h2>{{factory.selection.name}}</h2>
</div>
<div class="panel-body">
<table class=table>
<caption>{{factory.selection.desc}}</caption>
...
</table>
</div>
</div>
More or less something like that. Another way to do it would be to construct the link with the journey id as part of the query string and then in the controller check for the presence of the journey id and if you find one, look up the journey. This would be a case of firing the route, loading a new instance of the controller and then loading the data once you're on the view_journey page. You can search for query string parameters in the controller like this:
var journey_id = $location.search().id;
Either way works. The factory/service method allows you to minimize web service calls over time by storing some data. However, then you have to start considering data management so you don't have stale data in your app. The query string way would be your quickest way to solve the problem but means that every route transition is going to be waiting a web service call, even if you are just going back and forth between the same two pages.
So, I have a ng-repeated list of items as such.
<li><a ng-click="{{person.id}}">Name of Person</a></li>
I would like to create a service wherein, on click, I can collect that person.id and pass it to another controller in a different route.
This would normally be very simple by just using the url and route params, however, in this case it is important that the person.id not be exposed within the browser url.
-- More Context
Whether service or not, I am needing to extract a {{person.Id}} that is data available via an ng-repeat on a list page of persons.
On click, I move from a persons controller to a new route with a "person" controller. I need that "person" controller to be able to pull the {{Person.ID}} that was clicked on the previous route in order to look up that person in a DB.
Any help would be really great!
Services aren't meant to interact directly with DOM elements. DOM should interact with directives/controllers. Controller should interact with models.
This example below demonstrates sending data from controller 1 to myFactory and then controller 2 gets it the value from myFactory.
angular
.module('app', [])
.factory('myFactory', myFactory)
.controller('myCtrl1', myCtrl1)
.controller('myCtrl2', myCtrl2);
function myFactory() {
var fromSender = null;
return {
setSender: function(sender) {
fromSender = sender;
},
getSender: function() {
return fromSender;
}
};
}
function myCtrl1(myFactory) {
var vm = this;
vm.setSender = myFactory.setSender;
}
function myCtrl2(myFactory) {
var vm = this;
vm.getSender = myFactory.getSender;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.js"></script>
<div ng-app="app">
<div ng-controller="myCtrl1 as ctrl1">
Controller 1: <br>
<button ng-click="ctrl1.setSender('from controller 1')">Send to myFactory</button>
</div>
<hr>
<div ng-controller="myCtrl2 as ctrl2">
Controller 2: <br>
value from ctrl1 via myFactory: {{ctrl2.getSender()}}
</div>
</div>
All services in Angular are singletons. So if you inject personService or something like that, in multiple controllers, then those controllers will be using the exact same object. So if you set a value on that service, then the other controllers will be able to see it.
With more code and context, I'll be able to give a more specific example.
I have this code that loads the content when the page load,
Now I want to know how to reload the content by clicking the button.
Can you show me how to do it with example please?
Javascript code:
.controller('InterNewsCtrl', function($scope, NewsService) {
$scope.events = [];
$scope.getData = function() {
NewsService.getAll().then(function (response) {
$scope.events = response;
}), function (error) {
}
};
$scope.getData(); // load initial content.
})
Html code:
<ons-toolbar fixed-style>
<div class="left">
<ons-back-button>Voltar</ons-back-button>
</div>
<div class="right">
<ons-toolbar-button><ons-icon icon="ion-android-refresh"></ons-icon></ons-toolbar-button>
</div>
<div class="center">Internacional</div>
</ons-toolbar>
I think you're asking how to just retrieve new events from the backend. If that's correct, you don't need to reload the entire page.
You already have a function called getData which goes and retrieves you data via your service. Assuming your service doesn't cache the data, just call getData from your button:
<ons-toolbar-button ng-click="getData()"><ons-icon icon="ion-android-refresh"></ons-icon></ons-toolbar-button>
P.S. if you do explicitly have the cache set to true in your service, you can remove the cached data with $cacheFactory.get('$http').removeAll();.
For reloading same page in angular Js without post back
first remove that url from template cache if you call $route.reload() without removing it from $templateCache it will get it from cache and it will not get latest content
Try following code
$scope.getdata=function()
{
var currentPageTemplate = $route.current.templateUrl;
$templateCache.remove(currentPageTemplate);
$route.reload();
}
and Call it as following
<input type="button" ng-click="getdata();" value ="refresh"/>
Hope this will help
Please reffer this
Using cordova native share sheet like this:
module.controller('MyCtrl', function($scope, $cordovaSocialSharing) {
$scope.share = function() {
$cordovaSocialSharing
.share(message, subject, file, link) // Share via native share sheet
.then(function(result) {
// Success!
}, function(err) {
// An error occured. Show a message to the user
});
}
});
from html template, using the ng-click directive to invoke the function.
<div ng-controller="MyCtrl">
<div ng-click="share()"></div>
</div>
How could I know whether the user has clicked the cancel button from the share sheet?
I've tried looking at result, but it's always set to true.
Thanks!