I need to run a script when a Component is fully loaded. Is there a way to do that, like ajaxStart() and .ajaxStop() on jQuery?
I am trying to add my code on ngAfterViewInit(); but sometimes the component is not fully loaded when that function is called.
Thank you so much :)
My code is as follows:
export class AppComponent {
constructor(private elementRef: ElementRef) { };
ngAfterViewInit() {
var s = document.createElement("script");
s.type = "text/javascript";
s.src = "./assets/js/my-script-to-run.js";
this.elementRef.nativeElement.appendChild(s);
}
}
Related
How do you add components such as charts in google trends in reactjs?
componentDidUpdate() {
const that = this
const keyword = "your keyword"
conts script = document.createElement("script")
script.src = "https://ssl.gstatic.com/trends_nrtr/760_RC08/embed_loader.js"
script.async = true
ReactDOM.findDOMNode(this.refs.trendsWrapper1).appendChild(script)
script.onload = function () {
trends.embed.renderExploreWidgetTo(ReactDOM.findDOMNode(that.refs.trendsWrapper1), "TIMESERIES", {"comparisonItem":[{"keyword":keyword,"geo":"","time":"today 5-y"}],"category":0,"property":""}, {"exploreQuery":"q=%2Fm%2F0rfgxy2","guestPath":"https://www.google.co.uk:443/trends/embed/"})
}
}
I tried this and didn't work. Please try to suggest a much easier method than this as well.
You need to load the embed_loader script out side of react (like any other 3rd lib code)
At the component, run the trends.embed command.
var Trends = React.createClass({
render: function() {
trends.embed.renderWidget("US_cu_mqCQGFwBAABWOM_en",
"fe_line_chart_309e703b-7d68-4baa-8dc9-c12e2f9defc3",
{"guestPath":"https://trends.google.com:443/trends/embed/"});
return (
<div>must return something :/</div>
)}
});
Here is an example
I am new in react-native coding but have experienced on objective-c and swift coding and want use singleton pattern in react-native.
I have tried to find out the solution from other StackOverflow answer but most of them are creating only singleton functions as below code:
var Singleton = (function () {
var instance;
function createInstance() {
var object = new Object("I am the instance");
return object;
}
return {
getInstance: function () {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
function run() {
var instance1 = Singleton.getInstance();
var instance2 = Singleton.getInstance();
alert("Same instance? " + (instance1 === instance2));
}
As we can see in above code here we are creating singleton function not class.
Please let me know if any way to create singleton class and pass multiple variables in that class as objective-c or swift.
Note: Please also notify me if I am going in the wrong direction.
Here's my implementation for singleton class...
Controller.js
export default class Controller {
static instance = Controller.instance || new Controller()
helloWorld() {
console.log("Hello World... \(^_^)/ !!")
}
}
Usage:
import Controller from 'Controller.js'
Controller.instance.helloWorld()
You can use something like that
class SingletonClass {
static instance = null;
static createInstance() {
var object = new SingletonClass();
return object;
}
static getInstance () {
if (!SingletonClass.instance) {
SingletonClass.instance = SingletonClass.createInstance();
}
return SingletonClass.instance;
}
}
var instance1 = SingletonClass.getInstance();
var instance2 = SingletonClass.getInstance();
The singleton pattern isn't used much in the JS ecosystem. What you should look into is http://mobx.js.org. MobX is a library that allows you to create observable objects to store data for your apps. You instantiate one store for each domain you please and make edits to that store to change app state.
in my Nativescript Angular app i am using an ActivityIndicator, setup as i've seen in the Nativescript Angular docs (the GroceryList example):
<ActivityIndicator width="30" height="30" [busy]="refreshing" [visibility]="refreshing ? 'visible' : 'collapsed'" horizontalAlignment="center" verticalAlignment="center"></ActivityIndicator>
if the Component using it i have:
export class MyComponent {
public refreshing = false;
........
}
Then i fetch some data from my backend:
public onRefreshTap() {
console.log("onrefreshtap");
this.refreshing = true;
this.backend.getData(function (data) { //this.backend is my Service
this.refreshing = false;
})
}
The problem is that when i put this.refreshing to true, the ActivityIndicator correctly shows. But when bakend request completes (and so, i put this.refreshing=false) the ActivityIndicator does not hides... (and also it seems that its busy property is not updated, it stays in spinning state)..
What am i doing wrong ?
Thanks in advance
You could also try to access the refreshing property as it has been shown in the sample codes below. It could be a problem of accessing the property inside the callback method of your service.
public onRefreshTap() {
var that = this;
this.refreshing = true;
this.backend.getData(function (data) { //this.backend is my Service
that.refreshing = false;
})
}
or
public onRefreshTap() {
this.refreshing = true;
this.backend.getData((data) => {
that.refreshing = false;
})
}
It may be many things:
1) The change to false, on the Observable, is not being "seen" by the component.
------ The solution is run the code in a Zone (see https://angular.io/docs/ts/latest/api/core/index/NgZone-class.html )
2) The backend is returning an error (I don't see it dealing with that in the code).
------ The solution is put a function to deal with the error.
3) The callback is not being called. In your code, you're SENDING a function as a parameter to the backendService, so maybe the service is not executing it.
------ Try using a Promisses or Observables to deal with returned values (you'll have to Google about it, since I'm still learning them my explanation would be the worst). :)
Here's some code that might work:
my-component.html
<ActivityIndicator [busy]="isWorking" [visibility]="isWorking?'visible':'collapse'"></ActivityIndicator>
my-component.ts
import { Component, NgZone } from "#angular/core";
...
export class MyComponent {
isWorking:boolean = false;
constructor(private backendService: BackendService,
private _ngZone: NgZone)
{
this.isWorking = false;
}
public onRefreshTap() {
console.log("onrefreshtap");
this.isWorking = true;
this.backendService.getData()
.then(
// data is what your BackendService returned after some seconds
(data) => {
this._ngZone.run(
() => {
this.isWorking = false;
// I use to return null when some Server Error occured, but there are smarter ways to deal with that
if (!data || data == null || typeof(data)!=='undefined') return;
// here you deal with your data
}
)
}
);
}
}
I started working with meteor which seems to be good for my use, a problem occurred where I get my documents only 9/10 times. I think I implemented something wrong.
I use Angular 1.5 and Typescript
My collection gets created in the lib folder at /
import { Mongo } from 'meteor/mongo';
export const Locations= new Mongo.Collection('locations');
then the collection gets imported to my service
import {app} from '../../js/lib/app';
import {Locations} from '../../../lib/collections';
export class LocationsService {
locations: any;
constructor(){
console.log('Constructor called');
this.locations = Locations
.find({})
.fetch();
console.log('documents loaded');
console.log(this.locations);
}
public createLocation(any:any){
Locations.insert(any);
}
public updateLocation(identity:any, modifier:any){
Locations.update(identity,modifier);
}
}
app.service('locationsService', LocationsService);
Here are the console.logs from 3 different page refreshes:
It looks like the amount of docs I get is totally random.
Here is some code that will help you. It uses the "resolve" feature of ui-router to hold up loading of the page until data is loaded. In this case there are two things being resolved:
User record
Elders record
The second one needs an "elderid" from users.profile in order to find an elder record.
function config($locationProvider, $urlRouterProvider, $stateProvider) {
'ngInject';
$stateProvider
.state('member.calendar', {
url: '/calendar',
template: "<membercalendar></membercalendar>",
resolve: {
currentUser: ($q) => {
var deferred = $q.defer();
Meteor.autorun(function () {
if (!Meteor.loggingIn()) {
if (Meteor.user() == null) {
deferred.reject('AUTH_REQUIRED');
} else {
deferred.resolve(Meteor.user());
}
}
});
return deferred.promise;
},
elder: ($q) => {
var deferred = $q.defer();
Meteor.autorun(function () {
if (!Meteor.loggingIn()) {
if (Meteor.user() == null) {
deferred.reject('AUTH_REQUIRED');
} else {
deferred.resolve(Elders.find({_id: Meteor.user().profile.elderid}));
}
}
});
return deferred.promise;
}
}
});
}
This works well if you want the data to be loaded fully before the page loads. If you don't mind an asynchronous update to the page, you can use getReactively to make a helper run once the data has resolved. I can give you example code for that too if you like.
My new Service simply subscribes
export class LocationsService {
locations:any;
constructor(){
console.log('Constructor called');
//Subscribe to a collection//localStorage.getItem('ID')
Meteor.subscribe('locations', 2223 );
this.locations = Locations;
console.log('documents loaded');
}
public createLocation(any:any){
Locations.insert(any);
}
public updateLocation(identity:any, modifier:any){
Locations.update(identity,modifier);
}
}
app.service('locationsService', LocationsService);
In my controller i simply add the fetching of my documents in the Tracker.
import {app} from '../../js/lib/app';
import {LocationsService} from './LocationsService';
import {Tracker} from 'meteor/tracker';
export class LocationsController {
static $inject = ['locationsService','$reactive','$scope'];
public $reactive: any;
public $scope: any;
public locations: any[];
constructor(private locationsService: LocationsService, $reactive:any, $scope:any){
this.locationsService = locationsService;
this.$reactive = $reactive;
this.$scope = $scope;
$reactive(this).attach(this.$scope);
Tracker.autorun(() => {
//console.log('autorun');
this.locations = locationsService.locations.find({}).fetch();
console.log(this.locations)
});
}
public createLocation(location:any){
console.log('Locations does what it should');
console.log(location);
this.locationsService.createLocation(location);
}
public updateLocation(location:any, modifier:any){
this.locationsService.updateLocation(location._id,modifier)
}
}
app.controller('locationsController', LocationsController);
The only problem I have now is that the modell updates like a charm but not the view when I create new locations. The autorun works and the new location gets saved in my collection but I see it only if I reload. But that one is low priority for me.
I would like to know how to call a Firebase array using Angular 2. In my example here, I have an array addnote in my Firebase DB. There are two separate iterations of Do the dishes, and I would like to print them out to my HTML's unordered list.
The [] in my private addsnotes throws errors, and I didn't really expect otherwise. In the absence of understanding how to output the array, I am using it to illustrate what I am trying to achieve. I have also marked the relevant area where the call is being made.
My rainbow.component.html
<div><ul>
<li *ngFor="let addsnote of addsnotes">{{addsnote}}</li>
</ul></div>
My firebase schema:
My rainbow.component.ts
export class Rainbow implements OnInit{
private addsnotes: [];
private username: string;
ngOnInit(){
var self = this;
var user = firebase.auth().currentUser;
var getUserInfo = firebase.database().ref('users/' + user.uid);
setTimeout(acquisition, 1000);
function acquisition(){
if (user){
getUserInfo.once('value', function(snapshot){
self.username = snapshot.val().username;
self.addsnotes = snapshot.val().addnote; //incorrect
});
}
}
}
}
If you want the AngularFire2 makes is easy to tune into the power of Observables so you can detect changes on the firebase end and auto-update your user notes. With AngularFire2, your solution would look more like this...
rainbow.component.html
<div>
<ul>
<li *ngFor="let addsnote of addsnotes$ | async">{{ addsnote.$value }}</li>
</ul>
</div>
rainbow.component.ts
import { Injectable } from '#angular/core';
import { AngularFire } from 'angularfire2';
import { Observable } from 'rxjs/Observable';
export class RainbowComponent implements OnInit {
private addsnotes$: Observable<string[]>;
private username$: Observable<string>;
constructor (
private af: AngularFire
) {}
ngOnInit () {
let user = firebase.auth().currentUser,
userNamePath = `users/${user.uid}/username`,
notesPath = `users/${user.uid}/addnote`;
this.username$ = this.af.database.object(userNamePath);
this.addsnotes$ = this.af.database.list(notesPath);
}
}
You will need the async pipe when using Observables in your template HTML. It will auto subscribe to extract the data. The big difference with this and the previous code is that anytime your addsnotes data changes, it will automatically show the changes on the HTML view. If you want to keep it like the previous code where you are limiting it to one call using once('value'), you can add a .take(1) to the end of this.af.database.list(notesPath) to just take the list values one time.
In addition, I would recommend adding a sub field to your notes such as order so that you can sort your list in an order that you want. You can find info on how to sort with AngularFire2 here.
Hope this helps.
If you wanted to stick with Web Firebase API (no angularfire2), to get the addsnotes to work, it might look something like this.
ngOnInit () {
let user = firebase.auth().currentUser;
// Used ES6 arrow function instead, which is built into Typescript
setTimeout(() => {
// Make sure user and user.uid are defined
if (user && user.uid) {
let userRef = firebase.database().ref(`users/${user.uid}`);
userRef.once('value', (snapshot) => {
let userInfo = snapshot.val() || {};
this.username = userInfo['username'];
this.addsnotes = [];
// Traverse each child for 'addnote'
snapshot.child('addnote').forEach((childSnapshot) => {
let addnoteKey = childSnapshot.key,
addnote = childSnapshot.val();
addnote['id'] = addnoteKey; // Saving the reference key if you want reference it later
self.addsnotes.push(addnote);
});
}
}
}, 1000);
}