ReactiveUI Routing Replace ViewModel - xamarin.forms

I would like to navigate to a new page, popping the current one.
I tried this code in a ReactiveCommand
Router.Navigate.Execute(viewModel)
.Do(_ => Router.NavigationStack.RemoveAt(Router.NavigationStack.Count() - 2));
which almost does the job, but it navigates back before pushing the new page, which looks weird.
Any idea to achieve this? Kind of replacing the last ViewModel of the Router.NavigationStack?
Thanks!

Related

How to implement an onboarding page with Prism.Forms?

I am developing a Xamarin.Forms app with Prism in which I'd like to incorporate an onboarding page (more specific a top user benefits page, following the Material Design guidelines).
The app itself is structured around a NavigationPage to which I navigate with
NavigationService.NavigateAsync("/NavigationRootPage/MainPage")
in my App(). On the first start I'd like to show the onboarding page instead and I am having quite some issues getting it right.
My first approach was to navigate to MainPage with
_navigationService.NavigateAsync("/NavigationRootPage/MainPage")
from my viewmodel, when the user clicks the Get Started! button. This kind of worked by is also kind of ugly, since this absolute navigation will destroy the oboarding page immediately and not animate the transition. Furthermore at the moment the oboarding page is destroyed, the MainPage will be built, which will take a small, but noticeable, amount of time. In effect the user will notice the MainPage being built up, which does not look smooth at all.
My second approach was to navigate in a relative fashion from my viewmodel.
_navigationService.NavigateAsync("NavigationRootPage/MainPage")
This works way smoother, with the transition animation and after the animation is done, the MainPage is already ready to go. But again there is a major drawback. You can navigate back to the onbaording page, which we dont't want neither. And - to my knowledge - there is no (clean) way to remove the page from the navigation stack, too. I tried calling PageUtility.DestroyPage on my onboarding page, but this only worsened things, since it seemed to keep the page, but destroy the viewmodel, resulting in my onboarding page being shown without data when pushing the back button.
My third approach did not work at all, although it seemed promising to me. In my App() I pushed my navigation page with the main page then then my onboarding page modal
NavigationService.NavigateAsync("/NavigationRootPage/MainPage");
NavigationService.NavigateAsync("OnboardingPage", useModalNavigation: true);
and then in my viewmodel
_navigationService.GoBackAsync(useModalNavigation: true)
but this approach
showed the header of the NavigationPage although the onbaording page was supposed to be shown as a modal
refused to GoBackAsync - when calling this method, nothing happens
waiting for the first call to NavigateAsync did not change anything either.
Some other things I've tried
Implemented INavigationOptions in my viewmodel with ClearNavigationStackOnNavigation being true
Tried setting Application.MainPage after my NavigationPage was shown, but to no avail
I would have believed that this was king of a common requirement when programming an app. Is there something I've missed? Or will I have to live with one of those drawbacks?
I think you are overthinking this. If you want to show your Onborading page first then just navigate to it first.
`NavigateAsync("NavigationPage/OnboardingPage");
Or if you want to have the MainPage in the stack, then start the app with a deeplink
`NavigateAsync("NavigationPage/MainPage/OnboardingPage");
If you don't want to show the navigation header, just hide it for the onboarding page.
Don't use an absolute navigation unless you want to completely reset the navigation stack (equivalent to MainPage = new MainPage()).
I have found a solution, which is not the most beautiful one (it is actually quite ugly) but working.
In my MainPages viewmodel I implemented INavigatingAware and navigated to my OnboardingPage modally
public async void OnNavigatingTo(NavigationParameters parameters)
{
await _navigationService.NavigateAsync("LandingPage", useModalNavigation: true);
}
However, removing the moal page with
this._navigationService.GoBackAsync(useModalNavigation: true);
does not work as expected. The modal is not removed (although it should work this way from looking at the code, but I did not manage to debug with Re# generated PDBs). Hence I had to go the ugly (non-MVVM) way and create an event handler for Button.Click and remove the modal manually
private void Button_OnClicked(object sender, EventArgs e)
{
this.Navigation.PopModalAsync();
}
This will return to my MainPage smoothly with an animation and my MainPageis already up and running without being built up while already visible.
Still, I'd appreciate an answer on how to do it the Prism way.

Print Friendly Page

So I would like to be able to have a print button for entries in our database so users can print an entry via a print friendly "form".
My thought was to create a separate page, add labels and have those labels pull the relevant information.
I know I can add the open widget information via this code:
app.datasources.ModelName.selectKey(widget.datasource.item._key);
app.showPage(app.pages.TestPrint);
But I'm running into a few problems:
I can't get the page to open in a new window. Is this possible?
window.open(app.pages.TestPrint);
Just gives me a blank page. Does the browser lose the widget source once the new window opens?
I can't get the print option (either onClick or onDataLoad) to print JUST the image (or widget). I run
window.print();
And it includes headers + scroll bars. Do I need to be running a client side script instead?
Any help would be appreciated. Thank you!
To get exactly what you'd want you'd have to do a lot of work.
Here is my suggested, simpler answer:
Don't open up a new tab. If you use showPage like you mention, and provide a "back" button on the page to go back to where you were, you'll get pretty much everything you need. If you don't want the back to show up when you print, then you can setVisibility(false) on the button before you print, then print, then setVisibility(true).
I'll give a quick summary of how you could do this with a new tab, but it's pretty involved so I can't go into details without trying it myself. The basic idea, is you want to open the page with a full URL, just like a user was navigating to it.
You can use #TestPrint to indicate which page you want to load. You also need the URL of your application, which as far as I can remember is only available in a server-side script using the Apps Script method: ScriptApp.getService().getUrl(). On top of this, you'll probably need to pass in the key so that your page knows what data to load.
So given this, you need to assemble a url by calling a server script, then appending the key property to it. In the end you want a url something like:
https://www.script.google.com/yourappaddress#TestPage?key=keyOfYourModel.
Then on TestPage you need to read the key, and load data for that key. (You can read the key using google.script.url).
Alternatively, I think there are some tricks you can play by opening a blank window and then writing directly to its DOM, but I've never tried that, and since Apps Script runs inside an iframe I'm not sure if it's possible. If I get a chance I'll play with it and update this answer, but for your own reference you could look here: create html page and print to new tab in javascript
I'm imagining something like that, except that your page an write it's html content. Something like:
var winPrint = window.open('', '_blank', 'left=0,top=0,width=800,height=600,toolbar=0,scrollbars=0,status=0');
winPrint.document.write(app.pages.TestPage.getElement().innerHTML);
winPrint.document.close();
winPrint.focus();
winPrint.print();
winPrint.close();
Hope one of those three options helps :)
So here is what I ended up doing. It isn't elegant, but it works.
I added a Print Button to a Page Fragment that pops up when a user edits a database entry.
Database Edit Button code:
app.datasources.ModelName.selectKey(widget.datasource.item._key);
app.showDialog(app.pageFragments.FragmentName);
That Print Button goes to a different (full) Page and closes the Fragment.
Print Button Code:
app.datasources.ModelName.selectKey(widget.datasource.item._key);
app.showPage(app.pages.ModelName_Print);
app.closeDialog();
I made sure to make the new Print Page was small enough so that Chrome fits it properly into a 8.5 x 11" page (728x975).
I then created a Panel that fills the page and populated the page with Labels
#datasource.item.FieldName
I then put the following into the onDataLoad for the Panel
window.print();
So now when the user presses the Print Button in the Fragment they are taken to this new page and after the data loads they automatically get a print dialog.
The only downside is that after printing the user has to use a back button I added to return to the database page.
1.
As far as I know, you cannot combine window.open with app.pages.*, because
window.open would require url parameter at least, while app.pages.* is essentially an internal routing mechanism provided by App Maker, and it returns page object back, suitable for for switching between pages, or opening dialogs.
2.
You would probably need to style your page first, so like it includes things you would like to have printed out. To do so please use #media print
ex: We have a button on the page and would like to hide it from print page
#media print {
.app-NewPage-Button1 {
display : none;
}
}
Hope it helps.
1. Here is how it is done, in a pop up window, without messing up the current page (client script):
function print(widget, title){
var content=widget.getElement().innerHTML;
var win = window.open('', 'printWindow', 'height=600,width=800');
win.document.write('<head><title>'+title+'/title></head>');
win.document.write('<body>'+content+'</body>');
win.document.close();
win.focus();
win.print();
win.close();
}
and the onclick handler for the button is:
print(widget.root.descendants.PageFragment1, 'test');
In this example, PageFragment1 is a page fragment on the current page, hidden by adding a style with namehidden with definition .hidden{display:none;} (this is different than visible which in App Maker seems to remove the item from the DOM). Works perfectly...
2. You cannot open pages from the app in another tab. In principle something like this would do it:
var w=window.parent.parent;
w.open(w.location.protocol+'//'+w.location.host+w.location.pathname+'#PrintPage', '_blank');
But since the app is running in frame nested two deep from the launching page, and with a different origin, you will not be able to access the url that you need (the above code results in a cross origin frame access error). So you would have to hard code the URL, which changes at deployment, so it gets ugly very fast. Not that you want to anyway, the load time of an app should discourage you from wanting to do that anyway.

Delete button with dialogOK in Grid

Im trying to implement delete button in GRID, same as with CRUD. I found dialogOK (http://agiletoolkit.org/blog/introduction-to-dialog-integration/), but guess i don't know how to use it right.
My code:
$gridC=$this->add('Grid');
$gridC->setModel('Campaign');
$gridC->addcolumn('Button', 'Delete')->js('click', $this->js()->univ()->dialogOK('Yey','Some custom javascript action here',$this->delete()));
//test only
$gridC->addcolumn('Button', 'Deletex')->js('click')->univ()->dialogOK('Are you sure?','This will take you to other page',$this->js()->univ()->page($this->api->getDestinationURL('admin')));
...
function delete(){
...
}
When i click on the button the delete() function starts right away, before i click ok. Also modal window is started :(
Any suggestions, i searched but couldn't find any good example..
NEXT DAY:
I checked the thing again, im almost shure i did it the right way, but i think i found a bug i dialogOK (http://agiletoolkit.org/blog/introduction-to-dialog-integration/)
I i re-create this example on any normal page:
$button = $this->add('Button');
$button->js('click')->univ()->dialogOK('Are you sure?','This will take you to other page',
$button->js()->univ()->page($this->api->getDestinationURL('index'))
);
The page redirects to index page, it doen't wait for OK button clicked. Insted it opens the dialogOK, but in the background redirects to index page..
I'm using atk 4.2.5 from master branch..
OK, that webpage has some bugs :( I would really appreciate if you could edit it and send in pull request in Github atk4-web.
Some tips to get you on road:
Try to use dialogConfirm() method not dialogOK(). Is it working then?
Try to add ->_enclose() after ->page(). That'll enclose JS expression in function.
If dialogConfirm() works and similar dialogOK() does not work, then I guess there is small bug in dialogOK() method. There should be close: if(fn)fn(), instead of close: fn, in atk4_univ.js file dialogOK method.
Can you try these tips and tell me what works for you? Sorry I didn't do that myself - I'm really out of time now :(

View property returns 'undefined' when following a linkTo route

I have a custom view to display breadcrumbs for the current model navigation on a page, here's how it looks in my template (which is the singular model template):
<div class="breadcrumbs">
{{view Mdm.BreadcrumbView}}
</div>
Here's what my view looks like to render out the breadcrumbs:
App.BreadcrumbView = Ember.View.extend
templateName: 'breadcrumb'
penultimate: (->
content = #get('controller.content.self_and_ancestors')
content[content.length - 2]
).property('controller.content')
last: (->
content = #get('controller.content.self_and_ancestors')
content[content.length - 1]
).property('controller.content')
Here's the breadcrumb template that renders the data:
<ul class="breadcrumb">
{{#if view.penultimate}}
<li>{{#linkTo group view.penultimate}}Back{{/linkTo}}</li>
{{/if}}
<li>{{view.last.name}}</li>
</ul>
My model that the view is using (controller.content) has nested data self_and_ancestors that hold the breadcrumb data that I need for the current page I'm on. I'm only interested in seeing the last object in that array and the one just before it to navigate one page back (that's what the penultimate and last are displaying). The breadcrumbs render perfectly and I can navigate fine until I click the 'Back' (penultimate) link. When I hit that I got the following error:
Uncaught TypeError: Cannot read property 'length' of undefined
For some reason the content is undefined, but I'm not exactly sure why. Any thoughts?
Edit:
Welp, here's an example http://jsbin.com/ejazin/2. It is, however, working in this example, which makes me thing something else may be the problem. Basically every time you click a breadcrumb link in my app, the router does a find based on the ID which makes an ajax call to find the current group for that route and all sub groups. Maybe the view is trying to pull out the sub_groups data before it's come back from the server... Is there any way to check / prevent this?
Edit Edit:
This might convey what I'm trying to do a bit more - http://jsbin.com/obazus/1/.
Edit Edit Edit:
Ok, the issue is definitely the fact that the view is rendering and calling penultimate before the ajax request has finished in the model. That being said - what's the best way to make sure it renders after the ajax request for the model is finished?
After looking more closely at your use case, what might help is to check out the new router facelift here. It's still not wildly documented and should soon appear on the ember website as a blog post, but it's already merged into ember-latest so you will have access to the new implementation. Especially the beforeModel & afterModel hook's seam to be relevant for you.
For example in the afterModel hook you can do stuff like:
App.PostsIndexRoute = Ember.Route.extend({
afterModel: function(posts, transition) {
if (posts.length === 1) {
this.transitionTo('post.show', posts[0]);
}
}
});
Hope it helps.

How navigate to next page using AJAX in MVC4?

I don't have so much experience using AJAX in a MVC application, in fact is my first facing. Please check the below image and note the rectangles.
The image is just an example that I took from internet.
The biggest rectangle is a partial view in my application and I have to render it when the user press Continue or Continuar button. The application should replace the current view for another without refresh the page.
This is the code which I'm testing, note first that I'm passing the first element of a list, but when the user press the button, render the view with the next element index = 2.
public ActionResult DoTest()
{
if (!Request.IsAjaxRequest())
{ }
List<Worksheet> worksheets = new List<Worksheet>()
{
new Worksheet("Hoja 1", ...),
new Worksheet("Hoja 2", ...)
};
return View(worksheets[0]);
}
Can orient me a little bit to know how to implement this feature? I just know that I need to use Ajax.
Have a look through the tutorials and examples here. There's plenty of other material around on the web with information on this subject.
There are many different ways you can achieve this. One way would be to write a custom paging Helper (HtmlHelper) that accepts new content upon the post event. You can view all about Helpers here : Custom HTML Helpers
Another way could be to use partial page rendering to achieve the partial page update upon post event.
If I was you I would combine a partial view with a jquery function to update the content. You can view some help on that here: Change dive content with Jquery

Resources