ng-route- Parameters with Special Characters - angularjs

<nav>
<ul class="list">
<li ng-repeat="product in data | startFrom:currentPage*pageSize | limitTo:pageSize">
<span>{{product}}</span>
<a href="#getAttributes/{{product}}/{{code}}/{{name}}/{{hierName}}">
{{product.prd_name}}
</a>
</li>
</ul>
</nav>
I am calling the ng-route as above .
myApp.config([ '$routeProvider', function($routeProvider) {
$routeProvider
.when('/getAttributes/:product/:code/:name/:hierName', {
templateUrl : 'view/attributes.jsp',
controller : 'attributeController'
}).otherwise({
redirectTo : '/mainMenu'
});
} ]);
The Product is a JSON Object. The above code routes to otherwise if the product object contains "/" character. Any idea on how to resolve it.

First Approach: Use ng-click to Store the Model In A Service Then Go to Location
Start by adding an ng-click to your list item which calls get attributes on your controller taking caring to remove the href.
<nav>
<ul class="list">
<li ng-repeat="product in data | startFrom:currentPage*pageSize | limitTo:pageSize" ng-click="getAttributes(product)">
<span>{{product}}</span>
{{product.prd_name}}
</li>
</ul>
</nav>
Next, reconfigure your route to simply /getAttributes
myApp.config([
'$routeProvider', function($routeProvider) {
$routeProvider
.when('/getAttributes/', {
templateUrl: 'view/attributes.jsp',
controller: 'attributeController'
}).otherwise({
redirectTo: '/mainMenu'
});
}
]);
In your controller, add a method called getAttributes which will be invoked by the aforementioned ng-click. It will call a service and use its setModel function to persist which model was clicked. Finally, it will route getAttributes.
myApp.controller('someController', [
'$scope', '$location', 'someService', function($scope, $location, someService) {
$scope.getAttributes = function(model) {
someService.setModel(model);
$location.url('getAttributes');
};
}
]);
The attribute controller will call the same service and use its getModel method to set the model.
myApp.controller('attributeController', [
'$scope', 'someService', function($scope, someService) {
$scope.model = someService.getModel();
}
]);
The service would like something like the following:
myApp.service('someService', function() {
var model;
return {
getModel: function() {
return model;
},
setModel: function(newModel) {
model = newModel;
}
};
});
This approach is only suitable if you'll never be routing to getAttributes in any other way. A more flexible second approach is possible.
Second Approach: Use Resolve to Get the Model, Set In the Service Then Load in the Controller
Modify the href to be an ng-href and have it be getAttribute/123 or some such.
<nav>
<ul class="list">
<li ng-repeat="product in data | startFrom:currentPage*pageSize | limitTo:pageSize">
<span>{{product}}</span>
<a ng-href="/getAttributes/{{product.id}}">{{product.prd_name}}</a>
</li>
</ul>
</nav>
Modify your route to accept the product ID as seen below. Additionally, add a resolve object. This object will call someService which will in turn call your server, retrieve the products attributes and set the model variable equal to the result.
myApp.config([
'$routeProvider', function($routeProvider) {
$routeProvider
.when('/getAttributes/:id', {
templateUrl: 'view/attributes.jsp',
controller: 'attributeController',
resolve: {
getModel: function(someService) {
return someService.get();
}
}
}).otherwise({
redirectTo: '/mainMenu'
});
}
]);
As mentioned before, the service will take the passed ID on the /getAttributes/id route and use it to call the server. An alternative is to have the service call a repository for this instead. The end result is the same: set the model variable equal to the result of the call to the server.
myApp.service('someService', [
'$http', function($http) {
var model;
return {
get: function(id) {
return $http.get('api/attribute/' + id).then(function(response) {
model = response.data;
});
},
getModel: function() {
return model;
},
setModel: function(newModel) {
model = newModel;
}
};
}
]);
Finally, your attributeController will behave the same as the first example. It set $scope.model equal to someService.getModel() thereby providing the products attributes.
The advantage to this approach is that your route may deep linked from anywhere in your app without issue.

Related

Data-binding between ngInclude and ngView

I want to make a sidebar with list item that can be dynamically changed based on the settings page.
My app request settings.json via factory() and then called it in a controller. The controller will be used by settings.html (ngView) and sidebar.html (ngInclude).
The json will return Boolean value that also can be changed on setting page that contain checkbox which return true if check and false if not checked. I use ngShow on the sidebar to display/hide the list items.
How can I made the sidebar to reflect the changes as I tick the checkbox?
settings.factory.js
var settingsFactory = angular.module('settingsFactory', []);
settingsFactory.factory('SettingsFilterFactory', ['$http', function ($http) {
var settingsFactory = {};
settingsFactory.getSettings = function () {
return $http.get('app/data/settings.json');
};
return settingsFactory;
}]);
controller
var settingsControllers = angular.module('settingsControllers', ['settingsFactory']);
settingsControllers.controller('SettingsFilterController', ['$scope', '$http', 'SettingsFilterFactory', function ($scope, $http, SettingsFilterFactory) {
$scope.settings;
$scope.status;
getSettings();
function getSettings() {
SettingsFilterFactory.getSettings()
.then(function (response) {
$scope.settings = response.data;
}, function (error) {
$scope.status = 'Unable to load: ' + error.message;
});
}
}]);
app.js
var app = angular.module('app', ['ngRoute', 'settingsControllers']);
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider.
when('/settings', {
title: 'Settings',
templateUrl: 'app/components/settings/settings.html',
controller: 'SettingsFilterController'
}).
otherwise({
redirectTo: '/home'
});
}]);
My index.html is something like this:
...
<body>
<section class="sidebar">
<div ng-include="'app/components/sidebar/sidebar.html'"></div>
</section>
<section class="content">
<div ng-view></div>
</section>
</body>
...
sidebar.html
<ul class="sidebar-menu" ng-controller="SettingsFilterController">
<li ng-show"settings.hiddenMenu">This is secret link</li>
</ul>
settings.html
...
<div class="checkbox">
<input type="checkbox" ng-model="settings.hiddenMenu" ng-true-value=true ng-false-value=false> Check this to show hidden menu
</div>
...
Try something like this (untested):
settings.factory.js
var settingsFactory = angular.module('settingsFactory', []);
settingsFactory.factory('SettingsFilterFactory', ['$http', function ($http) {
var settingsFactory = {};
settingsFactory.getSettings = function () {
return $http.get('app/data/settings.json');
};
settingsFactory.hiddenMenu= true;
settingsFactory.someOtherSetting = {};
return settingsFactory;
}]);
sidebar controller
settingsControllers.controller('SidebarController', ['$scope', '$http', 'SettingsFilterFactory', function ($scope, $http, SettingsFilterFactory) {
//do this in each controller, so that the factory becomes a property of $scope and can be seen in the HTML
$scope.settingsFactory = SettingsFilterFactory;
}
sidebar.html
<ul class="sidebar-menu" ng-controller="SidebarController">
<li ng-show"settingsFactory.hiddenMenu">This is secret link</li>
</ul>
settings.html
...
<div class="checkbox">
<input type="checkbox" ng-model="settingsFactory.hiddenMenu" ng-true-value=true ng-false-value=false> Check this to show hidden menu
</div>
...
Essentially, you are binding the settingsFactory object which is a singleton to each $scope that is provided by each controller. Each controller is able to change the property on the factory object, which is then visible in all other controllers that have injected this object.

How to load a new page and pass data from api?

I have page with list of teams. On click I need title and members of that team on another page. I just loaded teams in the list. Code is here:
angular.module('my-app').controller('MainController', function($scope, $http) {
$http.get("http://...").success(function(response) {
$scope.details = response;
});
});
and
<article ng-app="seed-angular">
<section ng-controller="MainController as mc">
<ul>
<li ng-repeat="x in details">
{{x.name}}
</li>
</ul>
</section>
</article>
As mentioned in the comments by Claies, you need to use a router in your app. From the docs:
Application routes in Angular are declared via the $routeProvider, which is the provider of the $route service. This service makes it easy to wire together controllers, view templates, and the current URL location in the browser. Using this feature, we can implement deep linking, which lets us utilize the browser's history (back and forward navigation) and bookmarks.
There are some libraries in which you can do that. I'll use ngRoute as an example as it comes with Angular.js.
To do so, you need to add the angular-route.js and load the ngRoute module:
var app = angular.module('seed-angular', [
'ngRoute'
]);
You also need to configure your $routeProvider to setup your URL routes, like this:
app.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/team', {
templateUrl: 'team-list.html',
controller: 'MainController'
}).
when('/team/:teamId', {
templateUrl: 'team-detail.html',
controller: 'TeamDetailController'
}).
otherwise({
redirectTo: '/team'
});
}
]);
In the routes above, we've configured /team to route to a template page team-list.html. Note that the <a href=""> now passes a parameter teamId:
<article>
<section ng-controller="MainController">
<ul>
<li ng-repeat="x in details">
{{x.name}}
</li>
</ul>
</section>
</article>
We also configured /team/:teamId to route to a template page team-detail.html (note that :teamId means it's a parameter):
<article>
<section ng-controller="TeamDetailController">
Back
<div ng-repeat="x in details">
<strong>{{x._id}}</strong>
<ul>
<li>
{{x.firstName}} {{x.lastName}}
</li>
</ul>
</div>
</section>
</article>
In which you will be able to access teamId via $routeParams.teamId to retrieve the team details:
app.controller('TeamDetailController', function($scope, $http, $routeParams) {
var url = "http://est-teams.estudent.hr/api/teams/" + $routeParams.teamId + "/members";
$http.get(url).success(function(response) {
$scope.details = response;
});
});
See this fiddle for a working example.
if you want to pass the data you can create service and create function that store or set your data, then call it in your first controller that called the data and call it again in your next page controller here is the code :
this is the service :
myApp.service('sharedProperties', function() {
var stringValue = 'test string value';
var objectValue = {
data: 'test object value'
};
return {
getString: function() {
return stringValue;
},
setString: function(value) {
stringValue = value;
},
getObject: function() {
return objectValue;
}
};
});
you can call it in your controller like this :
myController.controller('UserUpdateController', ['$scope', '$http','sharedProperties',
function($scope, $http, sharedProperties ) {
$scope.updateUser = function() {
.....
id = sharedProperties.getString();
};
}
]);
and set the data from your previous page like this :
myController.controller('UserListController', ['$scope', '$http','sharedProperties',
function($scope, $http, sharedProperties) {
$scope.updateUser = function(id){
console.log("from index "+id);
sharedProperties.setString(id);
};
}
]);

Trouble rendering view in Angular

I think what I'm trying to do is very simple, but somehow I am having trouble getting it to work. I'm sure there must be some simple solution, but I can't find it.
Basically, I have a simple array of dictionaries containing the resources I'm trying to render (defined as $scope.acts in the acts controller). I'm not importing JSON or anything like that, it's all right there in the controller file. The index is working fine, but I'm trying to render a 'show' view (not sure if this is the proper Angular terminology, I'm used to working with Rails, so I still think in those terms), for a single item in the array, and it isn't working. The template is loading, but it isn't fetching data from the array.
I really want to do this in the simplest manner possible, no bells or whistles. Eventually, I want to load the data from a Rails app, but I want to build this up one step at a time so that I get a feeling for how Angular is doing it's thing.
Here's my code.
app.js
var controllers, snowball_effect;
snowball_effect = angular.module('snowball_effect', [
'templates',
'ngRoute',
'ngResource',
'controllers'
]);
snowball_effect.config([
'$routeProvider', function($routeProvider) {
return $routeProvider
.when('/', {
templateUrl: "static_pages/index.html",
controller: 'StaticPagesController'
})
.when('/acts/index', {
templateUrl: "acts/index.html",
controller: 'ActsController'
})
.when('/acts/:id', {
templateUrl: "acts/show.html",
controller: 'ActsController'
});
}
]);
controllers = angular.module('controllers', []);
controllers.controller("StaticPagesController", ['$scope', {}]);
controllers.controller("NavBarController", [
'$scope',
'$routeParams',
'$location',
function($scope,$routeParams,$location) {
$scope.actsIndex = function() {
$location.path("/acts/index");
}
$scope.actsNew = function () {
$location.path("/acts/index");
}
}]);
ActsController.js
controllers = angular.module('controllers');
controllers.controller("ActsController", [
'$scope',
'$routeParams',
'$location',
'$resource',
function($scope,$routeParams,$location,$resource) {
$scope.acts = [
{
id: 1,
name: "Plant a Flower",
description: "Plant a flower in your garden or along the street.",
inspires: 1
}
];
$scope.addAct = function() {
$scope.acts.push($scope.newAct);
}
$scope.deleteAct = function(act) {
$scope.acts.splice($scope.acts.indexOf(act), 1);
}
$scope.linkToShowAct = function(act) {
$location.path('acts/' + act.id);
}
}]);
templates/acts/show.html
<div class="container" ng-controller="ActsController">
<div class="body">
<h1>
{{act.name}}
</h1>
<p class="act-show-description">
{{act.description}}
</p>
<p class="act-show-inspires">
<strong>Inspires:</strong>
{{act.inspires}}
</p>
Edit
Back
</div>
</div>
Your ActsController creates an array named acts, but your html does not reference an item in that array. It is using act instead of acts[0].
Try changing your html to this:
<div class="container" ng-controller="ActsController">
<div class="body">
<h1>
{{acts[0].name}}
</h1>
<p class="act-show-description">
{{acts[0].description}}
</p>
<p class="act-show-inspires">
<strong>Inspires:</strong>
{{acts[0].inspires}}
</p>
Edit
Back
</div>

Pass variable from $routeProvider to View

How can I pass a variable from $routeProvider to a view file:
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/send-money', {
templateUrl: 'partials/send-money.html',
controller: 'PageCtrl'
})
.when('/receive-money', {
someURL: 'goToSomePage',
templateUrl: 'partials/receive-money.html',
controller: 'PageCtrl'
})
}]);
As you can notice on top:
.when('/receive-money') i have declared (someURL: 'goToSomePage')
but .when('/send-money') i have not declared (someURL)
Below is the code that I would like to make it work, without taking help of controller. Is that possible ? even I am not sure something like ng-if="someURL" will work.
<div ng-if="someURL">
<a ng-href="someURL" class="nav-back"> <i class="ico-back"></i> </a>
<div>
If you don't want to use controller you can use run block to pass a reference to route service into $rootScope, from where you could check if current route has property someURL:
app.run(function($route, $rootScope) {
$rootScope.$route = $route;
});
Now in template you could use $route.current in ngIf:
<div ng-if="$route.current.someURL">
<a ng-href="someURL" class="nav-back"><i class="ico-back"></i></a>
</div>
Demo: http://plnkr.co/edit/3XUl0QprIAj1PcnnHx9Q?p=preview

calling a function in a different scope

I am a novice with angular so bare with will I try and articulate what I am trying to achieve.
I have two web pages; a main 'login' page and a secondary 'about' page. The about page is nothing more than a page with HTML text. I access both pages simply with a link using ng-href.
I have a service which remembers some user login details which seem to persist when I click to the about page and then back to main.
However, I have no idea how to call a function when the user clicks back to the main page. This function would be the login function within the Main Controllers scope. I would then use the users details in the service to automatically login them in and display the users information.
This thread seems quite similar, but the user seems to be at a more advanced stage than me.
Main.html
<div class="main">
<div class="content" ng-hide="login">
<form>
... login fields ...
<button ng-click="$login()">Log in</button>
</form>
</div>
<div class="content" ng-show="login">
<form>
... user info ...
</form>
</div>
<div class="footer">
<a ng-href="#/about"/>About</a>
</div>
</div>
About.hml
<div class="about">
<div class="content">
... plain HTML ...
</div>
<div class="footer">
<a ng-href='#/main'/>mAIN</a>
</div>
</div>
app.js
'
use strict';
angular.module('sguAppApp', [
'ngResource',
'ngSanitize',
'ngRoute',
'ngAnimate'
])
.constant( 'eURL', '....webpage...' )
.config(function ($routeProvider) {
$routeProvider
.when('/main', {
templateUrl: 'views/main.html',
controller: 'MainCtrl'
})
.when('/about', {
templateUrl: 'views/about.html',
controller: 'AboutCtrl'
})
.otherwise({
redirectTo: '/main'
});
});
main.js
angular.module('App')
.service('TestService', function() {
var TestService = {};
TestService.testVar1 = '';
TestService.testVar2 = '';
TestService.testVar3 = ''
return TestService;
})
.controller('MainCtrl', function ($scope, $rootScope, .... TestService) {
$scope.user = {};
$scope.user.logon = '';
$scope.user.password = '';
$scope.sData = '';
$scope.$login = function () {
$scope.loading = true;
... do stuff to testVarNs ...
}
}
.controller('AboutCtrl', function ($rootScope, $scope, TestService) {
... somehow call $login function in MainCtrl using the testVarNs in the service ...
(not sure if this is even needed)
})
The way you laid things out, either you are in the context of MainCtrl or AboutCtrl, when you navigate from one page to the other, the existing context gets destroyed and the destination one gets instanciated.
The login function should probably live in a service, this way you can call it from wherever you like.
In that way you shold inject your TestService in the controller and call the function in response to a click for example, with ng-click="TestService.$login()"
An alternative approach could be to have another controller that is in a context that wraps your MainCtrl or AboutCtrl, Say an AppCtrl that you define early in your html file, for example:
< body ng-controller="AppCtrl'>...
now, properties defined in the AppCtrl Context are visible inside the child contexts, so if you have a $login function defined there you can call it.

Resources