route push in custom saga does not cause re-render - admin-on-rest

i have a custom saga, which responds to a simple search user action, if it finds a single user, it should show it otherwise go to the user list.
Problem is that when i push the new route, i can see that state changes but the app is not rendering the new route. I looked through the source and cant find any update-blockers.
If i do the push action twice, for some reason, only causing the location key to change again, the app re-renders correctly. Don't know where to keep looking, so any ideas of what the problem is?
import { put, takeEvery, all, call } from "redux-saga/effects";
import { searchUser } from "../services/userService";
import { SEARCH_USER } from "../actions/searchUser";
import { showNotification } from "admin-on-rest";
import { push, replace } from "react-router-redux";
function* handleSearchUser(action) {
try {
const { searchParam } = action.payload;
const filter = {};
if (isNaN(searchParam)) {
filter.Email = searchParam;
} else {
filter.UserId = parseInt(searchParam);
}
const result = yield call(searchUser, 0, 25, filter);
if (result && result.length === 1) {
// Duplicate this row and the page re-renders the correct page
yield put(push(`/User/${result[0].UserId}/show`));
} else {
yield put(
push(
`/User?filter=${JSON.stringify(
filter
)}&order=DESC&sort=id&page=1&perPage=25`
)
);
}
} catch (error) {
console.error(error);
yield put(showNotification("Error: error when searching", "warning"));
}
}
export default function* searchUserSaga() {
yield all([takeEvery(SEARCH_USER, handleSearchUser)]);
}

Was using gatsbyjs. I switched to Create React App for building and all problems went away.

Related

RxJs channing, setting and reading external values

I'm new in rxjs world and I have to rewrite some code. So, I draft my ideas.
I have a request, which could fail and return an observable. I simulate that with the ob-variable and two map operations. Then, I try to catch an error. I need the result in my local variable selected and raise an event on isChanged. I call my function now via subscription. I don't need a result.
My question: Is one big pipe enough and can I use following approach for the work with my local variables?
import { of, map, Observable, tap, Subject, throwError, EMPTY } from 'rxjs';
import { catchError } from 'rxjs/operators';
let selected = 0;
const isChanged = new Subject<number>();
function myfunc(): Observable<boolean> {
const ob = of(1,3,4,5,7);
return ob.pipe(
// simulates a http request
map(v => v*2),
// simulates a rare error condition
map(v => {
// if (v === 8) { throw `four`; }
if (v === 10) { throw `ten`; }
return v;
}),
// play with different failure situations
catchError((e) => {
if (e === `four`) {
return of(4);
}
if (e === `ten`) {
return EMPTY;
}
console.warn(e);
return throwError(e);
}
),
// I need the result in a local variable
// I need a information about success
// I need the result not really
map((res) => {
selected = res;
isChanged.next(res);
return true;
})
);
}
console.log(`a: selected is ${selected}`);
isChanged.subscribe(v =>
console.log(`b: isChanged received: ${v}, selected is ${selected}`));
console.log(`c: selected is ${selected}`);
// I have to call the function
myfunc().subscribe((b) => {
console.log(`d: selected is ${selected}`);
});
I create the world in Stackblitz too:
https://stackblitz.com/edit/rxjs-6fgggh?devtoolsheight=66&file=index.ts
I see results like expected. But I'm not sure if all ideas are the right way to solve all problems.
Thanks for you thought.

How do I initialise a NativeScript app fully programmatically (without XML)?

Here's what I have so far. The background goes green (the colour of the Page), but I'd expect a purple ContentView with some text inside to fill the page, too.
Is there anything further I'm missing?
import { on, run, launchEvent } from "tns-core-modules/application";
import { Frame } from "tns-core-modules/ui/frame/frame";
import { ContentView } from "tns-core-modules/ui/content-view/content-view";
import { TextBase } from "tns-core-modules/ui/text-base/text-base";
import { Page } from "tns-core-modules/ui/page/page";
on(launchEvent, (data) => {
const frame = new Frame();
const page = new Page();
page.backgroundColor = "green";
const contentView = new ContentView();
const textBase = new TextBase();
contentView.height = 100;
contentView.width = 100;
contentView.backgroundColor = "purple";
textBase.text = "Hello, world!";
contentView._addView(textBase);
page.bindingContext = contentView;
frame.navigate({ create: () => page });
data.root = page; // Incidentally, should this be the frame or the page?
});
run();
You are almost on track, you just need slight modification on your code.
import { on, run, launchEvent } from 'tns-core-modules/application';
import { Frame } from 'tns-core-modules/ui/frame/frame';
import { ContentView } from 'tns-core-modules/ui/content-view/content-view';
import { TextField } from 'tns-core-modules/ui/text-field';
import { Page } from 'tns-core-modules/ui/page/page';
run({
create: () => {
const frame = new Frame();
frame.navigate({
create: () => {
const page = new Page();
page.backgroundColor = "green";
const contentView = new ContentView();
const textField = new TextField();
contentView.height = 100;
contentView.width = 100;
contentView.backgroundColor = "purple";
textField.text = "Hello, world!";
contentView.content = textField;
page.content = contentView;
return page;
}
});
return frame;
}
});
You don't have to wait for launch event, you could set the root frame in run method itself.
In your code, you were creating the frame but never adding it to root UI element or mark the frame itself as root element
It's recommended to use .content to add child for a ContentView / Page as they are originally designed to hold one child element only.
Use TextField / TextView for input text, TextBase is just a base class.
It seems to me that you try to overcomplicate. You can replace XML with code just by implementing createPage method - Create a page via code.
I just modified default NS + TypeScript Playground template to operate without XML - NS + TypeScript template without XML.
I think you can't leave run as empty as it is expecting an entry to start the app. From {NS} website,
You can use this file to perform app-level initializations, but the
primary purpose of the file is to pass control to the app's root
module. To do this, you need to call the application.run() method and
pass a NavigationEntry with the desired moduleName as the path to the
root module relative to your /app folder.
if you look for run code in "tns-core-modules/application"
function run(entry) {
createRootFrame.value = false;
start(entry);
}
exports.run = run;
and
function start(entry) {
if (started) {
throw new Error("Application is already started.");
}
started = true;
mainEntry = typeof entry === "string" ? { moduleName: entry } : entry;
if (!androidApp.nativeApp) {
var nativeApp = getNativeApplication();
androidApp.init(nativeApp);
}
}

In application-settings, iterate the key values

In Nativescript application-settings module, is there a way to iterate through all the key values?
There is no appropriate way to get all keys from the application-settings and to iterate through them. You will still need to save somewhere the keys- for example in array, to be able to get the values. For your convenience I am attaching example, however the given code is not the best practise.
Example:
import { EventData } from 'data/observable';
import { Page } from 'ui/page';
import { HelloWorldModel } from './main-view-model';
import {setString, hasKey, getString, setNumber, getNumber} from "application-settings"
// Event handler for Page "navigatingTo" event attached in main-page.xml
export function navigatingTo(args: EventData) {
// Get the event sender
let page = <Page>args.object;
var arrayKeys=[{value:"string1", type:"string"}, {value:"string2", type:"string"}, {value:"string3", type:"string"}, {value:"number1", type:"number"}];
setString("string1", "stringValue1");
setString("string2", "stringValue2");
setString("string3", "stringValue3");
setNumber("number1", 1);
console.log("--------Get Values----------")
for(var i=0; i<arrayKeys.length;i++){
if(hasKey(arrayKeys[i].value)){
switch (arrayKeys[i].type) {
case "number":
console.log("Number "+getNumber(arrayKeys[i].value))
break;
case "string":
console.log("String "+getString(arrayKeys[i].value))
break;
default:
break;
}
}
}
}

Trouble w/ Meteor Sorting

I'm trying to add a simple drop down control above a list such that I can sort it by "created" or "title".
The list template is called posts_list.html. In it's helper .js file I have:
posts: function () {
var sortCriteria = Session.get("sortCriteria") || {};
return Posts.find({},{sort: {sortCriteria: 1}});
}
Then, I have abstracted the list into another template. From here I have the following click event tracker in the helper.js
"click": function () {
// console.log(document.activeElement.id);
Session.set("sortCriteria", document.activeElement.id);
// Router.go('history');
Router.render('profile');
}
Here I can confirm that the right Sort criteria is written to the session. However, I can't make the page refresh. The collection on the visible page never re-sorts.
Frustrating. Any thoughts?
Thanks!
You can't use variables as keys in an object literal. Give this a try:
posts: function() {
var sortCriteria = Session.get('sortCriteria');
var options = {};
if (sortCriteria) {
options.sort = {};
options.sort[sortCriteria] = 1;
}
return Posts.find({}, options);
}
Also see the "Variables as keys" section of common mistakes.
thanks so much for that. Note I've left commented out code below to show what I pulled out. If I required a truly dynamic option, versus the simply binary below, I would have stuck w/ the "var options" approach. What I ended up going with was:
Template.postList.helpers({
posts: function () {
//var options = {};
if (Session.get("post-list-sort")) {
/*options.sort = {};
if (Session.get("post-list-sort") == "Asc") {
options.sort['created'] = 1;
} else {
options.sort['created'] = -1;
}*/
//return hunts.find({}, options);}
console.log(Session.get("hunt-list-sort"));
if (Session.get("hunt-list-sort") == "Asc") {
return Hunts.find({}, {sort: {title: 1}});
}
else {
return Hunts.find({}, {sort: {title: -1}});
};
}
}
});

IE & Ajax / XPath

I've read countless threads and tried implementing many different suggestions, but haven't had any luck.
first:
function ajaxRequest() {
try {
var request = new XMLHttpRequest();
}
catch(e1) {
try {
var request = new ActiveXObject("Msxml2.HTMLHTTP");
}
catch(e2) {
try {
var request = new ActiveXObject("Microsoft.XMLHTTP");
}
catch(e3) {
var request = false;
}
}
}
return request;
}
It looks like IE is successfully using XMLHttpRequest. As far as I can tell, it's loading the XML fine, but Xpath is another story:
function XMLPath(doc, path) {
try {
return doc.evaluate(path, doc, null, XPathResult.STRING_TYPE, null).stringValue;
} catch (e) {
try {
doc.setProperty("SelectionLanguage", "XPath");
return doc.selectNodes(path);
}
catch(e2) {
alert(e2);
}
}
}
Basically, what must I change in my catch statement to make it work with IE? What's also interesting is that it never alerts an e2 error, meaning it's not actually throwing an error. Totally confused.
Thanks.
Try return doc.selectSingleNode(path).text; for IE, that is the closest you can get for accessing the string value of the node found by your path.

Resources