AngularJS - generating view from .NET controller only executed once - javascript

I have a MVC .NET application with AngularJS. In my route provider I use the controllers of mvc for retreiving the views as follows:
.when('/Units', {
templateUrl: 'Unit/Units'
})
.when('/UnitsDetail', {
templateUrl: 'Unit/UnitsDetail'
})
And my .NET UnitController has the following methods:
[Authorize]
public ActionResult Units()
{
return View();
}
[Authorize]
public ActionResult UnitsDetail()
{
ViewBag.reference = Guid.NewGuid().ToString().Substring(0, 6);
return View();
}
For the UnitsDetail view I need a reference that is generated in the UnitsDetail() method.
The problem comes when I go from Units to UnitsDetail several times. The first time the UnitsDetail() method is called and thus, the reference is generated but if I go back to Units and access again UnitsDetail the method is not called and the reference is the same. I need to generate one reference each time.
I know I could generate it using JS in the client or make an AJAX request from Angular to the server but what I really want to know is how to make Angular call the method UnitsDetail() every time I go to "#/UnitsDetail".
Thanks!

By default angular will cache all views in its template cache.
This is by design.
Angular expects the view to be just static HTML with the dynamic part marked using the {{ dynamicScopeVariable }} code. Angular will use scope objects to replaces the dynamic bit . But the static part will be shown from the cache.
The first time you execute the view , the view is cached.
You have 2 options here. Actually just one which is good from the Design point of you #2.
Option #1
Disable template caching in Angular as shown here
https://stackoverflow.com/a/27432167/2794980
This means that angular will call the HTML ACTION every time it needs the view.
This is not the best way to use angualar but it will work. You should also consider the performance side .. By far the most time consuming call on a ASP.NET MVC applications are Actions calls. This method means that while you are using a client side MVC framework , you are not utilizing one of its important benifits.
Options #2
Use a angualar service to get the GUID from back end . i.e. the code below.
ViewBag.reference = Guid.NewGuid().ToString().Substring(0, 6);
Preferably use a WebAPI if u can ( your request will be small ). The use angular to replace the GUID in ur VIEW.
It is not clear from the question why you need the GUID and if you could do with generating a random unique number using Javascript . If that is possible , it might be the best solution . But based on the info in the question you could use either of option 1 or 2.
Edit : Didn't realize that you have already tried option 2 , in that case Option 1 it is.
Edit 2 : If you want to remove a single element from the cache , you can do
$templateCache.remove("<Element Name>");
or you could use the
$cacheFactory.remove("Name")
Read more about cache factory here :
https://docs.angularjs.org/api/ng/service/$cacheFactory
The name is generally the page name , you can look at the template cache or cache factory object in debugger console to find the exact name.

Related

.NET Core MVC - Different Controllers, Different HTML In Same View-page, Different Scripts

I am developing an application using .NET Core MVC. I have different data-sets that loads through request to the controllers. For example, I have this controller methods:
localhost/Election
localhost/Election/2008
localhost/Election/2008/United_States
And the methods that works for this parameters are in ElectionController like:
public async Task<IActionResults> Index(){
// Return a list of data
}
public async Task<IActionResults> Index(string year){
// Return a list of data
}
public async Task<IActionResults> Index(string year, string location){
// Return a list of data
}
What happens is, these methods return some data in the same view page / Election/ Index.
But I want to use different JavaScript codes and different html to represent this data-sets in the /Election/Index view page. Let's consider a scenario:
If user enters : localhost/Election Then application is Rendering some table and using /script/jsCode1.js
When user enters into: localhost/Election/2008 Then application is Rendering some cards and using /script/jsCode2.js
When user enters into: localhost/Election/2008/United_States
Then application is Rendering some charts and using /script/jsCode3.js
How should I approach to solve this problem?
you need to render then in different pages.You should not render all in one.
Ex: localhost/Election will render Table.Html with their respective js. You can return specific html for specific endpoint.
Then you will have uno html for one of your endpoints in controller.
Ok so... first of all, there are a few different ways to deal with your issue, and each will teach you something very important about the framework. The first two I'll mention are not suggested but you should know what is possible to do, in general, even though in this specific scenario they are.... bad, conceptually (imo)
So, first of all: You can add a property to the Model. Say, HasYear... and HasCountry. And in your view do a switch or an if and output the needed scripts and/or html.
Second: You can have each of the Controller endpoints use a different view. So, instead of return View() at the end, you return View('IndexWithElection)andreturn View('IndexWithCountry')`
And here's the actually correct way to deal with this, conceptually:
The other two endpoints you have called Index are not Index endpoints. One is Year and the other is Country. You can annotate those endpoints with, say, [Route("{year}")] and [Route("{year}/{location}")]. So now you have three different controller actions, with explicit routes, and explicit View cshtmls.
Good luck!

Vue.js : Best way to implement MPA(Multi page app) in laravel

I have been looking around for quite a time, But didn't got anything convening.
What will be the best approach and practice to implement Vue MPA architecture in laravel.
Searched for quite a bit. But there isn't anything which will give you a clear idea. Your answer will help alot, Please make it brief.
It will also be helpful to answer the point :
Is it a good idea to use just laravel as a data API, And keep Vue
separate from laravel ?
Best approach for implementing hybrid of SPA and MPA.
Some options that I've already used:
Use Laravel to render the "main view" + connect vue.js application.
Basically laravel will render the Vue application and every request goes throught an API.
Easy to setup
Authentication + user validation is easier (you can use laravel session manager for that - don't need to build/use tokens or whatever. "No need to worry about your application state".)
Easy to "disconnect" from Laravel - if you choose in the future to decouple the SPA application.
Use laravel (or lumen) only as an API, and on another server render a SPA.
This can take more time, since you'll need to setup an extra server, prepare cross-origin, etc.
Also easy to setup, but can take more time than option #1
You'll need to create something for user validation/state management, etc.
Easy to place into laravel, if you decide in the future to use "only one app".
Can be easier to maintain/scale (if you have a frontend team, they don't need to worry about laravel - same for your "laravel team", they "won't need to worry" about the frontend)
Laravel + Vue = "one application"
You can use Laravel to render all views + vuejs for components/elements in the page.
Easy to setup. You have laravel + vuejs, and they are already prepared to be used together. https://laravel.com/docs/5.5/frontend#writing-vue-components
Not so easy to decouple. In this case you'll need to create the same views for vue.js. This can take time.
This is the "traditional web development" (in my opinion). If I had to start a project like this today, I wouldn't create all pages in Vue.js + something in Laravel (Controller + new route) to render this view. If you do this (again - my opinion), it's just extra work. If you are worried about SEO, there are "fallbacks"/extra options.
--
All options are testable + scalable.
It also depends on how you start (Should I worry about how I'll decouple the app in the future? Laravel + Vue will be for good?), how your team will work (Does the frontend team really needs to setup laravel or they only need to worry about the frontend code?), etc.
Not sure if i answered your question, if not, please leave a comment.
You haven't found anything clear because there isn't really anything to talk about other than 'What feels right to your understanding and project needs'. If you found yourself very unsure, feel free to dive into doing whatever makes sense to you and then re-adjust the structure when you gain more experience.
Also, read books about system architecture, those will help a lot.
Is it a good idea to use just laravel as a data API, And keep Vue separate from Laravel?
By this, I'm assuming you mean a SPA? Honestly, if your application is small, then I see this is fine.
Larger applications tend to be difficult to maintain if they were SPA.
Read: https://medium.com/#NeotericEU/single-page-application-vs-multiple-page-application-2591588efe58
If you end up using Laravel as an API endpoint, then use the stripped down version of it, Lumen, because it comes without Blade and few other stuff. Lumen is stripped down version to act as an API-endpoint.
Best approach for implementing hybrid of SPA and MPA.
From my experience having attempted to build 4+ projects as hybrids, here's what I found the most optimal structure:
My example will be about an app that saves 'Posts'.
1. Use a repository design pattern.
This one will save you a lot of headache in maintaining your code and maintain a DRY (Don't Repeat Yourself) concept on your code.
Create a directory App\Repositories\
Make a new class PostsRepository. This one will be the one communicating with the database and contains most of the logic.
Create the directory App\Services\
Make a new class PostsService. This one will have the PostsRepository in its constructor.
The service class will be one handling taking user's input whether from the Web controller or the API controller.
<?php
namespace App\Service;
use App\Repositories\PostsRepository;
class PostsService;
{
protected $repository;
public function __construct(PostsRepository $repository)
{
$this->repository = $repository;
}
}
Make a seperation between Web and API controllers.
For web controllers, you create the controller like usual:
php artisan make:controller PostsController
For API controllers, you create the controller to be inside an Api folder.
php artisan make:controller Api\PostsController
The last command will create the directory App\Http\Controllers\Api and have the controller be placed in it.
Recapping
Now we have different controllers to return results appropriate to the startpoint (web / api).
We have services that both the (web / api) controllers send their data to be validated (and have the action taken by the repository).
Examples:
<?php
namespace App\Http\Controllers;
use App\Service\PostsService;
class PostsController extends Controller
{
protected $service;
public function __construct(PostsService $service)
{
$this->service = $service;
}
public function index()
{
/**
* Instead of returning a Blade view and
* sending the data to it like:
*
* $posts = $this->service->all();
* return views('posts.index', compact('posts'));
*
* We go ahead and just return the boilerplate of
* our posts page (Blade).
*/
return view('posts.index');
}
}
...
<?php
namespace App\Http\Controllers\Api;
use App\Service\PostsService;
class PostsController extends Controller
{
protected $service;
public function __construct(PostsService $service)
{
$this->service = $service;
}
/**
* Returns all posts.
*
* A vue component calls this action via a route.
*/
public function index()
{
$posts = $this->service->all();
return $posts;
}
/**
* Notice we don't have a store() in our
* Web controller.
*/
public function store()
{
return $this->service->store();
}
}
...
<?php
namespace App\Services;
use App\Repositories\PostsRepository;
class PostsService extends Controller
{
protected $repository;
public function __construct(PostsRepository $repository)
{
$this->repository = $repository;
}
public function all()
{
$posts = $this->repository->all();
return $posts;
}
public function store()
{
$request = request()->except('_token');
$this->validation($request)->validate();
return $this->repository->store($request);
}
public function validation(array $data)
{
return Validator::make($data, [
'content' => 'required|string|max:255',
//
]);
}
}
In our PostsRepository we actually call methods that save the data. E.g. Post::insert($request);.
2. Dedicate an API group
Route::prefix('api/v1')->middleware('auth')->group(function() {
Route::post('posts/store', 'Api\PostsController#store')->name('api.posts.store');
});
Giving API routes a ->name() helps when you make phpunit tests.
3. Blade views
Those are ought to be stripped-down simple.
views/posts/index.blade.php:
#extends('layouts.app', ['title' => trans('words.posts')])
#section('content')
<!-- Your usual grid columns and stuff -->
<div class="columns">
<div class="column is-6">
<!-- This comp. can have a modal included. -->
<new-post-button></new-post-button>
<div class="column is-6">
<posts-index-page></posts-index-page>
</div>
</div>
#endsection
4. Vue structure.
https://github.com/pablohpsilva/vuejs-component-style-guide
So those Vue components might live in resources/assets/js/components/posts/ where inside /posts/ I'd have folders titled for example IndexPage, CreateModal, EditModal with each folder having its .vue and README.md.
I'd use the <posts-index-page> in index.blade.php and drop in the <post-create-modal> and <edit-post-modal> whenever I want.
All the vue components will use the API endpoint we specified in our Routes file.

Implications on Speed/Performance When Producing Multiple HTML Templates from Javascript Classes

I'm building a travel website with Ruby on Rails 4 that makes heavy use of Javascript (or Coffeescript) for Google Maps and other APIs. This involves making a single call to the server, creating a javascript object with the results, then immediately rendering some HTML. A bit later, I will need to render different HTML using the same data.
A typical use case might be:
User searches for transportation between two different destinations
Coffeescript sends ajax post request to rails server
Rails server returns a JSON object with the results. Let's call this searchResults, which is an array of routes (e.g. searchResults['routes'][0]['path'] or searchResults['routes'][0]['price'])
The application immediately renders the results of this search as HTML (format 1)
Later, based on user action, the application must render data about one of the routes in the search result. This action requires rendering of different HTML than in step 4 (format 2).
Currently, in Step 3, I'm creating an instance of a SearchResults class in Coffeescript:
#holds all of the information for a single the transporation search call
class TransportationSearch
constructor: (oLat, oLng, dLat, dLng, oName, dName) ->
#origin = oName
#destination = dName
response = #search(oLat, oLng, dLat, dLng).responseJSON
#longestRoute = response.longestRoute #in minutes
#routes = response.routes
The reason I'm creating a Coffeescript class is because I'm trying to avoid hitting the server again. That is slow and I have an API limits to consider. My question is about steps 4 and 5. I've come across two different methods of doing what I need and wondering what the implications of each on speed/performance are.
Method 1: Cloning Hidden Div
I have methods in TransportationSearch that clone a hidden div, set the attributes, and insert it into the DOM:
renderFormatOne: ->
for route in routes
content = $('.div-one-template').clone().removeClass('hidden')
#...sets content for template. For example:
content.find('.price').html(route['price'])
#Insert template into Dom
$('#results-one').append(content)
renderFormatTwo: ->
...
Method 2: Using AJAX/Rails to Render the HTML
The other approach is to have house the HTML templates in a Rails partial, then use AJAX to send data to the controller and render the result.
Coffeescript:
#sets the content of the side-menu with the HTML from transportation call
showTransportation: (searchResults) =>
#first, get HTML
$.ajax '/segments/side_menu_transportation',
type: 'post'
data:
searchResults: JSON.stringify(searchResults)
success: (data) ->
$('#add-transport-box').html(data)
return true
error: ->
alert 'passDestinationToTransportation Unsuccessful'
return
#show()
Controller:
def side_menu_transportation
#searchResults = JSON.parse(params[:searchResults])
render partial: 'trips/transport_search'
end
Method 1 seems a little sloppy to me as it places a lot of the HTML structure in Coffeescript. However, speed is my priority and will probably dictate my decision. I'd prefer to use Method 2, but I'm wondering if the AJAX POST request is slow even if I'm not hitting my rails server.
Please let me know the speed / performance implications of these approaches, or if I'm missing something totally obvious :D.
Thanks in advance!
I don't think you should be sending data back to server to generate some HTML - if you do wouldn't that be generating frontend on the backend? Sounds a little bit odd to me. And it is a no-no from UX point of view because of lower responsiveness of the UI.
The speed of Javascript should not be a concern for you. Angular for example renders HTML all the time and unless developer was really sloppy, the impact on browser is not that big.
The HTML in Javascript. Well... again, this is frontend, you can't avoid it that much. But what might work for you is to have interpolated templates rather than copying, traversing and modifying DOM nodes. Just like this: <some><markup> #{route.price} </markup><some>. Having it that way would (possibly) reduce number of DOM operations (esp. costly traversing and lookup) and also would define body of the templates, so you see them full, as they are in one place.
Btw, it looks like I described what an Angular directive is with this paragraph - did you try to investigate it?
If you really need to render HTML server side (I advise you to not, but maybe I'm missing something) please don't make user wait for it - how about to render both/few templates at the same time of first call?
Lastly a hint:
class TransportationSearch
constructor: (oLat, oLng, dLat, dLng, oName, dName) ->
#origin = oName
#destination = dName
is equal to
class TransportationSearch
constructor: (oLat, oLng, dLat, dLng, #origin, #destination) ->

How do I perform an action before any view(or partial) is rendered?

My problem is simple. I want to know if there is a method(or something) that is automatically called by default(by ASP.Net engine) before any view(or partial) is rendered. I like to intercept it to perform an action, e.g generate a random number.
I will explain my scenario in case of somebody have a better idea.
My app's views and partials renders elements with an id pattern like this: Model_Id. In some cases, when user brings up a modal window with a view that repeats the id of any element of the main window, my jquery selectors get confused. I've already create custom helpers to do that behavior, so I thought about generate random number(and store it on session) and concat it in the id, to get something like Model_Id_1234 or 1234_Model_Id.
If there is a better way, it will be really nice to know.
you could write a custom action filter to allow you to intercept ahead of the action performing. This would then be called using the method:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// do your pre action display stuff here!!
// if the code is added to basecontroller -
// also optionally defer back to base.OnActionExecuting(filterContext);
base.OnActionExecuting(filterContext);
}
I'll add to the answer with further detail as literally in transit just now.
See this post on MSDN, it covers the controller pipeline for MVC.
[Edit] - in response to comment, see also this SO answer: Calling the Session before any Controller Action is run in MVC. In effect, in this example, we don't create an actionfilter, we only override the OnActionExecuting inside the base controller.

Where does data returned by ember-data 'live'?

ya'll I have a bit of a structural/procedural question for ya.
So I have a pretty simple ember app, trying to use ember-data and I'm just not sure if I'm 'doing it right'. So the user hits my index template, I grab their location coordinates and encode a hash of it (that part works). Then on my server I have a db that stores 'tiles' named after there hash'd coords (if i hit my #/tiles/H1A2S3H4E5D route I get back properly formatted JSON).
What I would like to happen next, if to display each of the returned tiles to the user on the bottom of the first page (like in a partial maybe? if handlebars does that).
I have a DS.Model for the tiles, if I hard code the Hash'd cords into a App.find(H1A2S3H4E5D); I can see my server properly responding to the query. However, I cannot seem to be able to figure out how to access the returned JSON object, or how to display it to the user.
I did watch a few tutorial videos but they all seem to be outdated with the old router.
Mainly I would like to know:
1. Where does the information returned by App.find(); live & how to access it?
2. what is the 'correct' way to structure my templates/views to handle this?
3. how should I pass that id (the hash'd coords) to App.find? as a global variable? or is there a better way?
the biggest problem(to me) seems to be that the id I search by doesn't exist until the user hit the page tho first time. (since its dynamically generated) so I can't just grab it when the page loads.
I can post a fiddle if required, but I'm looking for more of a conceptual/instructional answer rather then some one to just write my code for me
I'm still learning a lot with Ember as well, but this is my understanding. When you follow the guides and the tutorials out there, you'll have something like this:
App.TileController = Ember.ObjectController.extend();
App.TileRoute = Ember.Route.extend({
setupController: function(controller) {
controller.set('content', App.Tile.find(MYHASH));
}
});
What it does is set the special content object to the result. So since we're declaring an object controller, and calling find with a parameter, it knows that a single result is expected. So a view & template that follow the naming convention of Tile will be loaded. And in there you can access properties on the Tile object:
<p>{{lat}}</p><p>{{lng}}</p>
I have to admit that this feels a bit mystical at times. The core to it is all in the naming convention. You need to be pretty specific in how you name all your various controllers, routes, etc. Once that's nailed down, it's a matter of binding what data you want to the controller's content.
1) Aside from the generic answer of "in memory", the .find() calls live where ever you return it to. Generally speaking, this is meant to be set on a 'content' property of a controller.
2) I more or less answered this, but generally speaking you take the name of your route, and base it off that. So for a route TileRoute, you have:
TileController = Ember.ObjectController.extend
Tile = DS.Model.extend
TileView = Ember.View.extend
tile.handlebars
I generally store all my handlebars files in a templates/ folder. If you nest them deeper, just specify the path in your view object:
App.TileView = Ember.View.extend({
templateName: "tiles/show"
});
3) This really depends on your app. Generally speaking its better for the id to be either obtained from the URL, or constructed locally in a function. Since you are encoding a hash, i imagine you're doing this in a function, and then calling find. I do something a bit similar for an Array controller.
I don't know at what point you are generating a hash, so let's say it's onload. You should be able to generate the hash just in the setupController function.
App.TileRoute = Ember.Route.extend({
generateHashBasedOnCoords: function() {
// ...
},
setupController: function(controller) {
var MYHASH = this.generateHashBasedOnCoords();
controller.set('content', App.Tile.find(MYHASH));
}
});
I hope that helps.
I believe that you can make use of the data binding in ember and basically have an array controller for tiles and set the content initially to an empty array. Then we you get back your response do a App.find() and set the content of the tiles controller with the data that is returned. This should update the view through the data binding. (Very high level response)
The data itself is stored in a store that is setup with ember data. You access it with the same method you are using the model methods App.Tile.find() ect. It checks to see if the data that is needed is in the store if so it returns the data otherwise it makes a call to the api to get the data.

Categories