AngularJs :: Share data between Views and parameters - angularjs

I'm creating an App with Ionic Framwork.
The Front is ok, and I made the view transitions using $stateProvider.
But I need to do the APP features and other functionalities. I have 2 major problems
1) Pass parameters through href=""
2) Execute some functions after the view transition e when the data is processed show the info on the new View.
Couldn't go further because I'm newbie at Angular and the other examples I searched were not specific.
That's what I got so far::
INDEX.HTML
<body ng-controller="AppController as controller">
<ion-view title="View1"><ion-content>
<a href="#/view2" ng-click="controller.test1">
<button class="button-style">TEST 1</button>
</a>
<a href="#/view2" ng-click="controller.test2">
<button class="button-style">TEST 2</button>
</a>
</ion-content></ion-view>
...
View2.HTML
<ion-view title="View2"><ion-content>
<input type="text" value="{{controller.parameter}}" />
</ion-content></ion-view>
APP.JS
app.config( function($stateProvider, $urlRouterProvider){
$stateProvider
.state('view2',{
url:'/view2',
templateUrl:'views/view2.html'
})
...
app.controller('AppController', ['$scope', function($scope){
parameter = '';
this.test1 = function(){
alert('test1');
parameter = 'One';
};
this.test2 = function(){
alert('test2');
parameter = 'Two';
};
}]);
I've tried alot. But now it's just guessing :(
I didn't understand how Controllers and $scope works. I've saw some examples using factory and service, even the CodeSchool (awesome by the way) coudn't help me...
So, any good soul can bring some light here?
Sorry about my misspelled english

First of all, you should be using the ui-sref attribute in your <a> tags (since you're using states, although using href works too), like this:
<a ui-sref="view2"></a>
And if you want to pass parameters, you have to configure your state's url to accept parameters:
url: '/view2/:parameter'
To access parameter in your controller, you'll also need to inject $stateParams into your controller:
app.controller('AppController', function($scope, $stateParams) {
});
And you access parameter via $stateParams.parameter like this:
app.controller('AppController', function($scope, $stateParams) {
alert($stateParams.parameter);
$scope.parameter = $stateParams.parameter;
});

Related

Map two controller to one view with ngRoute in Angular?

Hi everybody I am new to angular and still trying to get the basics down. I have a view that has two controllers controlling two different pieces of that view:
<div class="row" ng-app="appDiary">
<div ng-controller="entryController as vm">
....
</div>
<div ng-controller="otherController as vm">
....
</div>
<div>
entryController.js / otherController.js:
angular.module("appDiary").controller("entryController", entryController);
function entryController($scope, $http, $filter, service) {
...functionality here...
}
Basically the view html has gotten long and I want to break it up into templates. I tried searching around and using the ngRoute.
angular.module('appDiary', ['simpleControls', 'chart.js', 'ngRoute']);
.config(function ($routeProvider) {
$routeProvider.when("/", {
controller: "entryController", "otherController" // Want both controllers here
controllerAs: vm,
templateUrl: "/views/entry.html", "/views/other.html"
});
});
How do I get Angular to map two controllers/templates to one route? Is there a better way to do this? I tried using some suggestions from Routing in angularjs for mutliple controllers? and couldn't get anything working. Thank you!

Angular controller scope inheritance vs service

On my site I have a navbar component that I want to customize for each and every ng-view I end up loading. Currently I'm doing this as follows. I have a NavCtrl for the navbar itself and my ng-view directive sits outside the scope of this controller. And I use a navbar service to change/override functionality in the navbar e.g each of my views need to override the click handler for a save button my navbar. The NavbarService has hookes to set the save function. And in the NavCtrl the $scope.save = NavbarService.save
var app = angular.module('myApp', ['ngRoute', 'ngResource']);
app.config(function($routeProvider) {
$routeProvider.when('/world', {templateUrl : 'world.html', controller : 'WorldCtrl'}).otherwise({templateUrl : 'hello.html', controller : 'HelloCtrl'});
});
app.service('NavbarService', function() {
var message = 'Save button not clicked yet',
saveFunction = function() {
this.setMessage('Default save called');
};
this.save = function() {
_saveFunction();
};
this.setSaveFunction = function(funct) {
_saveFunction = funct;
};
this.setMessage = function(newMessage) {
message = newMessage;
};
this.getMessage = function() {
return message;
}
});
app.controller('NavCtrl', function($scope, $location, NavbarService) {
$scope.message = NavbarService.getMessage();
$scope.save = NavbarService.save;
$scope.world = function() {
$location.path('/world');
};
$scope.hello = function() {
$location.path('/hello');
};
$scope.$watch(NavbarService.getMessage, function(newValue) {
$scope.message = newValue;
});
});
app.controller('HelloCtrl', function($scope, NavbarService) {
$scope.init = function() {
NavbarService.setSaveFunction(function() {
NavbarService.setMessage('Save method called from the HelloCtrl');
});
};
});
app.controller('WorldCtrl', function($scope, NavbarService) {
$scope.init = function() {
NavbarService.setSaveFunction(function() {
NavbarService.setMessage('Save method called from the WorldCtrl');
});
};
});
<html lang="en">
<head>
<title>My App</title>
</head>
<body ng-app="myApp">
<nav ng-controller="NavCtrl">
<button ng-click="save()">Save</button>
<button ng-click="world()">Go to world</button>
<button ng-click="hello()">Go to hello</button>
<pre>{{message}}</pre>
</nav>
<div ng-view onload="init()"></div>
<script type="text/ng-template" id="hello.html">
<h2>Active view is Hello</h2>
</script>
<script type="text/ng-template" id="world.html">
<h2>Active view is World</h2>
</script>
<script src="https://code.jquery.com/jquery-2.1.4.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular-route.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular-resource.js"></script>
</body>
</html>
I'm wondering if I'm making this too complicated. I think the same thing can be achieved by just nesting the ng-view directive within the NavCtrl's scope. That way I can override $scope.save in each view controller.
But most the documentation states that services are the preferred way to share resources across controllers. Is one way better than the other? Why?
Please advise. Thanks.
However I will have to repeat the <button ng-click="save()">Save</button> element in every template if since all my views need that button.
This comment clears up a few things, and in that case I do not necessarily recommend the dynamic/declarative approach. Instead, this particular UX can be considered more of a static UX and I'd do something a little differently that still avoids any service or scope inheritance. I suggest using the following:
app level
Your app level nav can still borrow the other answer's suggestion, however let's disregard the dynamic/declarative content. So the nav looks like:
<div class="navbar navbar-submenu2">
<ul class="nav navbar-nav navbar-submenu2">
<!-- container for dynamic view-specific menu content -->
<li nx-view-menu-container class="nav navbar-nav">
</li>
<!-- static nav items -->
<li class="nav navbar-nav">
<a ng-click="saveRqst()">
<span class="current-del-name">Save</span>
</a>
</li>
The app-level controller would have this:
.controller 'appController', [
'$scope'
($scope)->
$scope.saveRqst = ->
$scope.$root.$broadcast 'saveRqst', <addt. data if applicable>
]
per view level controller
This makes a few assumptions:
only on of these types of views are ever present (meaning it's one view/controller per application state, otherwise you might have 2 controllers trying to process a save rqst)
your views reside as a child within your application template
your views adhere to the normal view controller paradigm (meaning they're not special directives with isolate scopes)
.controller 'someController', [
'$scope'
($scope)->
$scope.$on 'saveRqst`, (data)-> #do something for this view
]
Now I know this looks kinda like the scope inheritance bit, but it's not. You have to define your save logic per view controller logic anyway, there's no getting around that if I've understood what you've presented in the question and other answer's comment. The one nice bit about this event bus approach is it's easy enough to track the logic. If you notate your code, then another developer who's had any experience working with Java or Flex or whatever, will easily be able to get up to speed on implementing additional views w. save logic.
So I've presented 2 approaches to this. One is the more dynamic, declarative approach and this one is the more static, yet unique-per-view approach.
preface & some history
Let me propose something that utilizes neither a service, nor scope inheritance. Here is a similar issue I was trying to tackle. I have views that get loaded via ui.router's ui-view (we're basically talking application states here right?). I have my normal application nav & subnav, something like this...
['findings', 'tasks', 'reports', 'blah']
...that are common to all views. But I also have some specific UI per view that I want wired to that view's $scope/controller. In other words, I may have a dropdown specific to the current view, and a typeahead for another view. I elected to tackle this declaratively with a directive rather than trying to force some inheritance override paradigm.
directives
nxViewMenuContainer
First off I made a directive that was simply a container for housing the declared elements specific the views' nav elements.
.directive 'nxViewMenuContainer', [
()->
dir =
restrict: 'EAC'
link: ($scope, elem, attrs)->
if !elem.attr 'id'
elem.attr 'id', 'nx-view-menu-container'
$scope.$root.$on '$stateChangeSuccess', ->
elem.empty()
]
This is implemented like so:
<div class="navbar navbar-submenu2">
<ul class="nav navbar-nav navbar-submenu2">
<!-- container for dynamic view-specific menu content -->
<li nx-view-menu-container class="nav navbar-nav">
</li>
<!-- static nav items -->
<li class="nav navbar-nav" ng-repeat="nav in app.subnav">
<a><span class="current-del-name">{{nav.label}}</span></a>
</li>
nxViewMenu
This directive is declared on each view and serves as the view-specific elements container that gets moved to the directive above.
.directive 'nxViewMenu', [
()->
dir =
restrict: 'EAC'
link: ($scope, elem, attrs)->
elem.ready ->
name = attrs.nxViewMenu or '#nx-view-menu-container'
target = $(name)
if !target.length
return
target.empty()
target.append elem
]
And inside each view where I might want a dynamic menu to appear in else where (in this case, in the app-level's nav container) I declare it inside my view templates.
view 1
<div class="nx-view-menu">
<a class="btn btn-default">
<i class="fa fa-lg fa-bars nx-clickout-filter"></i>
<!--<label name="panel-title" style="color:#58585b; padding-left:5px;" class="nx-clickout-filter">Quick Links</label>-->
</a>
</div>
view 2
<div class="nx-view-menu">
<input typeahead="...">
</div>
view 3
<div class="nx-view-menu">
<date-picker>...</date-picker>
</div>
closing arguments
Firstly you are doing things declaratively so it's easy to follow the logic once you understand the directives.
It keeps the top level controllers ignorant of the specific views' logic while still displaying the DOM in a consistent way
It bypasses the need for special services or some hacky message bus
It simplifies your overall DOM structure because you don't need to set up some dual-view containers (in my case that was the other option in using ui.router) or parallel states.

How to prevent losing data when moving from one state to another in multi-step form?

I am new to web programming and especially to AngularJS.
So maybe my question will seem naive to some of you.
I'm developing a single page application using angular-ui-router.
I have created a multi-step form that contains 3 states:
(function () {
"use strict";
angular.module("sensorManagement", ["ui.router", "ngAnimate", "ngResource", "toaster"])
.config(["$stateProvider", "$urlRouterProvider", function ($stateProvider, $urlRouterPorvider) {
$urlRouterPorvider.otherwise("/Sensor/Home");
$stateProvider
.state("MultiStepForm", {
url: "/Sensor/MuiltyStepForm",
templateUrl: "/app/MultiStepForm/MuiltyStepForm.html",
})
.state('MultiStepForm.step1', {
url: '/step1',
templateUrl: '/app/MultiStepForm/FormStep1.html'
})
.state('MultiStepForm.step2', {
url: '/step2',
templateUrl: '/app/MultiStepForm/FormStep2.html'
})
.state('MultiStepForm.step3', {
url: '/step3',
templateUrl: '/app/MultiStepForm/FormStep3.html'
});
}]);
})();
Here is the HTML code:
<!-- form.html -->
<div class="row">
<div class="col-sm-8 col-sm-offset-2">
<div id="form-multiple-step">
<div class="page-header text-center">
<!-- the links to our nested states using relative paths -->
<!-- add the active class if the state matches our ui-sref -->
<div id="status-buttons" class="text-center">
<a ui-sref-active="active" ui-sref=".step1"><span>STEP1</span></a>
<a ui-sref-active="active" ui-sref=".step2"><span>STEP2</span></a>
<a ui-sref-active="active" ui-sref=".step3"><span>STEP3</span></a>
</div>
</div>
<form id="single-form">
<!-- our nested state views will be injected here -->
<div id="form-views" ui-view></div>
</form>
</div>
</div>
</div>
As you can see I have 3 states and each state has it's own view. The views have multiple elements (textboxes and checkboxes).
When the user enters some data for the STEP1 view and moves to the next step (STEP2 or STEP3) then at some point goes back to STEP1 all data is deleted. I checked with fiddler and can see that when I move from one state to another a call is made to the server and a new view generated.
My question is how can I prevent the lose of data when I move from one state to another? Do I have to use caching? Or maybe there is another way to prevent server calls and keep the data alive until the submit button clicked.
When you feel you have to save data across your controllers in your app. you should use a service.
app.factory('sharedDataService', function ($rootScope) {
console.log('sharedDataService service loaded.');
// do something here that can be shared in other controller
});
//pass this service in your controller to use it there
app.controller('Controller2', ['$scope', 'sharedDataService', function ($scope, sharedData) {
console.log('Controller2 controller loaded.');
//get data from shared service
}]);
find a useful Fiddle here
Cheers if it helps!
I think what you need to do is share you $scope between the parent and child stats. here is the stackoverflow post with good example. https://stackoverflow.com/a/27699798/284225

Unable to display angularjs page data after route change.

I am relatively new to angularjs and seems like I am missing something obvious here. I tried to go through documentation and tutorials, but unable to find any solution to my problem.
I have a REST service at "localhost:8080/app/rest/schools/{schoolId}/selectedindicators". I can display the results in an angularjs app page (selectedindicator.html) like so:
<tbody>
<tr ng-repeat="selectedindicator in selectedindicators">
<td>{{selectedindicator.id}}</td>
<td>{{selectedindicator.school.name}}</td>
<td>{{selectedindicator.userId}}</td>
<td>{{selectedindicator.dateVisit}}</td>
<td>
<button type="submit"
ng-click="update(selectedindicator.id)"
class="btn">
<span class="glyphicon glyphicon-pencil"></span> Edit
</button>
</td>
</tr>
</tbody>
Until this point everything is working fine. But, when I try to select one of the selected indicators by clicking on the Edit button I can not display the data on the Edit (selectedindicatorForm.html) page. Here is chunk of my Edit page.
<div>
<div class="form-group">
<label>ID</label> <input type="text" class="form-control" name="id"
ng-model="selectedindicator.id" required>
</div>
.
.
.
.
.
.
</div>
Here is my routeProvider code:
myApp
.config(['$routeProvider', '$httpProvider', '$translateProvider',
function ($routeProvider, $httpProvider, $translateProvider) {
$routeProvider
.when('/selectedindicator', {
templateUrl: 'views/selectedindicators.html',
controller: 'SelectedIndicatorController',
resolve:{
resolvedSelectedIndicator: ['SelectedIndicator', function (SelectedIndicator) {
return SelectedIndicator.query();
}]
}
})
.when('/selectedindicatorForm',{
templateUrl: 'views/selectedindicatorForm.html',
controller: 'SelectedIndicatorFormController',
})
}]);
Here is my controller code for the update method:
myApp.controller('SelectedIndicatorController', ['$scope', '$location', '$route','resolvedSelectedIndicator', 'SelectedIndicator',
function ($scope, $location,$route, resolvedSelectedIndicator, SelectedIndicator) {
$scope.selectedindicators = resolvedSelectedIndicator;
$scope.update = function (id) {
$scope.selectedindicator = SelectedIndicator.get({id: id});
$location.path("/selectedindicatorForm");
};
}]);
you can add a separate service (singleton object) to pass data between controllers .
looks like you SelectedIndicator (which is passed onto SelectedIndicatorController) is already doing that and you can simply re-use it .
On your SelectedIndicatorFormController inject this service SelectedIndicator and call SelectedIndicator.get({id: id});
Note - looks like this line $scope.selectedindicator = SelectedIndicator.get({id: id}); inside SelectedIndicatorController doesn't serve any purpose.
If you could setup a plunk, I could help you.
Is there some reason you're transitioning using $location.path?
The way I'd do that would be just put an or something, with a class="btn" or some other css styling if you really want it to look like a button. With regular ngRoute, you just work with URLs, and should put all the state into the URL. I'm pretty sure setting a $scope variable and then manually changing the state doesn't actually do anything, since the new state has its own $scope.
To be perfectly frank after looking at that code, I'm going to say that you probably ought to read some angular tutorial stuff. I found egghead.io to be pretty great myself.

How to access route parameters outside the view

I'm writing a very simple Angular app for showing information about football tournaments. For each tournament in the database I want to show a view of either the matches or the statistics, so I'm implementing this simple URL scheme:
foo/matches: matches of tournament Foo,
foo/stats: statistics for tournament Foo.
The index.html file is structured like this:
<nav ng-controller="NavController">
<ul>
<li> Matches
<li> Stats
</ul>
</nav>
<div ng-view></div>
and the routes are configured like this:
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/:t/matches', {templateUrl: 'partials/matches.html', controller: 'MatchesController'})
.when('/:t/stats', {templateUrl: 'partials/stats.html', controller: 'StatsController' })
}]);
My problem are those {{ t }} links in the HTML. I want the links to change the view to the stats or the match list for the current tournament. But since the links are outside the view, so they don't have access to the route parameters (I tried injecting $routeParams and all I get is an empty object).
In summary, I haven't been able to create links for changing the view outside of it. The nav bar doesn't know what is the current tournament.
How can I (and what is the best way to) access the current state of the view (in this case, the current tournament) outside of it?
After reading a bit about scopes I learned how they nest. I solved my problem simply by wrapping both the view and the navigation bar with a new controller:
<div ng-controller="NavController">
<nav>
<ul>
<li> Matches
<li> Stats
</ul>
</nav>
<div ng-view></div>
</div>
The view will inherit NavController's scope prototypically. I initialized an object in the outer scope, and assigned the desired value to one of its properties in the inner controllers:
app.controller('NavController', function ($scope) {
$scope.currentTournament = {};
});
app.controller('MatchesController', function ($scope, $routeParams) {
// ...
$scope.currentTournament.id = $routeParams.t;
});
app.controller('StatsController', function ($scope, $routeParams) {
// ...
$scope.currentTournament.id = $routeParams.t;
});
If you are using ui-router, you could use the $stateParams service to access state and state variables from anywhere in your app.

Resources