I am computing/building a javascript object in a directive (component) and I want to pass it to another component.
In my case, I am referring to the list of heroes that that is in the heroList.js (which is the source component), and I want to transfer the passingObject to the someOtherTabPage.js (which is the destination component).
This is the link to my plunk. Can you please help with this problem, I don't what's wrong in binding the passingObject to between my two components?
(function(angular) {
'use strict';
function someOtherTabPageController($scope) {
//do some work with the passingObject
alert(JSON.stringify(passingObject));
}
angular.module('heroApp').component('someOtherTabPage', {
templateUrl: 'someOtherTabPage.html',
controller: someOtherTabPageController,
bindings :{
passingObject: '='
}
});
})(window.angular);
To achieve what you need with the same architecture you can use the $rootScope to pass the data between your controllers. Here is the updated code :
(function(angular) {
'use strict';
function someOtherTabPageController($scope,$rootScope) {
var ctrl = this;
//do some work with the passingObject
alert($rootScope.passingObject);
}
angular.module('heroApp').component('someOtherTabPage', {
templateUrl: 'someOtherTabPage.html',
controller: someOtherTabPageController,
bindings :{
passingObject: '='
}
});
})(window.angular);
(function(angular) {
'use strict';
function HeroListController($scope, $element, $attrs,$rootScope) {
var ctrl = this;
// This would be loaded by $http etc.
ctrl.list = [
{
name: 'Superman',
location: ''
},
{
name: 'Batman',
location: 'Wayne Manor'
}
];
ctrl.create = function(hero) {
ctrl.list.push(angular.copy(hero));
};
ctrl.updateHero = function(hero, prop, value) {
hero[prop] = value;
};
ctrl.deleteHero = function(hero) {
var idx = ctrl.list.indexOf(hero);
if (idx >= 0) {
ctrl.list.splice(idx, 1);
}
};
$scope.passingObject = ctrl.list;
$rootScope.passingObject = ctrl.list;
}
angular.module('heroApp').component('heroList', {
templateUrl: 'heroList.html',
controller: HeroListController,
bindings: {
onCreate: '&'
}
});
})(window.angular);
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example - example-heroComponentTree-production</title>
<script src="//code.angularjs.org/snapshot/angular.min.js"></script>
<script src="index.js"></script>
<script src="heroList.js"></script>
<script src="heroDetail.js"></script>
<script src="editableField.js"></script>
<script src="someOtherTabPage.js"></script>
</head>
<body ng-app="heroApp">
<hero-list></hero-list>
<some-other-tab-page></some-other-tab-page>
</body>
</html>
I think the way you're meant to grab the passingObject in the controller, after it has been bound by the component is via the this keyword
function someOtherTabPageController($scope) {
var ctrl = this;
//do some work with the passingObject
alert(JSON.stringify(ctrl.passingObject));
}
You can see the same principle applied in your example plunk in heroDetail.js in the HeroDetailController
EDIT to address last comment: 'passingObject is undefined'
My first guess would be that the component wasn't passed the passingObject in the instantiation. The right way would look like this:
<some-other-tab-page passing-object="someObjectHere"></some-other-tab-page>
Related
I'm trying to component-ise my codebase and have come up stuck on this problem.
When using $scopes and Controllers I would pass a server token to my rest call method with ng-init. Trying to do the same with a Component is not working.
javascript
angular
.module('myApp', [])
.controller('mainCtrl', function() {
var self = this;
self.options = function() {
var o = {}
o.token = self.serverToken
return o;
}
self.restData = {
url: 'http://rest.url',
options: self.options()
}
})
.component('myComponent', {
bindings: {
restData: '<'
},
template: '<p>template, calls child components</p>',
controller: function(restService) {
this.callRestService = function() {
restService.get(this.restData.url, this.restData.options)
}
console.log(this.restData.url) // http://rest.url
console.log(this.restData.options) // {token: undefined}
}
})
html
<html ng-app="myApp">
<!-- head -->
<body ng-controller="mainCtrl as m" ng-init="m.serverToken='12345'">
<my-component rest-data="m.restData"></my-component>
</body>
</html>
How do I pass the value to the component?
The issue is that the ng-init is executed after the controller is instantiated. However you are creating your restData object during the construction of your controller at which time the serverToken is undefined.
You could build your restData object after ng-init is called with something like this:
.controller('mainCtrl', function() {
var self = this;
self.restData = {};
self.init = function(token) {
self.serverToken=token;
self.restData = {
url: 'http://rest.url',
options: {token:token}
};
};
})
Your component can then do something when that restData changes. For example:
.component('myComponent', {
bindings: {
restData: '<'
},
template: '<p>template, calls child components</p>',
controller: function(restService) {
this.callRestService = function() {
restService.get(this.restData.url, this.restData.options)
}
this.$onChanges = function(changes) {
console.log(this.restData) // http://rest.url
console.log(this.restData.options) // {token: 12345}
this.callRestService();
}
}
});
The HTML would change to call your init method:
<body ng-controller="mainCtrl as m" ng-init="m.init(12345)">
<my-component rest-data="m.restData"></my-component>
</body>
I am relatively new to angularJS, I am trying to set up a page where inturn multiple pages are called depending upon the selection made previously.
All the pages have their own controller, so I am trying to set the controller and view src through the javascript and using them in HTML tags.
Following is what I am doing:
HTML page:
<div ng-if="sidebarName=='sidebar-device-wire'">
<div ng-controller="getSidebarCtlr">
<div ng-include src="sidebarSrc"></div>
</div>
</div>
javascript:
$scope.sidebarSrc="views/sidebars/sidebar-device.html";
$scope.sidebarCtlr="SidebarDeviceCtrl";
$scope.getSidebarCtlr = function(){return $scope.sidebarCtlr;}
For some reason though, this does not work. i can get the HTML page but the controller is not being called. Can anyone please tell me what I am doing wrong?
I would also recommend to use ngRoute or ui.router because there are many features that aren't easy to implement from scratch (like named views, nested views / nested states or resolves) and these modules are well tested.
Not sure why your controller isn't running but I guess that the expression of the controller is evaluated before your controller that is setting the name is running. So it will be always undefined at compile time.
But if you really like to implement a very basic router you could do it like in the following demo (or in this fiddle).
Update 21.12.2015
Here are some router examples that I wrote for other SO questions:
simple ui.router example - jsfiddle
more complex nested state example ui.router - jsfiddle
dynamic link list with ngRoute - jsfiddle
Please also have a look at ui.router github pages to learn more about it.
angular.module('simpleRouter', [])
.directive('simpleView', simpleViewDirective)
.provider('simpleRoutes', SimpleRoutesProvider)
.controller('MainController', MainController)
.controller('HomeController', HomeController)
.config(function(simpleRoutesProvider) {
simpleRoutesProvider.state([{
url: '/',
templateUrl: 'home.html',
controller: 'HomeController'
}, {
url: '/view1',
templateUrl: 'view1.html'
}, {
url: '/view2',
templateUrl: 'view2.html',
controller: function($scope) {
$scope.test = 'hello from controller'
}
}]);
simpleRoutesProvider.otherwise('/');
})
function HomeController($scope) {
$scope.hello = 'hello from home controller!!';
console.log('home controller started')
}
function MainController($scope) {
$scope.hello = 'Main controller test';
}
function simpleViewDirective() {
return {
restrict: 'EA',
scope: {},
template: '<div ng-include="templateUrl"></div>',
controller: function($scope, $location, $controller, simpleRoutes) {
var childControllerInst;
$scope.templateUrl = simpleRoutes.currentRoute.templateUrl || simpleRoutes.otherwise.templateUrl;
$scope.$watch(function() {
return $location.path();
}, function(newUrl) {
//console.log(newUrl)
$scope.templateUrl = simpleRoutes.changeRoute(newUrl);
childControllerInst = $controller(simpleRoutes.currentRoute.controller || function() {}, {$scope: $scope});
});
$scope.$on('$destroy', function() {
childControllerInst = undefined;
})
},
link: function(scope, element, attrs) {
}
}
}
function SimpleRoutesProvider() {
var router = {
currentRoute: {
templateUrl: ''
},
states: [],
otherwise: {},
changeRoute: function(url) {
var found = false;
angular.forEach(router.states, function(state) {
//console.log('state', state);
if (state.url == url) {
router.currentRoute = state;
found = true;
}
});
if (!found) router.currentRoute = router.otherwise;
//console.log(router.currentRoute);
return router.currentRoute.templateUrl;
}
};
this.state = function(stateObj) {
router.states = stateObj;
};
this.otherwise = function(route) {
angular.forEach(router.states, function(state) {
if (route === state.url ) {
router.otherwise = state;
}
});
//console.log(router.otherwise);
};
this.$get = function simpleRoutesFactory() {
return router;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="simpleRouter" ng-controller="MainController">
<script type="text/ng-template" id="home.html">home route {{hello}}</script>
<script type="text/ng-template" id="view1.html">view1</script>
<script type="text/ng-template" id="view2.html">view2 {{test}}</script>
<div simple-view="">
</div>
home
view1
view2
<br/>
{{hello}}
</div>
What's that code means? $scope.getSidebarCtlr = function(){return $scope.sidebarCtlr;}
the ng-directive requires a Controller name, its argument type is string and you cannot pass a simple function, you need to register a valid controller associating it to a module via the controller recipe.
https://docs.angularjs.org/guide/controller
angular.module('test', []).controller('TestCtrl', function($scope) {
$scope.greetings = "Hello World";
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<section ng-app="test">
<article ng-controller="TestCtrl">{{ greetings }}</article>
</section>
So i still new to angular and java script but i read it was good practical to wrap the everything into functions.
This is what i have done.
in comment is the previous version
app.js
//var app = angular.module('app',['ngRoute','ngResource','routes']);
(function(angular) {
angular.module("app.directives", []);
angular.module("app.controllers", []);
angular.module("app", ['ngRoute','ngResource','routes','app.directives','app.controllers']);
}(angular));
directives.js
/*
app.directive('footer', function () {
return {
replace: true,
templateUrl: "/template/main_footer.html",
controller: ['$scope', '$filter', function ($scope, $filter) {
}]
}
});
*/
(function(angular) {
var footer = function () {
return {
replace: true,
templateUrl: "/template/main_footer.html",
controller: ['$scope', '$filter', function ($scope, $filter) {
}]
};
};
footer.$inject = [];
angular.module("app.directives").controller("footer", footer);
});
controller.js
/*app.controller('HeaderController',function($scope, $location) {
$scope.isActive = function (viewLocation) {
var active = (viewLocation === $location.path());
return active;
};
});*/
(function(angular) {
var HeaderController = function($scope,$location){
$scope.isActive = function(viewLocation){
var active = (viewLocation === $location.path());
return active;
};
}
HeaderController.$inject = ['$scope','$location'];
angular.module("app.controllers").controller("HeaderController", HeaderController);
})
and how should i proceed for routes.js
angular.module('routes', []).config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'pages/home.html'
})
.when('/second', {
templateUrl: 'pages/second.html'
})
.when('/third', {
templateUrl: 'pages/third.html'
})
.when('/123', {
templateUrl: 'pages/123.html'
})
.otherwise({
redirectTo: '/'
});
});
index.html
<!DOCTYPE html>
<html ng-app="app">
<head>
<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/custom.css">
<script type="text/javascript" src="https://code.angularjs.org/1.4.4/angular.js"></script>
<script type="text/javascript" src="https://code.angularjs.org/1.4.4/angular-resource.js"></script>
<script type="text/javascript" src="https://code.angularjs.org/1.4.4/angular-route.js"></script>
<script type="text/javascript" src="js/routes.js"></script>
<script type="text/javascript" src="js/app.js"></script>
<script type="text/javascript" src="js/controllers.js"></script>
<script type="text/javascript" src="js/directives.js"></script>
</head>
<body>
<div ng-view></div>
</body>
</html>
But it does not work. And i find it hard to find what went wrong because i don't get a error with the development tools on google chrome
Update
Now i do call the function at the end.
But i still can't see the footer. I also added the footer.html just in case there would something i forgot there.
directives.js
(function(angular) {
var footer = function () {
return {
replace: true,
templateUrl: "/template/main_footer.html",
controller: ['$scope', '$filter', function ($scope, $filter) {
}]
};
};
footer.$inject = [];
angular.module("app.directives").controller("footer", footer);
}(angular));
home.html
<div>
<div>
<h3>This is the homepage</h3>
</div>
<div footer></div>
</div>
In the 'directives.js' and 'controller.js' files, you forgot (angular) at the end, like in 'app.js'.
If you write a function like this:
function f() {
// do stuff...
}
it's never going to run unless you call it somewhere: f();. Now, if you want to wrap a bit of code in a function, you still want it to run immediately as if it had not been wrapped. So what you do is you call the wrapping function immediately like this:
(function f() {
// do stuff...
})();
or like this (same thing):
(function f() {
// do stuff...
}());
In that second way of writing things, the outermost parentheses are useless for the program but they help the reader see that the function will be immediately run (or "evaluated", or "called").
You can also pass anything that lives outside of the function into the function, for example angular:
(function f(angular) {
// do stuff...
}(angular));
That is because you're missing the function invocation in some of those wrappings. For example, controller.js needs to be:
(function(angular) {
var HeaderController = function($scope,$location){
$scope.isActive = function(viewLocation){
var active = (viewLocation === $location.path());
return active;
};
}
HeaderController.$inject = ['$scope','$location'];
angular.module("app.controllers").controller("HeaderController", HeaderController);
})(angular); // CALL FUNCTION
Note in the last line I added (angular); to actually call the function.
You can see a snippet of some code i am working on below.
angular.module('myApp', [])
.directive('maskedInput', [
function() {
return {
scope: {
mymask: '=maskedInput'
},
link: function(scope, elem, attrs) {
console.log('attributes', attrs);
scope.$watch('mymask', function(newVal) {
console.log(newVal);
elem.inputmask(newVal);
}, true)
}
}
}
])
.controller('MainCtrl', ['$scope',
function($scope) {
$scope.mask = {
mask: '99999[-9999]',
greedy: false
};
}
]);
<script src="https:/cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.min.js"></script>
<script src="https://rawgit.com/RobinHerbots/jquery.inputmask/3.x/dist/jquery.inputmask.bundle.js"></script>
<body>
<div ng-app="myApp" ng-controller="MainCtrl">
<h3>Mask</h3>
<p>Mask: 99999[-9999]</p>
<input type="text" ng-model="test" masked-input="mask">
<br />
<input type="text" ng-model="mask.mask" />
<br />{{mask}}
<br/>{{test}}
</td>
</body>
I am working on recreating this in typescript. Here's what I have so far.
class InputMaskDirective implements ng.IDirective {
constructor () {
var directive: ng.IDirective = {};
directive.restrict = 'A';
directive.scope = { mymask : '=maskedInput' };
directive.link = (scope: any, element: ng.IAugmentedJQuery, attrs: ng.IAttributes) => {
scope.$watch('mymask', function (newVal) {
element.inputmask(newVal);
}, true);
};
return directive;
}
}
export = InputMaskDirective;
We are using requirejs as well, so when we instanciate the class in our app.ts file, we do something like
import MaskedInputDirective = require("src/common/directives/inputmaskdirective");
var application: angular.IModule = angular.module("application",[InjectionStuffHere]);
application.directive("maskedInput",[MaskedInputDirective]);
export = application;
The problem i forsee is that because of the "class" structure of requirejs and the typescript code, in theory a user can name the directive whatever they want.
Wouldn't that break the code with the scope? Since the scope is looking for the masked-input, the snake cased name of the directive, to be in the html, if the user named it something else, how would it know the correct name?
I've tried attrs to see if I can get something out of it, but no luck. Do I just have to hope that the users don't muck with it when they create the code?
I follow this structure when writing angular directives in typescript.
Factories and Directives are created as functions, where directive-functions return a :ng.IDirective instead.
function inputMaskDirective(): ng.IDirective {
return {
restrict: "A",
scope: {myMask: "=maskedInput"},
//and on
}
}
if you have any injects add them to the property$inject of the function
inputMaskDirective.$inject = ["someService"];
I'm developing an AngularJS application and I would like to integrate some functions to let an other web-application interact with the application. My application should run and triggered on the same website. Iframes are not used.
The application has several tabs, and I would like to set a tab active by specifying it's id.
This has been done in an AngularJS controller, through this method:
$scope.setActiveTab = function(tabId) {
ribbon.setActiveTab(tabId);
$scope.$apply();
}
In my application, I have a ng-click attribute and this function is being called in it. But now, I do want a other application to set the active tab by calling some JavaScript function.
Therefore, I've written the following function:
$.fn.OfficeUI.EnableTab = function(element) {
angular.element($('#OfficeUI')).scope().setActiveTab('tabSendReceive');
}
However, this doesn't work, but when I past this method:
angular.element($('#OfficeUI')).scope().setActiveTab('tabSendReceive');
in the developer console of FireBug or Google Chrome, then it's working.
Any idea on what's wrong and how I should solve this?
Kind regards
As I understand you right. You need a application which allows developers (as you said Users :) to interact and configure your application by using HTML/JavaScript. Checkout this plunker demo.
A good approach is to do this with configuration files, global object, or events bindings. The following example shows you, how to let a user configure an a global config object in JavaScript & allows developers to interact with your application. For example by using $scopes.
Controller with global configuration object:
/**
* Active state holder
* #type {null|integer}
*/
var myConfig = {
menus : {
mainbar: {
active: true
},
subbar: {
active: false
},
othersubbar: {
active: false
}
}
};
var OfficeUIRibbon = angular.module('OfficeUIRibbon', [])
OfficeUIRibbon.factory('OfficeUIRibbonFactory', [function() {
return {
setActiveTabId: function (tabId) {
activeTabId = tabId;
}
}
}]);
OfficeUIRibbon.controller('OfficeUIRibbon', ['$scope', '$http', '$animate', function($scope, $http, $animate) {
//as scope set
$scope.isVisible = function (name){
if (myConfig.menus[name].active) {
return 'active';
}
}
$scope.activateMenu = function (name) {
console.log(name);
if (!myConfig.menus[name].active) {
myConfig.menus[name].active = true;
}
}
$scope.deactivateMenu = function (name) {
if (myConfig.menus[name].active) {
myConfig.menus[name].active = false;
}
}
$scope.listing = {
mainbar: {
id: 1,
label: 'mainbar'
},
subbar: {
id: 2,
label: 'subbar'
},
othersubbar: {
id: 3,
label: 'othersubbar'
}
}
}]);
HTML-Template:
<head>
<link rel="stylesheet" href="style.css">
<script src="https://code.angularjs.org/1.3.10/angular.js"></script>
<script src="script.js"></script>
</head>
<body ng-controller="OfficeUIRibbon">
<ul>
<li ng-repeat="(key,list) in listing" ng-class="isVisible(key)">
{{ list.label }}
</li>
</ul>
<button ng-click="activateMenu('mainbar');">Activate Mainmenu</button>
<button ng-click="deactivateMenu('mainbar');">Deactivate Mainmenu</button>
</body>
</html>
User configuration, should be set before angular was loaded. For example, disable mainbar on default:
myConfig.menus.mainbar.active = false;
Please see demo below that should helps you.
var app = angular.module('app', []);
app.controller('firstCtrl', function($scope) {
$scope.test = function(msg) {
$scope.$apply(function() {
$scope.msg = msg
})
}
});
$(function() {
angular.element(document.querySelector('#contoller')).scope().test("Message for Angular from jQuery");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="app">
<div id="contoller" ng-controller="firstCtrl">
{{msg}}
</div>
</body>
try:-
$scope.apply(function(){
angular.element($('#OfficeUI')).scope().setActiveTab('tabSendReceive');
})
You are in jquery scope you must have to use $scope.apply to reflect it to angular.