I'm using SignalR with an Angular2 app, where we want the SignalR client methods to call into the Angular app with data received from the server, and then have Angular redo the data-bindings. For example, within the Angular app I expose a global variable for our store, which has a collection on it.
E.g.
(TypeScript)
....
export class Store{
Customers : Customer[];
constructor(){
window["GlobalStore"] = this;
}
setCustomers (customers : Customer[]){
this.Customers = customers;
}
}
....
and in my client SignalR javascript I have a function:
$.connection.MyHub.client.receive = function(data){
//Call into the Angular app and set data, which is then rendered in views
//via data-binding
//data contains a json array of customers
window.GlobalStore.setCustomers(data);
}
This seems to work and set the data on the store, however, when the data is reset Angular does not seem to detect changes, and hence the UI is not refreshed.
It's not an issue with data-typing, as even passing a simple string/integer etc through to the store correctly sets the store property when I debug, however, the Angular framework doesn't seem to then trigger change detection and refresh the views.
Any ideas on how to either:
A) Manually trigger the angular databinding so it refreshes the view?
B) Call methods within the Angular 2 app from external using a different means?
Thanks
To manually run change detection:
Use ApplicationRef::tick() method.
Use NgZone::run() method to wrap you code which should be executed inside angular zone.
You can get them by using dependency injection or by bootstrapping your application using platform().application(bindings).bootstrap(Component):
import { platform } from 'angular2/angular2';
const app = platform().application([] /* - bindings */); // you can use `app.tick()`
const zone = app.zone; // you can use `zone.run`
app.bootstrap(Component);
Related
I'm creating a single page application (SPA) that uses Vue Router, therefore it is comprised mostly of Vue components with one blade component that puts it all together using
<router-view></router-view>
I want to know how I can pass data computed within my controller and pass it to a Vue component. The current way I'm doing it is by exposing extra API endpoints, for example in my controller I have:
public function countUsers()
{
$userCount = DB::table('users')->count();
return $userCount;
}
Then in api.php:
Route::get('usercount', 'UserMController#countUsers');
this way I can get the data within my Vue component using axios.get call to usercount.
Is there a better way of doing this? The data seems to take 1-2 seconds to display on the page and I can't imagine having this implementation for over 20 computations I need to do.
I've seen another method where you attach the data into the JavaScript context using the blade template, but I'm not sure how to get that to work for a SPA with Vue Routers.
get userCount in your controller and pass it to normal blade file. You can pass the variable in vue like below.
<router-view userCount="{{userCount}}"></router-view>
then for accessing userCount variable in vue, you can load this variable from props.
export default {
props: ['userCount'],
name: 'router-view',
},
mounted:function(){
let a = this;
this.userCount = JSON.parse(this.userCount)
}
for more information you should read the documentation first. It will help you understand thoroughly.
https://v2.vuejs.org/v2/guide/components-props.html
Thanks to an ongoing integration, I'm currently trying to get 2 JS applications running on the same page (one in Angular 2, other in React) to communicate with each other. Currently, all the data sharing happens between the 2 using an object in the window scope of the page (not ideal, but straightforward) and since data is non-critical, it'll make do. This is what that looks like
<script>
window.sharedStuff = {oscar_winner: "LALALAND", news: "FAKE"}
</script>
// The angular 2 Application
<my-app>
</my-app>
// React App
<div class="react-app">
</div>
However, I'm having issue setting up callbacks from the Angular application which the React application can listen to and perform some action upon. Basically, when a specific event occurs in an Angular component, I want to inform the React application that it has occured. How can I do this? I have access to the window scope from both Angular and react, so ideally, I want to use the window.sharedStuff itself to define a callback which the react application can bind to. But I can't find any documentation on how to go about this (Perhaps because this is a unique/weird situation).
How would I go about this? Help much appreciated!
For simple cases like yours you can always create mediator or some implementation of Observable pattern. For example quick and simplest one could looks like this:
window.observer = {
listeners: {},
subscribe (event, callback) {
this.listeners[event] = this.listeners[event] || []
this.listeners[event].push(callback)
},
broadcast (event, data) {
if (Array.isArray(this.listeners[event])) {
this.listeners[event].forEach(callback => callback(data))
}
}
}
And it could be used by Angular 2 app to push data with broadcast method. React app on other hand could subscribe on the same window.observer and set it's state:
window.observer.subscribe('name.selected', name => {
this.setState({ name })
})
Check the simple demo with Angular 2 and React apps talking like good friends: http://plnkr.co/edit/NT1eGQUvoKSAnH9ZwgeB?p=preview
Why not add an event listener to the window object and have your React app fire the event which is picked up by Angular and vice versa?
window.addEventListener('click', function () { alert('hello world') });
In sails.js, can a service use data or functions generated by a hook or by sails.config.bootstrap?
Or it's only the other way around?
EDIT: I was trying to add a hook to set up my rate limiter's parameters before sails lift, and then use this rate limiter from within the application.
The bootstrap file in config/bootstrap.js is intended mainly for setting up data, jobs, etc. that are specific to your app and may need to run only once. It's run after all of your models, services and hooks have already loaded, so it can rely on them.
You can use a hook method inside of a service method--you just can't use it to set up a service. So, this is okay:
// config/services/GoodService.js
module.exports = {
someMethod: function() {
var rateLimit = sails.hooks.someHook.getRateLimit();
}
};
This is not okay:
// config/services/BadService.js
var rateLimit = sails.hooks.someHook.getRateLimit();
module.exports = {
someMethod: function() {...do something with rateLimit...}
}
I am wondering if there is a convention within AngularJS for creating an object that lives within the app module, but is not attached directly to the view in any way, but is called when the view has loaded and the app starts up. In particular, I am trying to write an object that dispatches messages to listening controllers when they come in from the server.
Currently, I have implemented this by creating a "Controller" that attaches to the view. It has a monitor() function that is called when the page loads, and then listens in a loop for any incoming messages. I call the monitor() function from within the loaded view, by setting the ng-controller like so:
<div ng-controller="MyController">
{{ monitor() }}
</div>
This doesn't feel like the right thing to do. This "Controller" isn't interacting with the view in any way, so my gut tells me I am violating principles of AngularJS. But I haven't been able to turn up an easy solution that is endorsed by the AngularJS doc.
I am looking for a way to create an object that lives within the AngularJS world (in other words, it can use dependency injection to get access to services, and it can use $scope.$broadcast to send messages to other listening controllers), but that doesn't need to attach itself to the view in any way.
Ideally, I am looking for a way to say, "Here Angular, on startup, create this object, and run this method on it." Is there a way to do this?
You may use this as a starting point:
declaration of your object.
AngularJS: Service vs provider vs factory
myApp.factory('MessageBus', function() {
return {
listeners: [],
init: function() {
// do whatever you need at startup
},
pushMessage: function(msg) {
angular.forEach(this.listeners, function(listener) {
listener(msg);
});
},
subscribe: function(onMessageCallback) {
this.listeners.push(onMessageCallback);
}
};
});
calling a method on angular appilcation start
https://docs.angularjs.org/api/ng/type/angular.Module#run
myApp.run(function(MessageBus) {
MessageBus.init();
});
using this object within controllers
https://docs.angularjs.org/guide/di
myApp.controller('MessageCtrl', function($scope, MessageBus) {
$scope.messagesToShow = [];
MessageBus.subscribe(function(message) {
$scope.messagesToShow.push(message);
});
$scope.submitMessage = function(id, text) {
MessageBus.pushMessage({
type: 'TEXTMESSAGE',
id: id,
payload: text
});
};
});
Note that this is something to start with and nothing for any production code. For example the controller doesn't unsubscribe after being destroyed - if the page changes - and so you leak memory.
Don't use $broadcast-events for this
1: they are slow
2: if this MessageBus has a specific concern, than in should be an own object with a meaningfull name and api. Otherwise your $rootScope will be flooded with thousends of different events for different concerns when your application grows. A service is always easier to document and you have a clean dependency on that specific service. Only using events on the $rootScope hides this dependency from every developer reading and hopefully understanding your codebase,
Yeah you approach is really smelly. This function will be called every time a $apply/$digest invokes.
Maybe move the function into the run callback on the module.
var app = angular.module("YourApp", [//dependencies]);
app.run(function($YourUIService){
$YourUIService.monitor();
});
The run will be invoked, when your angularjs-module has loaded every dependency and is ready to run.
Didn't find the doc for this :/
service implementation
myService = function()
{
this.config = {
show0: false,
show1: true,
role : -1,
id : -1;
};
};
in controller, I map the config values
$scope.config = myService.config; //I guess this by reference, isnt it???
in templates of these controllers for e.g. the $scope.config.show0 is used with for e.g. ng-model
Now outside angular in my threejs code
I get the service using injector which I have defined earlier and change some values depending on certain conditions
var service = window.my.injector.get('myService');
service.config.id = 1991;
Now this value is not immediately reflected in the HTMl template,
Source = {{config.id}} still renders as Source = -1
But when I click on some other button in the same template which is mapped to any other value in the same scope
Source = {{config.id}} still renders as 1991
How should I force this rerendering or refreshing in my non angular code soon after
var service = window.my.injector.get('myService');
service.config.id = 1991;
///do something to refresh that controller
Am I using the service wrong? How should I make this config available in angular controllers, templates and non angular code if not via a service?
Shouldnt changing the $scope.config properties values and changing the values outside angular by retrieving the service via injector change the values everywhere ?
This is because the angular digest cycle does not kick in from your threejs code. I am not sure where your three js code is, but try using $scope.$apply to kick in the digest cycle, and it should work fine.
If you can share a jsFiddle, I can have a better understanding on what you are trying to achieve, but the reason why this is not happening is, as I said, that the digest cycle does not kick in.