Angular2 : How to display image using Promise? - image

Following is my component, where I want to display image using Promise. Following is my code, browser got unresponsive when it run.
Can any one help me to fix this issue.
HTML:
<img [src]="getImage()" />
JS: my.component.ts
// ...
public imageUrl:string;
constructor() {
this.imageUrl = "path-to-my-image/image.png";
}
getImage(){
return new Promise((resolve,reject) =>{
// this.imageUrl is not static one, it may fetch from the server
resolve(this.imageUrl);
});
}
// ...

When you use a function call as you are for the value of src, Angular's change detection mechanism will call it over and over again. In your case, that will kick off the server calls hundreds or thousands of times.
What you want is something along the lines of:
<img [src]="image | async">
ngOnInit() {
this.imageUrl = "path-to-my-image/image.png";
this.image = this.getImageUrl();
}
getImageUrl() {
return new Promise((resolve,reject) =>{
// this.imageUrl is not static one, it may fetch from the server
resolve(this.imageUrl);
});
}
However, this will work only once. In other words, if you call getImageUrl again, nothing will happen. What you probably want is to make the URLs into a stream (observable):
<img [src]="image$ | async">
ngOnInit() {
this.imageUrl = "path-to-my-image/image.png";
this.image$ = this.getImageUrl();
}
getImageUrl() {
return Observable.of(this.imageUrl);
}

Related

Sinon with complex object structure

I'm using #types/xrm and attempting to test a method call with sinon. Unfortunately I am hitting quite a few issues due to the complex nature of the return and call I need to mock. I can find really simple examples of sinon stubbing or spying on calls, but nothing more complex than that.
I have the following simple code:
export class AccountForm {
static Fields = class {
static PrimaryContact = "primarycontactid";
static Name = "name";
}
public onLoad(context: Xrm.Events.EventContext): void {
// Get the form context
const formContext = context.getFormContext();
// Get the attributes required
const primaryContact = formContext.getAttribute(AccountForm.Fields.PrimaryContact);
const name = formContext.getAttribute(AccountForm.Fields.Name);
// Add our onchange events
primaryContact.addOnChange(this.onChangePrimaryContact);
name.addOnChange(this.onChangeName);
}
public async onChangePrimaryContact(context: Xrm.Events.EventContext): Promise<void> {
alert("Do something");
}
public async onChangeName(context: Xrm.Events.EventContext): Promise<void> {
alert("Do something else");
}
}
I want to test that an onchange event has been registered to both fields. Ideally, I'd like to check it's the RIGHT onchange, but I'll settle with the fact that it's been called once.
The "easy" way has been to check that the addOnChange method was called twice, this is as below:
import {AttributeMock, XrmMockGenerator} from "xrm-mock";
import * as sinon from "sinon";
import { AccountForm } from "../../src/entities/Account/Form";
describe("Account Form Tests", () => {
describe("Check Onload", () => {
beforeEach(() => {
XrmMockGenerator.initialise();
XrmMockGenerator.Attribute.createString("name", "");
XrmMockGenerator.Attribute.createLookup("primarycontactid", []);
});
it("should register onChange functions", () => {
// Arrange
let formContext = XrmMockGenerator.getFormContext();
let context = XrmMockGenerator.getEventContext();
// Stub
const attributeStub = sinon.stub(AttributeMock.prototype, "addOnChange");
// Act
let form = new AccountForm();
form.onLoad(context);
// Assert
expect(attributeStub.calledTwice).toBeTruthy();
});
});
});
But this is not very resilient, as it is not checking WHICH attributes the onChange functions were added to, or what function was registered.
I've tried stubbing the ForContext's "GetAttribute", but looks like it's requiring me to mock the entire return object, as otherwise, the stub does not return anything? I can get around this with using spy, but still can't work out how to check the attribute that the onChange is being added to and what the function is
Am I missing something obvious here?

How to subscribe new value in Akavache?

I'm using Akavache's GetAndFetchLatest method and I have created dependency services to communicate with Akavache's method. I'm calling akavache from service layer successfully when i directly reference. For subscribing
MyMod result = null;
var cache = BlobCache.LocalMachine;
var cachedPostsPromise = cache.GetAndFetchLatest(
"mykey",
() => GetInfo(),
offset =>
{
//some condition
});
cachedPostsPromise.Subscribe(subscribedPosts => {
Device.BeginInvokeOnMainThread(() =>
{
//do sothing.
});
});
result = await cachedPostsPromise.FirstOrDefaultAsync();
return result;
It works.But how an I call subscribe on service layer with interface/dependency service?
I think you are new to reactive programming. Understanding the basic principles helps when using Akavache. Maybe this intro helps.
To answer your question, place code like this in your "repository" class:
public override IObservable<MyClass> Get(string key)
{
var cachedObservable = blobCache.GetAndFetchLatest<MyClass>(key,
() => GetFromServerAsync(key));
return cachedObservable ;
}
And in the caller:
private void getNewData()
{
var myClassObservable = myRepository.Get("the key");
myClassObservable.Subscribe(handleNewMyClass);
}
private void handleNewMyClass(MyClass newClass)
{
//handle the new class
}
Note that handleNewMyClass() is called twice:
first with the MyClass from cache
then with the MyClass that was fetched (from the server)
Using this approach you can simply place the repository class in your IoC Container.
Please find the the sample code :
var result = BlobCache.LocalMachine;
var cachedPostsPromise = cache.GetAndFetchLatest(
"mykey",
() => ViewModelLocator.GetInstance<IYourServiceName>().MethodName(),
offset =>
{
//some condition
});
cachedPostsPromise.Subscribe(subscribedPosts => {
Device.BeginInvokeOnMainThread(() =>
{
//Your piece of code
});
});
result = await cachedPostsPromise.FirstOrDefaultAsync();
return result;
Please note the there any anything inside subscribed will be called twice : first set of data will be cache and second set will be freshly fetched from server.You have to manage according.

When to use dispatch with alt.js flux

I'm new to using flux and have started using the alt.js implmentation. I'm wondering when I would use dispatch from within my actions. For example, take this code.
//ImageActions.js
class ImageActions {
getImages(id) {
return Api.get(`topics/${id}`).then(response => {
let images = response.data.filter(image => {
return !image.is_album;
});
this.updateImages(images);
});
}
updateImages(images) {
return images;
}
}
---------------------------------------------------
//ImageStore.js
class ImageStore {
constructor() {
this.images = [];
this.image = {};
this.bindListeners({
handleUpdateImages: ImageActions.UPDATE_IMAGES
});
}
handleUpdateImages(images) {
this.images = images;
}
}
Currently this works without using the dispatch() function as seen in their tutorial here http://alt.js.org/guide/async/
I'm wondering when I'd want to do this and what dispatch does and what it does differently than just returning the value from the updateImages function in ImageaActions.js
You use dispatch when your async calls resolve. In this case it works because when your sync call finishes, you are calling another action (updateImages) which is triggering the dispatch, since getImages is not triggering a dispatch. Remember the return of an async call is a Promise.

Can't get jQuery Ajax to parse JSON webservice result

I have validated the JSON response from my C# Webmethod, so I don't believe that's the problem.
Am trying to parse the result using simple jQuery $.ajax, but for whatever reason I can't get the method to correctly fire and parse the result, also incidentally can't seem to get the function to fire the result. Are their any limits on the size of the JSON object that can be returned.
I also removed this code from inside a "Site.Master" page because it would always refresh when I hit the simple button. Do tags work correctly with jQuery elements like the button input I'm grabbing from the DOM?
function ajax() {
//var myData = { qtype: "ProductName", query: "xbox" };
var myData = { "request": { qtype: "ProductName", query: "xbox"} };
$.ajax({
type: "POST",
url: "/webservice/WebService.asmx/updateProductsList",
data: {InputData:$.toJSON(myData)},
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
// var msg = {__type: "Testportal.outputData", id: "li1234", message: "it's work!", myInt:101}
alert("message=" + msg.d.ProductName + ", id=" + msg.d.Brand);
},
error: function (res, status) {
if (status === "error") {
// errorMessage can be an object with 3 string properties: ExceptionType, Message and StackTrace
var errorMessage = $.parseJSON(res.responseText);
alert(errorMessage.Message);
}
}
});
}
And the page:
<asp:Button ID="Button1" runat="server" OnClientClick="ajax();" Text="Button" />
And the Serverside Webmethod:
public class WebService : System.Web.Services.WebService
{
[WebMethod]
[ScriptMethod(UseHttpGet = true, ResponseFormat = ResponseFormat.Json)]
public OutputData updateProductsList(InputData request)
{
OutputData result = new OutputData();
var db = new App_Data.eCostDataContext();
var q = from c in db.eCosts
select c;
if (!string.IsNullOrEmpty(request.qtype) && !string.IsNullOrEmpty(request.query))
{
q = q.Like(request.qtype, request.query);
}
//q = q.Skip((page - 1) * rp).Take(rp);
result.products = q.ToList();
searchObject search = new searchObject();
foreach (App_Data.eCost product in result.products)
{
/* create new item list */
searchResult elements = new searchResult()
{
id = product.ProductID,
elements = GetPropertyList(product)
};
search.items.Add(elements);
}
return result;
}
And helper classes:
public class OutputData
{
public string id { get; set; }
public List<App_Data.eCost> products { get; set; }
}
public class InputData
{
public string qtype { get; set; }
public string query { get; set; }
}
One problem you may be having is that you aren't doing anything to prevent the button from submitting the form and executing a full postback/reload at the same time you're starting your $.ajax() callback.
I'd suggest wiring this up unobtrusively instead of using the OnClientClick property, like this:
$(document).ready(function() {
// May need to use $('<%= Button1.ClientID %>') if your Button is
// inside a naming container, such as a master page.
$('#Button1').click(function(evt) {
// This stops the form submission.
evt.preventDefault();
$.ajax({
// Your $.ajax() code here.
});
});
});
I also agree with Oleg that you should use json2.js for your JSON stringifying and parsing. In newer browsers, that will fall back to the browsers' native implementations of those methods, which is much faster and makes the parsing safer.
Update:
To answer your question about the data, no that doesn't look quite right.
What you want to ultimately send to the server is this (sans formatting):
{"request":{"gtype":"ProductName","query":"xbox"}}
To accomplish that, you want something like this:
var req = { request : { qtype: "ProductName", query: "xbox" }};
$.ajax({
data: JSON.stringify(req),
// Remaining $.ajax() parameters
});
Keep in mind that request, qtype, and query must match your server-side structure with case-sensitive accuracy.
You can also be more verbose in defining the request object (which I prefer, personally, to keep things clear and readable):
var req = { };
req.request = { };
req.request.qtype = "ProductName";
req.request.query = "xbox";
I've written a bit more about this here, if you're interested: http://encosia.com/2009/04/07/using-complex-types-to-make-calling-services-less-complex/
What does your server side code method look like? Set a break point. Is it even being hit?
It should look something like:
[WebMethod, ScriptMethod]
public string updateProductsList(string qtype, string query)
{ // stuff
}
Also, your javascript data params do not look formatted correctly.
It seems to me that your problem is that you try to use manual JSON serialization. There are more direct way. You should just declare [ScriptMethod (ResponseFormat = ResponseFormat.Json)] or [ScriptMethod (UseHttpGet = true, ResponseFormat = ResponseFormat.Json)] and return return direct an object instead of the string from the web method. On the client side (in JavaScript) I strictly recommend you to use JSON.stringify (from json2.js which can be downloaded from http://www.json.org/js.html) for the constructing of the data parameter of jQuery.ajax.
Look at Can I return JSON from an .asmx Web Service if the ContentType is not JSON? and How do I build a JSON object to send to an AJAX WebService? probably also in JQuery ajax call to httpget webmethod (c#) not working you have have an interest for more experiments.

Handling servlet output in AJAX

My problem: I am sending a request to a servlet from an AJAX function in a JSP.
The servlet processes the data and returns a ArrayList.
My question is how to handle the ArrayList inside AJAX, and display it as a table in same JSP.
The code is
function ajaxFunction ( ) {
// var url= codeid.options[codeid.selectedIndex].text;
url="mstParts?caseNo=9&cdid=QCYST0020E1";
// alert(cid);
var httpRequest;
if (window.XMLHttpRequest) {
httpRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) {
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
if (httpRequest == null){ alert('null');}
alert(url);
httpRequest.open("GET", url, true );
httpRequest.onreadystatechange = function() { alertContents(httpRequest); };
//httpRequest.setRequestHeader('Content-Type', 'text/plain');
httpRequest.send(null);
alert('t1');
}
function alertContents(httpRequest) {
if (httpRequest.readyState == 4) {
var cType =httpRequest.getResponseHeader("Content-Type");
//document.write(httpRequest.toString());
// alert(cType);
// var xmlDoc=httpRequest.responseText;
//document.write(xmlDoc.toString());
// if (xmlDoc == null) {alert('null returned');}
if (!httpRequest.status == 200) {
alert('Request error. Http code: ' + httpRequest.status);
}
else
{
var profileXML = eval(<%=request.getAttribute("data")%>);
if ( profileXML != null){ alert('null'); }//else { alert(profileXML(0)); }
// httpRequest.getAttribute("data");
}
}
}
var profileXML = eval(<%=request.getAttribute("data")%>);
Firstly, I would recommend you to learn about the wall between JavaScript and JSP. JS runs entirely at the client side and JSP/Java runs entirely at the server side. They certainly doesn't run in sync as you seem to think. To learn more, read this blog article.
function ajaxFunction ( )
Secondly, I would recommend you to use an existing, robust, thoroughly-developed, well-maintained JavaScript library with Ajaxical capabilities such as jQuery instead of reinventing the AJAX wheel and fighting/struggling/worrying with browser specific issues/troubles/behaviors/pains. I would also recommend to use JSON as data transfer format between Java Servlet at server and JavaScript at client. In the Java side you can use the great Gson library for this.
Here's a kickoff example with all of the mentioned techniques. Let's start with a Servlet and a JavaBean:
public class JsonServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Data> list = dataDAO.list();
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(new Gson().toJson(list));
}
}
public class Data {
private Long id;
private String name;
private Integer value;
// Add/generate getters/setters.
}
The JsonServlet (you may name it whatever you want, this is just a basic example) should be mapped in web.xml on a known url-pattern, let's use /json in this example. The class Data just represents one row of your HTML table (and the database table).
Now, here's how you can load a table with help of jQuery.getJSON:
$.getJSON("http://example.com/json", function(list) {
var table = $('#tableid');
$.each(list, function(index, data) {
$('<tr>').appendTo(table)
.append($('<td>').text(data.id))
.append($('<td>').text(data.name))
.append($('<td>').text(data.value));
});
});
The tableid of course denotes the idof the <table> element in question.
That should be it. After all it's fairly simple, believe me. Good luck.

Resources