Maybe I am designing this incorrectly ( new to angular ). Currently I have something like this:
[TVController] depends on [TVService]
[TVService] depends on [GameShowChannel] #could possibly have more channels
In code its something like so:
html
<div class='tv' ng-controller='TVController' ng-include='currentChannel()> </div>
TVController.js
var TVController = function($scope,TVService){
$scope.currentChannel = function(){ TVService.currentChannel(); }
}
TVService.js
angular.module('app.service').factory('TVService',function(GameShowChannel){
return { currentChannel: function(){ GameShowChannel.getView(); }
});
GameShowChannelService.js
angular.module('app.serivce').factory('GameShowChannel',function(){
return { getView: function(){ './partial/game_show_intro.html'} };
});
In GameShowChannelService.js I want to be able to update the scope in game_show_intro.html. I have a GameShowIntroController object in a file, but I am not able to import that into the GameShowChannelService.js. Is there a better solution to this problem? Again the problem is that I want to be able to update the view in the GameShowChannelService. I want the GameShowChannelService to be more than just a static html, but be dynamic so that I can actually 'play' something in that channel. Hope its clear enough.
Hmm..Maybe GameShowChannel should be a directive instead?
Another thought would be to broadcast event from service that some state has changed and the GameShowIntroController which controls the game_show_intr.html view, would listen and update to that state accordingly. Would this be a angular approach?
Related
I have got 2 components, let's say, Component A is a list view and Component B is a details view. Each row from the list view is clickable and will redirect to Component B upon clicking.
Component B allows editing and saving the details. I have added a Back button to Component B to allow me to go back to the list view.
But the problem I am having is that I can't see the updated list view and have to manually refresh the browser, and then I can see the updated list there.
I have tried directly using window.location and it works but really I don't prefer this approach.
public back() {
window.location.assign('/listview');
}
I wonder if there's any better way to solve this problem?
Update:
public onSelected(model: MyModel) {
const detailsViewUrl = `/detailsview/${model.id}`;
this._router.navigateByUrl(detailsViewUrl );
}
You can just emit an #Output EventEmitter with a method on Parent that looks in the event for a change with a variable stored in the component like this:
#Output someOutput: EventEmitter = new Event Emitter<any>;
HTML:
<b-component (someOutput)=getOutput($event)></b-component>
AComponent:
getOut(event){
let output = event;
if(this.something != output){
this.ngOnDestroy(); // or something that you can use to make it
}
That should work as intended.
It sounds like this is an issue with Angular's change detection when changing the contents of an array. See here:
Angular 2: How to detect changes in an array? (#input property)
The solutions in this questions should work but an easy way I have used in the past to force changes in an array to be recognised by Angular is to reassign the array after making the changes:
myArray = [...myArray];
use following routing fuction on back button click
public back() {
this._router.navigateByUrl('/listview')
}
or
public back() {
this._router.navigate('/listview')
}
Try this,
Just called the list view again internally and hit db at same time so updated values will be displayed in the list view.
calling the route by using below:
this.router.navigate(['/listview']);
Seems like a change detection issue, there are some ways to manually trigger change detection like so:
Inject ChangeDetectorRef.
Call it when you go back like so:
public back() {
ChangeDetectorRef.detectChanges()
}
Refer to this: Triggering change detection manually in Angular
I'm new to meteor.js. Still getting used to it.
I get how templates update reactively according to the cursor updates on the server, like this:
{{#if waitingforsomething.length}} Something Happened! {{/if}}
This is good to display elements on the page, updating lists and content. Now, my question is: what if I want to call some javascript or fire some event when something gets updated reactively? What would be the right way to do it with meteor.js?
Anything inside Tracker.autorun or template instance this.autorun runs with changes in reactive data sources inside these autoruns.
Reactive data sources are ReactiveVar instances, db queries, Session variables, etc.
Template.myTemplate.onCreated(function() {
// Let's define some reactive data source
this.reactive = new ReactiveVar(0);
// And put it inside this.autorun
this.autorun(() => console.log(this.reactive.get()));
});
Template.myTemplate.events({
// Now whenever you click we assign new value
// to our reactive var and this fires
// our console.log
'click'(event, template) {
let inc = template.reactive.get() + 1;
template.reactive.set(inc);
}
});
It is a little bit outdated, but Sacha Greif's Reactivity Basics is a very quick and concise introduction to meteor's reactivity model.
Basically, you have what's called reactive computations, code that observes special data objects (sessions, subscriptions, cursors, etc.) and gets executed whenever any of these reactive sources changes.
This is exposed via the Tracker API
Computation works pretty well for me:
Template.myTemplate.onRendered(function() {
this.computation = Deps.autorun(function () {
if (something) {
$(".reactive").html("Something Happened!");
}
});
});
Template.myTemplate.destroyed = function(){
if (this.computation){
this.computation.stop()
}
};
I Hope this helps.
I'm not sure if i have completely wrapped my head around this idea - but I'll try my best to clearly describe what I am trying to do here.
I have a factory that changes and parses a URL for me, so I can pass params into a controller for use (that were stored in the url). This is sort of so I can save a state for the user and they can share it via copy'ing of a URL (send it to their friends or bookmark it or w/e).
I am trying to set up a factory (or service) that listens for locationChangeSuccess - so that if the user mofies the url and presses enter, it will refresh the scopes in the controllers. So here is what I have:
.factory("urlFactory", function($location, freshUrl, StateString){
//request to change new url
requestObj.requestState = function(moduleName, stateName, startVar){
}
//request item from url, via your module and state name
requestObj.parseState = function(moduleName, stateName){
}
I dropped the center out (if it is needed im happy to link), but those just get and set the url for me.
So in the controllers I do something like
$scope.mod2m3 = urlFactory.parseState("module2", "mod3");
$scope.mod2m4 = urlFactory.parseState("module2", "mod4");
So when they land on the page, they pull their state. This works great. However, now i'm trying to solve some edge case scenarios where maybe the user modifies the url.
So I can latch onto that even pretty easily with
.factory("urlWatcher", function($location, $scope){
var urlWatcher = {};
$scope.$on('$locationChangeSuccess', function(event) {
console.log("Asdsa");
});
return urlWatcher
});
However, where I am struggling is trying to determine a way where when this fires, it would connect the new value to the scope in the controller. It was suggested to me that a callback of some sort in the parse (set) function, but I am struggling with how to approach that. It would be super cool if I could set a way for this factory/service to re send the new value when it changes to the right place. Callback sounds good, however I don't know how to config this correct.
The easiest route would be to just do an
$scope.$on('$locationChangeSuccess', function(event) {
console.log("Asdsa");
});
In each controller and manually bind to each scope, but I am trying to make this as modular as possible (and thats also a ton of watchers on the locationchangesuccess). would be fantastic if I could figuire out a clean way to set the service/factory to listen once, and on change find the right module/controller and change the value.
I can't seem to think a clear route, so I would be very greatful for any insight to this issue. Thank you very much for reading!
If what you want is a publish/subscribe architecture, where publications are global and subscriptions have the same lifecycles as Angular scopes... then Angular events are what you're looking for. There's no point setting up an ad hoc communication system with callbacks and whatnut, that would just be partially reinventing events.
However, if you want to make the semantics more obvious / add flexibility, you can listen once to $locationChangeSuccess in a service and broadcast a custom event.
$rootScope.$on("$locationChangeSuccess", function (event) {
$rootScope.$broadcast('myCustomeEvent', {message: "Guys, time to refresh!"});
});
Then listen to this event in each of the scopes where it is relevant.
$scope.$on('myCustomeEvent', function (event) {
console.log("Asdsa");
});
If setting up the listening gets repetitive, by all means, factor it out in a function, which you can for example put in a service:
myApp.factory('someFactory', [function () {
return {
listenToLogAsdsa: function (scope) {
scope.$on('myCustomeEvent', function (event) {
console.log("Asdsa");
});
}
};
}]);
Then all you have to write in your controller is:
someFactory.listenToLogAsdsa($scope);
You can assign a variable in the scope to an object in the factory, that way it's bound to a reference instead of a value. Then, in your HTML you bind the reference to the DOM. urlFactory.parseState() should then save the result to said object, and return the key where it was saved.
For example:
In urlFactory:
requestObj.parseState = function(moduleName, stateName){
var key = moduleName+stateName;
this.urlContainer[key] = "www.example.com";
return key;
}
In the controller:
$scope.urls = urlFactory.urlContainer;
$scope.mod2m3 = urlFactory.parseState("module2", "mod3");
In your HTML:
{{urls[mod2m3]}}
This way, "urls" is bound to a reference, which angular watches for changes, and whenever you change urls[mod2m3], it will affect the DOM.
You can also just react to changes in the scope variables by watching them:
$scope.$watch('urls', function() {
//do something
});
NOTE: Since this is an object, you might need to use $watchCollection instead of $watch.
is there a way to subscribe in meteor when an event triggers or a template is rendered? Im trying to get a message popup and subscribe to all usernames when this happens.
Tried:
Template.newMessage.rendered = function(){
Meteor.subscribe("allUsernames");
}
And:
Template.layout.events({
"click #new-message": function(e, t){
$("#styledModal").modal();
Meteor.subscribe("allUsernames");
}
});
Neither work though, any way to do this or do I have to use a different route? Im using iron router
Im trying to understand why you would wanna do subscribe is such a late state of the process?
I would recommand that u subscribe in the waitOn property in IronRouter.
If you use the waitOn property to subscribe to all users, within that route, you can just display them in the popup =)
1 of the 7 Meteor principles is:
Latency Compensation. On the client, use prefetching and model simulation to make it look like you have a zero-latency connection to the database.
source:
http://docs.meteor.com/#sevenprinciples
Hope this helps,
Alex
You could try to do it using Deps and Session (although your code should work too, maybe you have a problem with publications?).
In your main template rendered function put this:
Template.layout.rendered = function() {
Session.set('getAllUsers',false);
Deps.autorun(function () {
if(Session.get('getAllUsers' == true)
Meteor.subscribe('allUsernames');
})
}
Then in the template that opens the user list:
Template.newMessage.rendered = function(){
Session.set('getAllUsers',true); //this should trigger Deps.autorun and subscribe.
}
Looking at the Virtual DOM in React.js and by doing a few performance tests, I'm very interested in this library. It seems like the perfect add-on to Backbone's awesome model, router and collection structure.
However, due to the lack of quality tutorials and courses out there, I'm left with a few questions I hope someone here will be able to answer:
HTML templates
Does React completely do away with the notion of HTML templates? I'm talking about having your view markup in a separate HTML file (Or on the same page in a <script type=text/template> tag). You know, like you do with underscore.js Handlebars etc ...
The Starter kit examples all seem to have the JSX or React.DOM functions right inside your view classes, which seems a little messy to me, and I can see this getting a little out of hand, as your views grow in complexity.
Here's an example that renders 2 values and the sum of them, using a basic Twitter Bootstrap panel element with a nested table.
var CustomView = React.createClass({
render: function() {
var x = this.props.x;
var y = this.props.y;
return (
<div className="panel panel-primary">
<div className="panel-heading">
<h1 className="panel-title">Should I put all this markup somewhere else?</h1>
</div>
<div className="panel-body">
<table className="table">
<thead>
<tr>
<th>X</th>
<th>Y</th>
<th>Combined val</th>
</tr>
</thead>
<tbody>
<tr>
<td>{x}</td>
<td>{y}</td>
<td>{x + y}</td>
</tr>
</tbody>
</table>
</div>
</div>
);
}
});
I'm not interested in knowing whether it's possible or not to move this stuff to a separate file, rather, I'm trying to understand what's considered the best practise when working with React.
Updating and setting data
The Why React page states the following:
Simply express how your app should look at any given point in time, and React will automatically manage all UI updates when your underlying data changes.
I'm not fully understanding how this works. For instance, take the React component from before <CustomView x="20" y="10">. Initially I would render it like so:
var x = 20;
var y = 10;
React.renderComponent(
<CustomView x={x} y={y} />,
document.getElementById('view-container')
);
Now, when I want to update CustomView any time x changes, how should I proceed? React is supposed to be an alternative to the data binding you find in Angular and Ember, without doing a 2-way binding, so how do I make this happen? How do I tell CustomView to keep an eye on x and automatically re-render when it changes?
Naturally, just assigning x a new value does nothing.
I know there's the setState method, but I still manually have to call that, right? So if I was working with a React view and a Backbone model, the code could look something like this:
// Data model: Backbone.js
var model = new Backbone.Model({text: "Please help! :)"});
// Create view class
var View = React.CreateClass({
render: function() {
return (
<p>{this.props.text}</p>
);
}
});
// Instantiate new view
var view = React.renderComponent(
<View text={model.get("text")}>,
document.getElementById('view-container')
);
// Update view
model.on("change:text", function(model, newValue) {
view.setState({
text: newValue
});
});
// Change data
model.set("text", "I do not understand this ...");
That seems like a really strange setup, and I'm almost sure this can't be the way you're supposed to do it.
I would love some pointers to help me move in the right direction here.
Thank you in advance for any feedback and help.
Does React completely do away with the notion of HTML templates?
Yes, in favor of declaring your views with JavaScript. It also allows the Virtual DOM structure to work efficiently.
The Starter kit examples all seem to have the JSX or React.DOM functions right inside your view classes, which seems a little messy to me, and I can see this getting a little out of hand, as your views grow in complexity.
You shouldn't allow your view to grow in complexity. Make big components from small components, and you won't have an issue. If you feel it's getting complex, you can always reorganize it.
I know there's the setState method, but I still manually have to call that, right? So if I was working with a React view and a Backbone model [...]
You should search for "react backbone", and you'll find some blog posts and code examples. They're often used together. Feel free to add any links you found helpful here.
You're on the right path, however there are two things to fix. One is a bug, the other is a preferred pattern.
The bug: In the View, you are using this.props.text (good!), but you are using setState in the model listener. This sets the this.state.text value, which you are not using, so it won't work. setState should 'only' be used from inside the component itself - for all intents and purposes, think of it as a protected method. Instead, there is the setProps function, which is intended to be used only from outside the component.
The preferred pattern: The usage of setProps will soon be deprecated, as it causes a number of subtle issues. The better thing to do is just re-render the whole component each time. The right code in your case is:
// Data model: Backbone.js
var model = new Backbone.Model({text: "Please help! :)"});
// Create view class
var View = React.CreateClass({
render: function() {
return (
<p>{this.props.text}</p>
);
}
});
function rerender() {
React.renderComponent(
<View text={model.get("text")}>,
document.getElementById('view-container')
);
}
// Update view
model.on("change:text", function(model, newValue) {
rerender();
});
rerender();
Thank you for the replies guys,
So, am I correct in assuming that if I want the views to observe the data models, what I end up with is actually pretty close to Backbone view code, where you hook up event listeners in the intialize method? Here's a quick example that works:
var model = new Backbone.Model({
text: "Hey there :)"
});
var TextBox = React.createClass({
getInitialState: function() {
return this.props.model.toJSON();
},
componentWillMount: function() {
this.props.model.on("change:text", function(model, newText) {
this.setState({
text: newText
});
}, this);
},
render: function() {
return (
<p>{this.state.text}</p>
);
}
});
React.renderComponent(
<TextBox model={model} />,
document.getElementById('view-holder')
);
As I said this does work as intended. The view re-renders whenever the model's text property changes. Would this be considered "Good" React code, or should I hook this up differently?