There's a nice project for using Angular with WinJS controls together here: https://github.com/winjs/angular-winjs. I already have this working nicely in my app. However it doesn't really cover the use of angular for other parts of WinJS.
I am devloping on a windows 10 phone, and I have code like this:
var app = WinJS.Application;
var activation = Windows.ApplicationModel.Activation;
app.onactivated = function (args) {
How can I modify my app.onactivated assigment to do things the angular way so I can use $providers and modify $scope variables and so on?
By $providers do you mean the services that angular offers(like $http,$location and so on)?
If so, you can put onactivated method inside the controller callback Definition function(see below codes) so that it can use the angular services.
JS:
(function () {
"use strict";
var app = WinJS.Application;
var activation = Windows.ApplicationModel.Activation;
var myApp = angular.module("myApp", ["winjs"]);
var myController = myApp.controller("myController", function ($scope) {
app.onactivated = function (args) {
if (args.detail.kind === activation.ActivationKind.launch) {
if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
$scope.$apply(function () {
$scope.test = "The Scope.test has been changed!";
});
}
args.setPromise(WinJS.UI.processAll());
}
};
});
app.start();
})(angular);
HTML:
<body class="win-type-body" ng-app="myApp">
<div ng-controller="myController">
<input type="text" ng-model="test"/>
{{test}}
</div>
If not so, can you tell me what $providers are?
Related
Explaining the problem:
So in the current app we have a couple of constant configuration declarations that connects the app to either the production or development environment, and we comment one out whenever we want to switch which doesn't seem like the ideal scenario to me. So what I was after is having a configuration external json file that contains the values and have that file separately from the changing code and get values from there into my constant.
The actual question:
In this piece of code:
application.constant('servicesConfig', (function() {
var con = 'appdev';
//var con = 'appprod';
return {
host: con+'.appdomain.com'
}
}
As you can see I have to modify the 'con' variable manually in order to switch between the dev and prod environments, instead, I want to do the following:
application.constant('servicesConfig', (function() {
var deferred = $q.defer();
var configLocation = 'config/server.json';
var configurations = $http.get(configLocation)
return {
host: configurations.con+'.appdomain.com'
}
}
My question is how can I get the $http or other angular services injected?
You can manually bootstrap angular after receive data from server.
Example on jsfiddle:
var app = angular.module("myApp", []);
app.controller("myController", function($scope, servicesConfig) {
console.log(servicesConfig);
$scope.model = servicesConfig.host;
$scope.reset = function() {
$scope.model = '';
};
});
angular.element(document).ready(function() {
//Simulate AJAX call to server
setTimeout(function() {
app.constant('servicesConfig', {
host: "appdev"
});
angular.bootstrap(document, ['myApp']);
}, 2000);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-cloak>
<div ng-controller="myController">
<input type="text" ng-model="model" /> {{model}}
<button ng-click="reset()">Reset</button>
</div>
</div>
I have a project where a lot of the models are going to be managed by almost the same controller-code with the only exception that they are calling different services.
The way I'm handling this now is by instantiating a Crud-Controller with common code into every custom controller and then redirecting the service-call by changing the variable inside the custom controller. I.e.
vm.service.get() inside the Crud-Controller changes by setting vm.service = teamService; in a custom controller.
This is how I instantiate the Crud-Controller into my custom controllers at the moment:
$injector.invoke(Crud, this, {$scope:$scope});
This works fine, however I don't know if this is the right way to share common controller code. Maybe it is possible to instantiate a service for this use? Because the question I have (if my way of doing it is correct), how do I access other controllers while using IIFE? Right now I am not using IIFE since I have not figured out a way to do so.
I have tried with angular.module('app').controller('Crud') but it does not work.
I.e: How do I get access to the PrimaryCtrl's edit function without using $injector or relying on the $scope inheritance?
http://jsfiddle.net/tcVhN/62/
It looks like your example uses Angular 1.0.x that supports global controllers out of the box. That's how it would be done with more recent Angular, without going too deep into the perils of JS inheritance.
'use strict';
(function (root, angular) {
root.ctrls = root.ctrls || {};
var primaryCtrl = function ($scope) {
var self = this;
console.log('primaryCtrl constructor', self, $scope);
};
primaryCtrl.prototype = {
items: ['Item 1', 'Item 2'],
edit: function (item) {
//do stuff
}
};
primaryCtrl.$inject = ['$scope'];
root.ctrls.primaryCtrl = primaryCtrl;
})(this, angular);
(function (root, angular) {
root.ctrls = root.ctrls || {};
var secondaryCtrl = function ($scope) {
var self = this;
console.log('secondaryCtrl constructor', self, $scope);
};
secondaryCtrl.prototype = angular.extend({},
root.ctrls.primaryCtrl.prototype,
{
items: ['Stuff 1', 'Stuff 2']
}
);
secondaryCtrl.$inject = ['$scope'];
root.ctrls.secondaryCtrl = secondaryCtrl;
})(this, angular);
var app = angular.module('app',[]);
app.controller('PrimaryCtrl', ctrls.primaryCtrl);
app.controller('SecondaryCtrl', ctrls.secondaryCtrl);
and
<div ng-controller="PrimaryCtrl as primary">
<p ng-repeat="item in primary.items">{{item}}</p>
</div>
<div ng-controller="SecondaryCtrl as secondary">
<p ng-repeat="item in secondary.items">{{item}}</p>
</div>
You may also check Angular Classy, which brings opinionated but viable extending syntax to Angular.
So let's say I have a controler that dppends on a news service. Whenever news is published, the controller would like to diisplay the latest news. I'd rather not use $broadcast and $on, because this does weird things to the way components are coupled. Components which don't depend on news could still listen for these events.
So, here's what I'd like to be able to do:
myApp.controller("myController", ["news", function(news){
news.onPublish.addListener(function(story){
... Show the latest juicy story.
});
}]);
news.onPublish would be an Event instance, with Event defined as follows:
Event = function(){
var listeners = [];
this.addListener = function(l){
listeners.push(l);
}
this.trigger = function(){
var params = arguments
listeners.map(function(l){
l.apply(undefined, params);
});
}
return this
}
Is this a good way to implement communication between services and other components? Also, would it be good to call $rootScope.$apply at the end of Event.trigger so that any changes the listeners made will be picked up?
The best is to write common data storing factories that do the job for you. Here's a working example: http://jsfiddle.net/9L5xL8sx/ which shows how this works. The NewsService factory can be used across several Angular modules, and within the same module, as shown in my example.
Here's the JS:
var app = angular.module("TestSharing", []);
app.factory("NewsService", [function() {
var articles = [];
var makeNews = function (text) {
articles.push({text: text});
};
var getNews = function() {
return articles;
};
return {
get: getNews,
make: makeNews
};
}]);
app.controller("FirstCtrl", ["$scope", "NewsService", function($scope, NewsService) {
$scope.articles = function () {
return NewsService.get();
};
}]);
app.controller("SecondCtrl", ["$scope", "NewsService", function($scope, NewsService) {
$scope.articletext = "";
$scope.submit = function () {
NewsService.make($scope.articletext);
};
}])
The HTML:
<div ng-app="TestSharing">
<div ng-controller="FirstCtrl">
<span ng-repeat="article in articles()">{{article.text}}<br/></span>
</div>
<div ng-controller="SecondCtrl">
<input type="text" ng-model="articletext"/>
<button ng-click="submit()">Make some news</button>
</div>
</div>
Also, services like these can also share events. For example, if you'd used the factory to expose an object called somethingNew, which merely contained if something new had happened in one of the Controllers it had been shared in, you could achieve the same effect. The idea would be to only listen for changes where you'd want (using something like $scope.$watch(NewsService.somethingNew, function (now, then) {…})) and that would be just as easy.
I have seen a few exmaples on stack overflow about this ng-init issue, although I cant seem to find one which references it with the use of a controller.
I have called the function in the controller by having the following in the html file
<div class="tab-container" ng-controller = "ExampleController" ng-init = "init()" >
In the controller:
$scope.init = function(){
alert("do something");
};
It does run, but it runs before the components have loaded on the screen.
Am i missing something?
Thanks
ng-init is supposed to work like this, because it's used to initialize data.
A very simple example:
<ul ng-init="list = [1,2,3,4]">
<li ng-repeat="l in list"></li>
</ul>
If you are trying to run something while your controller loads, it's actually much simpler than you thought:
app.controller('mainCtrl', function ($scope) {
var init = function ($scope) {
// do whatever you need to do to initialize your controller
$scope.someData = ["Hey", "I'm", "Alive"]
$scope.otherData = localStorage.getItem('myBackup')
}
init()
})
Or even simpler, if you don't need the function (no closures or whatever)
app.controller('mainCtrl', function ($scope) {
// do whatever you need to do to initialize your controller
$scope.someData = ["Hey", "I'm", "Alive"]
$scope.otherData = localStorage.getItem('myBackup')
})
Edit - assuming you're using ngView:
To have the code run on when the page is fully loaded you should set a watcher on the event $viewContentLoaded, like this:
$scope.$on('$viewContentLoaded', function(){
//Here your view content is fully loaded !!
});
app.controller('mainCtrl', function ($scope) {
// This event is triggered when the view has finished loading
$scope.$on('$viewContentLoaded', function() {
$scope.someData = ["Hey", "I'm", "Alive"]
$scope.otherData = localStorage.getItem('myBackup')
})
})
another option is using jquery. It would fit if you depend on many elements. But make sure to load jquery with a version of your choice to project.
loading jquery (insert version where it's ...):
<script src="https://code.jquery.com/jquery-..."></script>
the js code:
$(document).ready(function() {
alert("do something");
});
I've created an angular app that has the following structure.
Application configuration, routes, directives, controllers and filters are all defined in index.js (I know this is not recommended). All of my general functions are in a controller called main.js, this is also the controller I am using in my main view in index.html. From then on the app consists of 10 different views, each has it's own controller.
main.js has become very difficult to maintain, so I would like to separate it into five external "utility" style files that contain the general function the application uses. These functions all use angular's $scope and must be able to be accessed by all the views and controllers that exist in the application.
For the past few days I've tried several different methods, such as defining the functions under angular's factory service, using angular's $provide method, defining a controller without a view and many others. None of them worked for me. What is the simplest way to separate the functions that exist in main.js to external js files without changing any code within the functions themselves. Let's pretend that the function cannot be turned into a directive.
Example -
Function that checks users name for 'guest' string and returns an image
main.js -
$scope.defaultpic = function(username) {
var guest = username;
if (guest.indexOf("guest") != -1){
{return {"background-image": "url('data:image/png;base64,chars"}}
}
}
in the view
<img ng-style="defaultpic(JSON.Value)" class="user_pic" ng-src="getprofilepic/{{JSON.Value}}"/>
Cheers,
Gidon
In order to use the function in markup, you still have to bind it to the scope. But, you can move the body of the function to a service:
angular.module('myapp').factory('picService',[ function () {
return {
defaultpic: function(username) {
var guest = username;
if (guest.indexOf("guest") != -1){
{return {"background-image": "url('data:image/png;base64,chars"}}
}
}
};
}]);
And then bind it up in the controller:
$scope.defaultpic = picService.defaultpic;
Refactor controller functions as services declared in different files
As you correctly stated, a great approach to refactor the functions is to put them into different services.
According to the angular Service docs:
Angular services are singletons objects or functions that carry out specific tasks common to web apps.
Here is an example:
Original code
Here we have a simple Hello World app, with a controller that has two functions: greet() and getName().
app.js
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.getName = function () {
return 'World';
}
$scope.greet = function (name) {
return 'Hello ' + name;
};
});
index.html
...
<div id="container" ng-controller="MainCtrl">
<h1>{{greet(getName())}}</h1>
</div>
...
We want to test that our scope always has both functions, so we know it is working as intended, so we are going to write two simple jasmine tests:
appSpec.js
describe('Testing a Hello World controller', function() {
var $scope = null;
var ctrl = null;
//you need to indicate your module in a test
beforeEach(module('plunker'));
beforeEach(inject(function($rootScope, $controller) {
$scope = $rootScope.$new();
ctrl = $controller('MainCtrl', {
$scope: $scope
});
}));
it('should say hallo to the World', function() {
expect($scope.getName()).toEqual('World');
});
it('shuld greet the correct person', function () {
expect($scope.greet('Jon Snow')).toEqual('Hello Jon Snow');
})
});
Check it out in plnkr
Step 1: Refactor controller functions into separate functions
In order to start decoupling our controller to our functions we are going to make two individual functions inside app.js.
app.js
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.getName = getName;
$scope.greet = greet;
});
function getName() {
return 'World';
}
function greet(name) {
return 'Hello ' + name;
}
Now we check our test output and we see that everything is working perfectly.
Check out the plnkr for step 1
Step 2: Move functions to their own services
We will define a NameService and GreetService, put our functions in them and then define the services as dependencies in our controller.
app.js
var app = angular.module('plunker', []);
app.service('NameService', function () {
this.getName = function getName() {
return 'World';
};
});
app.service('GreetService', function() {
this.greet = function greet(name) {
return 'Hello ' + name;
}
});
app.controller('MainCtrl', ['$scope', 'NameService', 'GreetService', function($scope, NameService, GreetService) {
$scope.getName = NameService.getName;
$scope.greet = GreetService.greet;
}]);
We make sure that our tests are still green, so we can move on to the final step.
Have a look at step 2 in plunker
Final Step: Put our services in different files
Finally we will make two files, NameService.js and GreetService.js and put our services in them.
NameService.js
angular.module('plunker').service('NameService', function () {
this.getName = function getName() {
return 'World';
};
});
GreetService.js
angular.module('plunker').service('GreetService', function() {
this.greet = function greet(name) {
return 'Hello ' + name;
}
});
We also need to make sure to add the new scripts to our index.html
index.html
...
<script src="NameService.js"></script>
<script src="GreetService.js"></script>
...
This is how our controller looks like now, neat huh?
app.js
var app = angular.module('plunker', []);
app.controller('MainCtrl', ['$scope', 'NameService', 'GreetService', function($scope, NameService, GreetService) {
$scope.getName = NameService.getName;
$scope.greet = GreetService.greet;
}]);
Plunker for the final step.
And that's it! Our tests still pass, so we know everything works like a charm.