I'm honestly really new to angularjs and this might sound like a really stupid question.
I'm trying to place a md-list on my page:
function apply(list){
angular.module('App', ['ngMaterial']).controller('ListCtrl', function($scope, $mdDialog) {
$scope.people = list;
$scope.goToPerson = function(person, event) {
$mdDialog.show(
$mdDialog.alert()
.title('Navigating')
.textContent('Inspect ' + person)
.ariaLabel('Person inspect demo')
.ok('Neat!')
.targetEvent(event)
);
};
});
}
}
HTML:
<md-list ng-controller="ListCtrl" ng-cloak>
<md-subheader class="md-no-sticky">Now online</md-subheader>
<md-list-item ng-repeat="person in people" md-ink-ripple ng-click="goToPerson(person.name, $event)" class="noright">
<p style="color:white; border-bottom: 1px solid rgba(225,225,225,.2); height:45px; line-height:45px">{{ person.uuids }}</p>
<md-icon md-svg-icon="social:android" aria-label="android "></md-icon>
</md-list-item>
</md-list>
needless to say, it doesn't work..
i wanna be able to call the apply() function to compile the md-list. Usually its supposed to load on page start but i dont really want that..can anyone help?
I could imagine that you are not correctly initializing your angular application at all. You need to either have an ng-app="App" attribute somewhere on your page (e.g. on the html body tag) for automatic initialization or you'll have to manually bootstrap your angular application.
More information on the initialization of angular applications (automatic or manual) can be found in the Angular Docs.
Version of apply(list) with manual initialization. Make sure to remove the ng-appattribute completely when using this
function apply(list){
// define the angular module to be initialized
angular
.module('App', ['ngMaterial'])
.controller('ListCtrl', function ($scope, $mdDialog) {
$scope.people = list;
$scope.goToPerson = function(person, event) {
$mdDialog.show();
$mdDialog.alert()
.title('Navigating')
.textContent('Inspect ' + person)
.ariaLabel('Person inspect demo')
.ok('Neat!')
.targetEvent(event);
};
});
// initialize the angular module (this will render the list in the end)
angular.element(document).ready(function() {
angular.bootstrap(document, ['App']);
});
}
Related
I have been tasked with developing an app in Angular 1.6, and I have not done any Angular 1.x for quite some time. Been mostly doing 2.x. In fact, never done 1.6 at all.
I have two components: a tile container, and a tile. The tiles are selectable, so I imagined having a tile container component that would keep track of which tile was selected, and a tile component which is primarily a UI component.
My tile component looks like this:
// dp-claim-filter-tile.component.js
function ClaimFilterTileController() {
var ctrl = this;
ctrl.isDisabled = () => {
return ctrl.claimCount == 0;
}
ctrl.test = function () {
console.log('ctrl = ' + JSON.stringify(ctrl));
ctrl.onTileClicked(ctrl);
}
}
angular.module('dpApp').component('dpClaimFilterTile', {
templateUrl: '/templates/dp-claim-filter-tile.tmpl.html',
controller: ClaimFilterTileController,
bindings: {
tileTitle: '#',
claimCount: '#',
isAdd: '<',
isActive: '<',
onTileClicked: '&'
}
});
And the template looks like this:
// dp-claim-filter-tile.tmpl.html
<div ng-if="$ctrl.isAdd===true" layout="column" layout-align="space-around center">
<div><h2 class="md-title">{{$ctrl.tileTitle}}</h2></div>
<div>
<md-icon md-font-icon="fa-plus" class="fa fa-2x"></md-icon>
</div>
</div>
<div ng-if="$ctrl.isAdd!==true" layout="column" layout-align="space-around center" ng-click="$ctrl.test()">
<div><h2 class="md-title tile-text">{{$ctrl.tileTitle}}</h2></div>
<div class="md-display-1 tile-text">{{$ctrl.claimCount}}</div>
</div>
The tile container component looks like this:
// dp-claim-filter-tiles.component.js
function ClaimFilterTilesController() {
var ctrl = this;
ctrl.tileClicked = function(tile) {
console.log('tile = ' + JSON.stringify(tile));
}
}
angular.module('dpApp').component('dpClaimFilterTiles', {
templateUrl: '/templates/dp-claim-filter-tiles.tmpl.html',
controller: ClaimFilterTilesController
});
And an extract of the container UI looks like this:
// dp-claim-filter-tiles.tmpl.html
<md-grid-tile>
<dp-claim-filter-tile is-active="true" tile-title="Links Sent" claim-count="7" on-tile-clicked="$ctrl.tileClicked($event)"></dp-claim-filter-tile>
</md-grid-tile>
What I am expecting, and hoping for, is for the $event parameter I am supplying to surface as a parameter to the ClaimFilterTilesController.tileClicked() function.
How do I accomplish this?
The click $event will be available in the ngClick directive by itself, you won't be able to carry it around before the actual ngClick directive gets triggered.
Therefore, you could access the click event exclusively in dp-claim-filter-tile.tmpl.html template, like this:
ng-click="$ctrl.test($event, otherParam)"
Try utilizing ng-click rather than the on-tile-clicked. This way you can get at the $event object which can be passed into other functions within your component definition.
<md-grid-tile>
<dp-claim-filter-tile-is-active="true" tile-title="Links Sent" claim-count="7" ng-click="$ctrl.tileClicked($event)"></dp-claim-filter-tile>
</md-grid-tile>
I found the answer on the AngularJS site: Intercomponent Communication. The trick lies in using the require property when defining the component. You can essentially create a reference to the enclosing component. Shall implement tomorrow.
Thanks for your answers guys.
This is my first post/question here on StackOverflow so if you see any improvement I can made - please give me an advice :).
Now let me dive into the issue.
For the sake of simplicity I removed any irrelevant portions of code and presented only necessary files.
//app.modules.js
if (typeof window.app == "undefined") {
window.app = angular.module("AppModule", []);
}
//app.services.js
window.app
.service("settingsOverlaySvc", function($rootScope) {
this.broadcastToggle = function() {
$rootScope.$broadcast("toggle-conf");
};
});
//settings-ctrl.js
window.app.controller("SettingsController", ["$scope", "$window", "$sce", "settingsOverlaySvc",
function($scope, $window, $sce, settingsOverlaySvc) {
$scope.visible = false;
$scope.open = false;
$scope.toggleSettings = function() {
$scope.open = !$scope.open;
};
$scope.broadcastToggle = function() {
settingsOverlaySvc.broadcastToggle();
};
$scope.$on("toggle-conf", function() {
console.log("toggle-conf received");
$scope.visible = !$scope.visible;
});
}
]);
angular.bootstrap($("div[ng-controller='SettingsController']").parent(":not(.ng-scope)"), ["AppModule"]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- Appended by JS - control1.html -->
<div>
<div ng-controller="SettingsController" ng-init="visible=true">
<span class="glyphicon glyphicon-cog" ng-class="{'settings-open': open}" ng-click="broadcastToggle();toggleSettings()">COG</span>
</div>
</div>
<!-- Appended by JS - control2.html-->
<div>
<div ng-controller="SettingsController" ng-cloak>
<div ng-if="visible">
<span class="glyphicon glyphicon-cog" ng-class="{'settings-open': open}" ng-click="toggleSettings()">COG</span>
<div ng-if="open">
<div class="divControl_2">Content_2</div>
</div>
</div>
</div>
</div>
The above snippet works as I expected - the $broadcast is called, all controllers receive the message and reacts. In my application the broadcast is received only by controller sending it. I think the problem is caused by dynamic HTML insertions.
The requirement is to render controls dynamically on page load. I'm using the following approach to generate content.
AddWidgets: function () {
var controlContainer1 = $("<section>", {
class: "sidebar-section",
id: "ctrl1-container"
});
var controlContainer2 = $("<section>", {
class: "sidebar-section",
id: "ctrl2-container"
});
$("aside.sidebar").append(controlContainer1);
$("aside.sidebar").append(controlContainer2);
$("#ctrl1-container").load("..\\assets\\ctrls\\control1.html");
$("#ctrl2-container").load("..\\assets\\ctrls\\control2.html");
}
I'm using the same controller because it shares the same logic for all controls.
I've read a lot materials about $broadcast and $emit functionality, guides on creating controllers, defining modules and services (that one gives me idea about creating service with $rootScope injected).
Now I'm thinking that generating angular content outside angular framework (AddWidgets function) can cause the problem (#stackoverflow/a/15676135/6710729).
What raised my spider sense alarm is when I've checked the JSFiddle example for the similar situation (http://jsfiddle.net/XqDxG/2342/) - no parent-child relation of controllers. When I peek at angulat scope of the controllers I can see that $$nextSibling and $$prevSibling properties are filled. In my case these are nulled [here].
Can you give me some guidelines how can I resolve my issue? I'm fairly new to AngularJS and learning as I'm developing the application.
I'm working on a site, and I started building it before I realized I needed some dynamic framework. After learning about AngularJS, I decided to use it, where I needed (not the whole site).
I have a very long script in JS, and I want to be able to get and set the variables from within AngularJS directives and controllers.
I found this answer, and it was quite good - I was able to get the variable from within the function. But when the variable changed outside the function, AngularJS' variable won't update.
My code looked something like this:
JS:
var app = angular.module('someName', []);
var currentPage = 'Menu';
app.controller('PageController', ['$window','$scope', function($window,$scope){
this.currentPage = $window.currentPage;
this.isPage = function(page){
return (page == this.currentPage);
};
}]);
function button1onClick(){
currentPage = 'Game';
}
HTML:
<div ng-controller="PageController">
<div id="Game" ng-show="page.isPage('Game')">
...
</div>
<div id="Menu" ng-show="page.isPage('Menu')">
...
</div>
</div>
(button1onClick was called when I clicked some button on the page)
The idea is that I have two dives I want to switch between, using a globle variable. 'Menu' page was visible at first but upon clicking I was supposed to see only the 'Game' div.
The variable inside the controller didn't upadte, but was only given the initial value of currentPage.
I decided to use the $window service inside the isPage function, but this didn't work either. Only when I called a function that tested the $window.currentPage variable, the pages switched - like I wanted:
JS:
var app = angular.module('someName', []);
var currentPage = 'Menu';
app.controller('PageController', ['$window','$scope', function($window,$scope){
this.isPage = function(page){
return (page == $window.currentPage);
};
this.button2onClick = function() {
$window.alert($window.currentPage);
}
}]);
function button1onClick(){
currentPage = 'Game';
}
HTML:
<button onclick="button1onClick()">CLICK ME</button> //Button 1
<div ng-controller="PageController">
<button ng-click="page.button2onClick">CLICK ME</button> //Button 2
<div id="Game" ng-show="page.isPage('Game')">
...
</div>
<div id="Menu" ng-show="page.isPage('Menu')">
...
</div>
</div>
So the only way I was able to update the pages is to call a function that tests the variable, thus updating the variable in AngularJS.
Is there a way to access a global variable without needing to test it to update it?
Am I doing something wrong? I don't want to convert my whole site to AngularJS-style, I like the code the way it is. Is AngularJS not the framework for me?
EDIT:
some things to clear out:
I'm new to AngularJS, so if you could explain what your answer does it would be great.
The whole reason why I do this instead of redirecting to another page is not to shut down socket.io 's connection
OP, take a look at UI Router.
var app = angular.module("app", ['ui.router']);
app.config(['$urlRouterProvider', '$stateProvider', function($urlRouterProvider, $stateProvider) {
$urlRouterProvider.otherwise('/main');
$stateProvider.state('main', {
controller: 'MainCtrl',
templateUrl: 'main.html',
url: '/main/'
}).state('game', {
controller: 'GameCtrl',
url: '/game/',
templateUrl: 'game.html'
});
}]);
HTML links:
<a ui-sref="main">Go to Main</a>
<a ui-sref="game">Go to Game</a>
View injection
<div ui-view="">
</div>
You should not use $window as a map object.
You should probably create a PageService:
angular.module('someName')
.factory('Page', [function(){
var currentPage = 'Menu';
return {
getPage: function() {
return currentPage;
},
isPage: function(page) {
return page === currentPage;
},
setPage: function(page) {
currentPage = page;
}
}
}]);
app.controller('PageController', ['Page','$scope', function(Page,$scope){
this.currentPage = Page.getPage();
this.isPage = Page.isPage;
this.button10Click = function(){
Page.setPage('Game');
}
}]);
HTML
<div class="button" ng-click="page.button10Click()">Game</div>
After reading malix's answer and KKKKKKKK's answer, and after researching a bit, I was able to solve my problem, and even write a better looking code.
To switch divs as I wanted in the example, I used ui-router, almost exactly the way KKKKKKKK did. The only difference is that I change state programmaticly - $state.go('menu')
To access global variables in other places in my code, I had to re-structure my whole code to fit AngularJS's structure, and used a Service, similarly to malix's answer:
app.factory('Data', function(){
var Data = {};
//DEFINE DATA
Data.stateChange = function(){};
Data.menuData = {};
Data.randomColors = ['#35cd96', '#6bcbef', '#E8E1DA', '#91ab01'];
/RETURN DATA
return Data;
});
It can be done using $rootScope. Variable initialize once can be accessible in other controllers.
function Ctrl1($scope, $rootScope) {
$rootScope.GlobalJSVariableA= window.GlobalJSVariable; }
Any controller & any view can access it now
function CtrlN($scope, $rootScope) {
$scope.GlobalJSVariableA= $rootScope.GlobalJSVariableA;
}
Have an angular application. Using ui-select.
<div class="col-md-12" ng-controller="FlatController as flat">
<form ng-submit="flat.createFlat()">
<ui-select ng-model="flat.flatData.typelocal" theme="bootstrap">
<ui-select-match placeholder="Type" id="localtype">
{{ $select.selected.type }}
</ui-select-match>
<ui-select-choices repeat="uitypelocal.type as uitypelocal in flat.typelocal1 track by $index | filter: $select.search">
<div ng-bind-html="uitypelocal.type | highlight: $select.search"></div>
</ui-select-choices>
</ui-select>
<button class="btn btn-danger">Send</button>
</form>
</div>
In form i have some inputs and selects. Now, i want to have in the select data from another controller. How can i do it?
I have a service:
angular.module('flatService', [])
.factory('Flat', function($http){
var flatFactory = {};
flatFactory.allFlats = function(){
return $http.get('/api/flats');
};
flatFactory.create = function(flatData){
return $http.post('/api/addflat', flatData);
};
return flatFactory;
})
});
If you want to call one controller into another there are five methods available
$rootScope.$emit() and $rootScope.$broadcast()
If Second controller is child ,you can use Parent child communication .
Use Services
Kind of hack - with the help of angular.element()
inject '$controller'
1. $rootScope.$emit() and $rootScope.$broadcast()
Controller and its scope can get destroyed,
but the $rootScope remains across the application, that's why we are taking $rootScope because $rootScope is parent of all scopes .
If you are performing communication from parent to child and even child wants to communicate with its siblings, you can use $broadcast
If you are performing communication from child to parent ,no siblings invovled then you can use $rootScope.$emit
HTML
<body ng-app="myApp">
<div ng-controller="ParentCtrl" class="ng-scope">
// ParentCtrl
<div ng-controller="Sibling1" class="ng-scope">
// Sibling first controller
</div>
<div ng-controller="Sibling2" class="ng-scope">
// Sibling Second controller
<div ng-controller="Child" class="ng-scope">
// Child controller
</div>
</div>
</div>
</body>
Angularjs Code
var app = angular.module('myApp',[]);//We will use it throughout the example
app.controller('Child', function($rootScope) {
$rootScope.$emit('childEmit', 'Child calling parent');
$rootScope.$broadcast('siblingAndParent');
});
app.controller('Sibling1', function($rootScope) {
$rootScope.$on('childEmit', function(event, data) {
console.log(data + ' Inside Sibling one');
});
$rootScope.$on('siblingAndParent', function(event, data) {
console.log('broadcast from child in parent');
});
});
app.controller('Sibling2', function($rootScope) {
$rootScope.$on('childEmit', function(event, data) {
console.log(data + ' Inside Sibling two');
});
$rootScope.$on('siblingAndParent', function(event, data) {
console.log('broadcast from child in parent');
});
});
app.controller('ParentCtrl', function($rootScope) {
$rootScope.$on('childEmit', function(event, data) {
console.log(data + ' Inside parent controller');
});
$rootScope.$on('siblingAndParent', function(event, data) {
console.log('broadcast from child in parent');
});
});
In above code console of $emit 'childEmit' will not call inside child siblings and It will call inside only parent, where $broadcast get called inside siblings and parent as well.This is the place where performance come into a action.$emit is preferrable, if you are using child to parent communication because it skips some dirty checks.
2. If Second controller is child, you can use Child Parent communication
Its one of the best method, If you want to do child parent communication where child wants to communicate with immediate parent then it would not need any kind $broadcast or $emit but if you want to do communication from parent to child then you have to use either service or $broadcast
For example HTML:-
<div ng-controller="ParentCtrl">
<div ng-controller="ChildCtrl">
</div>
</div>
Angularjs
app.controller('ParentCtrl', function($scope) {
$scope.value='Its parent';
});
app.controller('ChildCtrl', function($scope) {
console.log($scope.value);
});
Whenever you are using child to parent communication, Angularjs will search for a variable inside child, If it is not present inside then it will choose to see the values inside parent controller.
3.Use Services
AngularJS supports the concepts of "Seperation of Concerns" using services architecture. Services are javascript functions and are responsible to do a specific tasks only.This makes them an individual entity which is maintainable and testable.Services used to inject using Dependency Injection mecahnism of Angularjs.
Angularjs code:
app.service('communicate',function(){
this.communicateValue='Hello';
});
app.controller('ParentCtrl',function(communicate){//Dependency Injection
console.log(communicate.communicateValue+" Parent World");
});
app.controller('ChildCtrl',function(communicate){//Dependency Injection
console.log(communicate.communicateValue+" Child World");
});
It will give output Hello Child World and Hello Parent World . According to Angular docs of services Singletons – Each component dependent on a service gets a reference to the single instance generated by the service factory.
4.Kind of hack - with the help of angular.element()
This method gets scope() from the element by its Id / unique class.angular.element() method returns element and scope() gives $scope variable of another variable using $scope variable of one controller inside another is not a good practice.
HTML:-
<div id='parent' ng-controller='ParentCtrl'>{{varParent}}
<span ng-click='getValueFromChild()'>Click to get ValueFormChild</span>
<div id='child' ng-controller='childCtrl'>{{varChild}}
<span ng-click='getValueFromParent()'>Click to get ValueFormParent </span>
</div>
</div>
Angularjs:-
app.controller('ParentCtrl',function($scope){
$scope.varParent="Hello Parent";
$scope.getValueFromChild=function(){
var childScope=angular.element('#child').scope();
console.log(childScope.varChild);
}
});
app.controller('CarentCtrl',function($scope){
$scope.varChild="Hello Child";
$scope.getValueFromParent=function(){
var parentScope=angular.element('#parent').scope();
console.log(parentScope.varParent);
}
});
In above code controllers are showing their own value on Html and when you will click on text you will get values in console accordingly.If you click on parent controllers span, browser will console value of child and viceversa.
5.inject '$controller'
You can inject '$controller' service in your parent controller(MessageCtrl) and then instantiate/inject the child controller(DateCtrl) using:
$scope.childController = $controller('childController', { $scope: $scope.$new() });
Now you can access data from your child controller by calling its methods as it is a service.
Let me know if any issue.
The view's scope is determine by it's controller. However, angular allows nested view scope inheritance, meaning that a view nested inside another view has access to the parent view's scope.. for example:
<div ng-controller="OuterCtrl">
<div ng-controller="InnerCtrl">
...
</div>
<div>
the inner div would have access to the InnerCtrl's scope as well as the OuterCtrl's scope, but the outer div would only have access to the OuterCtrl's scope.
If you need data shared between non-related controllers (those that are not related by nested views), then the data should be provided by a service that can be injected into the controllers like:
app.factory('service', function() {
...
return {
data: someData
};
});
app.controller('ctrl1', [... function(..., service) {
$scope.data = service.data;
}]);
app.controller('ctrl2', [... function(..., service) {
$scope.data = service.data;
}]);
use $broadcase or $emit event like:
app.controller('ctrl1', ['$scope', '$rootScope', function($scope, $rootScope) {
$rootScope.$on("CustomEvent", function($event, data){
alert(data);
})
}]);
app.controller('ctrl2', ['$scope', function($scope) {
$scope.$emit("CustomEvent", "SecondControllerValue");
}]);
Looking to implement folder hierarchy in Angular:
I'm implementing this via custom directive that references itself inside its template.
Currently it's going into infinite loop with this setup:
<!-- index.html -->
<subfolders folder="default_folder"></subfolders>
This is the <subfolders> directive:
//subfoldersDirective.js
angular.module('app').directive('subfolders', subfolders);
function subfolders() {
var directive = {
restrict: 'AE',
scope: {
folder: '=',
},
templateUrl: '/pathto/subfoldersDirective.html',
controller: DirCtrl,
controllerAs: 'vm'
};
return directive;
function DirCtrl($scope) {
var vm = this;
vm.folder = $scope.folder;
}
}
and its template:
<!-- folderDirective.html -->
<div ng-repeat="folder in vm.folder.subfolders">
{{ folder.name }}
<button ng-click="folder.is_open = true">Open folder</button>
<div ng-if="folder.is_open">
<!-- This is the problem line -->
<subfolders folder="folder"></subfolders>
</div>
</div>
In the template, <subfolders> should only get rendered after the button is clicked which triggers ng-if="folder.is_open". I guess Angular does not know this when it compiles the directive. It goes into infinite loop, even though it technically should not.
Is there a way to make it work with the directive? The logic is a bit more complex in the real app, this is why I'm looking to make it work with the directive.
I'm currently using Angular 1.2.26.
You can do this, but you need to override the compile behavior of the directive. You need to remove contents of the directive element during the compilation step, and then compile and reattach it during the post-link step. I have used the compile function below with great success:
function compile(element) {
var contents = element.contents().remove();
var contentsLinker;
return function (scope, iElement) {
if (angular.isUndefined(contentsLinker)) {
contentsLinker = $compile(contents);
}
contentsLinker(scope, function (clonedElement) {
iElement.append(clonedElement);
});
};
}
I based this function on this post, which is probably more comprehensive if you need a pre-link function.