How to get the values of the subscribed data in angular2 - angularjs

I wrote a injectable service in which i want to return "display" in my data and i done it sucessfully as follows,
export class GetAllList {
str = localStorage.getItem('social');
loc = JSON.parse(this.str);
id = this.loc._id;
private _productUrl = 'http://localhost:3000/getprofiledetails/'+this.id;
constructor(private _http: Http) { }
getList(): Observable<IDetails[]> {
return this._http.get(this._productUrl)
.map((response: Response) => {
return response.json().data.display;
});
}
}
Here i am subscribing to it,
this._profileservice.getList()
.subscribe(
details1 => this.details1 = details1);
console.log("displaystas:"+this.details)
The problem is,my console is displaying undefined?so how can i see my display value in my console?Can anyone suggest me help.Thank you.

You are printing wrong variable (details instead of details1) and you are missing {}:
this._profileservice.getList()
.subscribe(
details1 => {
this.details1 = details1;
console.log("displaystas: " + this.details1)
}

Related

Filter a broadcasted property in Angular 9

I have a dbService that calls the database!
//DB service code -----------------------
private changedEvents = new BehaviorSubject<IEvent[]>(null);
broadCastEvents = this.changedEvents.asObservable();
getEvents() {
this.http.get<IEvent[]>(this.configUrl + 'Event/GetEvents').subscribe(
(data: IEvent[]) => {
this.changedEvents.next(data)
});
}
In my component on ngOnInit I starts listening to this
ngOnInit(): void {
this.dbService.broadCastEvents.subscribe(data => {
this.events = data;
})
// this.dbService.getEvents();
}
Now all of this working like a charm! But now I'm only interested in records where this.events.type == 2
I tried by a standard filtering like below!
ngOnInit(): void {
this.dbService.broadCastEvents.subscribe(data => {
this.events = data.filter(event => event.eventTypeRefId == 2);
})
// this.dbService.getEvents();
}
But it results in the following Error!? Any ideas how to this in a better way (that works :-))
core.js:6241 ERROR TypeError: Cannot read property 'filter' of null
at SafeSubscriber._next (start-training.component.ts:26)
at SafeSubscriber.__tryOrUnsub (Subscriber.js:183)
at SafeSubscriber.next (Subscriber.js:122)
at Subscriber._next (Subscriber.js:72)
at Subscriber.next (Subscriber.js:49)
at BehaviorSubject._subscribe (BehaviorSubject.js:14)
at BehaviorSubject._trySubscribe (Observable.js:42)
at BehaviorSubject._trySubscribe (Subject.js:81)
at BehaviorSubject.subscribe (Observable.js:28)
at Observable._subscribe (Observable.js:76)
ngOnInit(): void {
this.dbService.broadCastEvents.pipe(filter(event => event.eventTypeRefId == 2)).subscribe(data => {
this.events = data
})
// this.dbService.getEvents();
}
Reference:
https://rxjs-dev.firebaseapp.com/guide/operators
There are multiple ways for it. One way is to use array filter like you did. Other way would be to use RxJS filter pipe as shown by #enno.void. However both these methods might still throw an error when the notification is null. And since the default value of your BehaviorSubject is null, there is high probability of hitting the error again.
One workaround for it is to use ReplaySubject with buffer 1 instead. It's similar to BehaviorSubject in that it holds/buffer the last emitted value and emits it immediately upon subscription, except without the need for a default value. So the need for initial null value is mitigated.
Try the following
private changedEvents = new ReplaySubject<IEvent[]>(1);
broadCastEvents = this.changedEvents.asObservable();
...
Now the error might still occur if you were to push null or undefined to the observable. So in the filter condition you could check for the truthiness of the value as well.
ngOnInit(): void {
this.dbService.broadCastEvents.subscribe(data => {
this.events = data.filter(event => {
if (event) {
return event.eventTypeRefId == 2;
}
return false;
});
});
}

ERROR TypeError: Cannot read property 'details' of undefined

I have a plain text on local server that i want to display on my web page using angular. I have my model, service and component.
So I am getting an error at this line >> this.paragraphs = this.announcement.details.split('#');
I tried using ? operator (this.paragraphs = this.announcement?.details.split('#')) but it could not build.
MODEL
export class Announcements
{
public id: number;
public details: string;
public date: Date;
}
SERVICE
getAnnouncementById(id)
{
return this.http.get<Announcements>('http://localhost:49674/api/Announcements/' + id)
.pipe(catchError(this.errorHandler));
}
COMPONENT-.ts
import { Announcements } from '../models/Announcement';
export class ReadMoreComponent implements OnInit
{
public announcementId;
public announcement : Announcements
public paragraphs = [];
constructor(
private route: ActivatedRoute,
private router:Router,
private announcementservice: AnnouncementsService
){}
ngOnInit()
{
this.route.paramMap.subscribe((params:ParamMap) => {
let id = parseInt(params.get('id'));
this.announcementId = id;
this.getAnnouncenentById(id)
//split
this.paragraphs = this.announcement.details.split('#');
})
}
getAnnouncenentById(id){
this.announcementservice.getAnnouncementById(id)
.subscribe(data => this.announcement = data);
}
COMPONENT-.html
<div class="article column full">
<div *ngFor=" let paragraph of paragraphs">
<p>{{paragraph.details}}</p>
</div>
</div>
this.paragraphs = this.announcement.details.split('#'); is called before this.announcement = data so this.annoucement is undefined in that moment.
To be sure that both values already comes form observables you can use combineLatest function or switchMap operator.
Adding ? operator is workaround. Your observable still can call with unexpected order.
e.g.:
this.route.paramMap.pipe(switchMap((params: ParamMap) => {
let id = parseInt(params.get('id'));
this.announcementId = id;
this.getAnnouncenentById(id);
return this.announcementservice.getAnnouncementById(id)
})).subscribe((data) => {
this.announcement = data
this.paragraphs = this.announcement.details.split('#');
});
In that code subscription will start after first observable emits value.

Angular: What's the correct way to return Observable?

I have the following method which isn't working correct:
getProducts(): Observable<Product[]> {
let PRODUCTS: Product[];
this.http.get(this.base_url + "api/products")
.subscribe(
(data) => {
for(var i in data) {
PRODUCTS.push(new Product(data[i].id, data[i].name, data[i].category, data[i].description, data[i].price, data[i].amount));
}
},
(error) => {
console.log(error);
});
return of(PRODUCTS);
}
The error I'm getting is this:
TypeError: Cannot read property 'push' of undefined
Now, I know that the PRODUCT array is not accessable from within the subscribe function, but I cannot get the correct solution for it.
Can anyone help me with that. I want to return an Observable<Product[]>.
Thank you in advance!
Edit: Updated to account for the fact that the API seems to return an array-like object rather than a true array.
You want to use map:
getProducts(): Observable<Product[]> {
return this.http.get(this.base_url + "api/products")
.map(data => {
let products = [];
for (let i in data) {
products.push(new Product(data[i].id, data[i].name, data[i].category, data[i].description, data[i].price, data[i].amount));
}
return products;
})
.do(null, console.log);
}
Since #pixelbit's comment keeps getting upvotes despite being wrong, here's an example showing why it is wrong:
// Fakes a HTTP call which takes half a second to return
const api$ = Rx.Observable.of([1, 2, 3]).delay(500);
function getProducts() {
let products = [];
api$.subscribe(data => {
for (let i in data) {
products.push(data[i]);
}
});
return Rx.Observable.of(products);
}
// Logs '[]' instead of '[1, 2, 3]'
getProducts().subscribe(console.log);

ngShow expression is evaluated too early

I have a angular component and controller that look like this:
export class MyController{
static $inject = [MyService.serviceId];
public elements: Array<string>;
public errorReceived : boolean;
private elementsService: MyService;
constructor(private $elementsService: MyService) {
this.errorReceived = false;
this.elementsService= $elementsService;
}
public $onInit = () => {
this.elements = this.getElements();
console.log("tiles: " + this.elements);
}
private getElements(): Array<string> {
let result: Array<string> = [];
this.elementsService.getElements().then((response) => {
result = response.data;
console.log(result);
}).catch(() => {
this.errorReceived = true;
});
console.log(result);
return result;
}
}
export class MyComponent implements ng.IComponentOptions {
static componentId = 'myId';
controller = MyController;
controllerAs = 'vm';
templateUrl = $partial => $partial.getPath('site.html');
}
MyService implementation looks like this:
export class MyService {
static serviceId = 'myService';
private http: ng.IHttpService;
constructor(private $http: ng.IHttpService) {
this.http = $http;
}
public getElements(): ng.IPromise<{}> {
return this.http.get('./rest/elements');
}
}
The problem that I face is that the array elements contains an empty array after the call of onInit(). However, later, I see that data was received since the success function in getELements() is called and the elements are written to the console.
elements I used in my template to decide whether a specific element should be shown:
<div>
<elements ng-show="vm.elements.indexOf('A') != -1"></elements>
</div>
The problem now is that vm.elements first contains an empty array, and only later, the array is filled with the actual value. But then this expression in the template has already been evaluated. How can I change that?
Your current implementation doesn't make sense. You need to understand how promises and asynchronous constructs work in this language in order to achieve your goal. Fortunately this isn't too hard.
The problem with your current implementation is that your init method immediately returns an empty array. It doesn't return the result of the service call so the property in your controller is simply bound again to an empty array which is not what you want.
Consider the following instead:
export class MyController {
elements: string[] = [];
$onInit = () => {
this.getElements()
.then(elements => {
this.elements = elements;
});
};
getElements() {
return this.elementsService
.getElements()
.then(response => response.data)
.catch(() => {
this.errorReceived = true;
});
}
}
You can make this more readable by leveraging async/await
export class MyController {
elements: string[] = [];
$onInit = async () => {
this.elements = await this.getElements();
};
async getElements() {
try {
const {data} = await this.elementsService.getElements();
return data;
}
catch {
this.errorReceived = true;
}
}
}
Notice how the above enables the use of standard try/catch syntax. This is one of the many advantages of async/await.
One more thing worth noting is that your data services should unwrap the response, the data property, and return that so that your controller is not concerned with the semantics of the HTTP service.

server-sent events with angularjs (and Play Framework) keeps restarting after submit

i am trying to build an SSE using Angular.js single-page-app, and Play Framework as the backend. it seems like every time i submit a registration using angular's $http, the EventSource reconnects.
because i am trying to make the UI flow smooth, on connection i add the push a NoOpUpdate over SSE, which sends the entire registered list, that the client takes and renders. on updates (registering a new user, etc) SSE will send the entire list but also the change (the added registrant, etc), and the client can tell by the event type what (and if) it needs to do (add the new registrant to the rendered list). but, for some reason, i keep getting the NoOpUpdate after submit, instead of the update event.
code below:
SSE connection (in the angular controller - coffeescript notation):
$scope.sseOpen = () ->
alert("SSE connection opened")
return
$scope.sseMessage = (message) ->
jsonMessage = JSON.parse(message.data)
if (jsonMessage.type.indexOf("NoOpUpdate")==0
$scope.$apply () ->
list = $scope.list = jsonMessage.list
else if jsonMessage.type = "Added"
newOne = jsonMessage.affected
$scope.$apply () ->
$scope.list.push(newOne)
return
$scope.listen = () ->
$scope.serverListener = new EventSource('<address>')
$scope.serverListener.addEventListener('message', $scope.sseMessage, false)
$scope.serverListener.addEventListener('open', $scope.sseOpen, false);
return
$scope.listen()
calling the save method from the controller:
$scope.saveSuccessCallback = () ->
alert("save succeeded")
$scope.saveFailureCallback = () ->
alert("save failed")
$scope.save = ->
myData = {
...
}
AjaxFactory.save('<address>', myData, $scope.saveSuccessCallback, $scope.saveFailureCallback)
return
factory method for $HTTP:
save : (myURL, myData, successCallback, failureCallback)->
$http({
url: myURL,
method: "POST",
data: myData
})
.success (data, status, headers, config) ->
successCallback
.error (data, status, headers, config) ->
failureCallback
server side code:
def getSSE = Action { implicit request =>
Async {
Receiver.join map { join =>
Ok.feed(join._2 &> EventSource()).as("text/event-stream")
}
}
}
Receiver:
class Receiver extends Actor {
val (enumerator, channel) = Concurrent.broadcast[JsValue]
def receive = {
case Connect => {
sender ! Connected(enumerator)
}
case Save(newOne) => {
MyDao.save(newOne) map { saved =>
sender ! ActionSuccess("New one Created", Some(saved))
self ! notifyAll("Added", Some(saved)
}
}
case NoOpUpdate(eventType, affectedOne) => {
notifyAll("NoOpUpdate: " + eventType, affectedOne)
}
}
def notifyAll(eventType: String, affectedOne: Option[Person]) {
MyDao.findAll map { people =>
val msg = JsObject(
Seq(
"type" -> JsString(eventType),
"affectedOne" -> Json.toJson(affectedOne),
"list" -> Json.toJson(people)
)
)
channel.push(msg)
}
}
}
object Receiver {
val default = Akka.system.actorOf(Props[Receiver])
implicit val timeout = Timeout(1 second)
def update(eventType: String, affectedOne: Option[Person] = None) {
default ! NoOpUpdate(eventType, affectedOne)
}
def join: Future[(Iteratee[JsValue,_],Enumerator[JsValue])] = {
default ? Connect map {
case Connected(enumerator) => {
val iteratee = Iteratee.foreach[JsValue]{ js =>
Json.stringify(js \ "type") match {
case "save" => {
Json.fromJson[Person](js \ "person").asOpt map { jsOpt =>
default ? Save(jsOpt)
}
}
case _ => {
}
}
}
(iteratee, enumerator)
}
}
}
MyDAO:
def save(person: Person): Future[Person] = {
collection.save(person).map {
case ok if ok.ok =>
person
case error => throw new RuntimeException(error.message)
}
}
def findAll: Future[Seq[Guardian]] = {
collection.find(Json.obj())
.sort(Json.obj("_id" -> -1))
.cursor[Guardian]
.toList
}
lots of code and a long question. any idea?
Not 100% sure why it's closing, but looking at this:
$scope.serverListener.addEventListener('message', $scope.sseMessage, false)
You're subscribing to messages of type message (similar with open). But you're not sending a type with any of the messages you're sending (setting a property called type in the JSON is not sending a type). To send a type, you need to implement a play.api.libs.EventSource.EventNameExtractor that will extract the name out of your event. Typically I do something like this:
https://github.com/typesafehub/play-mongo-knockout/blob/master/app/models/Event.scala
Then I make my enumerator an enumerator of these event types.
Also, I hope you know you never use the iteratee that you create in the join method. Perhaps you're also using that for a WebSocket, if that's the case, that's fine.
Anyway, to debug why it's closing... what does the network tab in the developer console in your browser say?

Resources