I am trying to build a relatively simple web application following tutorials from the book ProAngular. The book examples work fine, but when I try and build my own app, I am getting stuck on a strange error. Here is part of my code:
$scope.dispositionsResource = $resource(dispositionUrl + ":id", { id: "#id" },
{ create: {method: "POST"}, save: {method: "PUT"}, delete: {method: "DELETE"}
});
. . .
$scope.updateDisposition = function (disposition) {
alert("DISPOSITION: "+disposition.name);
disposition.$save();
}
The Create and Delete functions work fine. The updateDisposition method is being called form an HTML form and the correct disposition value is being passed (based on the Alert). But the error I am getting is:
"Error: disposition.$save is not a function"
None of my example code separately defines a save function, the function should be part of the restful service ($resource). Shouldn't it?
Any pointers in the right direction would be greatly appreciated.
Ted
I did end up getting this working. Not totally sure why, but I renamed the Save function to 'Update' and associated it with the PUT functionality.
$scope.dispositionsResource = $resource(dispositionUrl+":id", { id: "#id" },
{ 'create': {method: "POST"}, 'update': {method: "PUT"}
});
$scope.updateDisposition = function (disposition) {
$scope.dispositionsResource.update(disposition);
$scope.editedDisposition = null;
}
calling update rather than save worked. Something seemed to be interfering with using the term 'save'. Like I said, not sure what . . . yet. One of those head-scratchers for me. Thanks to those who tried to assist!
I am learning angular myself, but the first problem I can see with your code is that it doesn't look like you are defining $resource correctly ( fair warning, Angular has a ton of caveats and you may simply be delving into one I am not aware of).
I believe a more straight forward way of doing what you are trying to do is first creating an angular factory for the $resource, like so:
angular.module('yourModuleName')
.factory('disposition', function($resource) {
return $resource('/whatever/url/youwant/:id', {
id: '#id'
})
});
And then from there, declare the factory as a dependency for your controller:
angular.module('yourModuleName')
.controller('yourControllerName', function($scope, disposition) {
$scope.submitForm = function($scope)
disposition.save($scope.nameOfYourModel);
});
One thing to keep in mind is that $resource has all of the methods that you declared by default. From the docs at https://docs.angularjs.org/api/ngResource/service/$resource these are what are available out of the box:
{ 'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'} };
Personally, I prefer to use the $http service myself. Yes, it is quite a bit more verbose than using $resource but I feel that it is much easier to understand $http when starting with angular than the $resource service. To save yourself from a world of headaches in the future, I highly recommend becoming familiar with the concept of promises in Angular as many of its services make use of them.
Good luck.
Related
I have an angular Universal application(server-side rendering).
I am using IntersectionObserver for lazy loading images and it is working fine. Although it is giving me this error on server-side logs (pm2 server-error.log).
ERROR ReferenceError: IntersectionObserver is not defined
at LazyLoadDirective.lazyLoadImage (/home/ubuntu/dist/server/main.js:13160:21)
at LazyLoadDirective.ngAfterViewInit (/home/ubuntu/dist/server/main.js:13154:33)
at callProviderLifecycles (/home/ubuntu/dist/server/main.js:52384:18)
at callElementProvidersLifecycles (/home/ubuntu/dist/server/main.js:52349:13)
at callLifecycleHooksChildrenFirst (/home/ubuntu/dist/server/main.js:52331:29)
at checkAndUpdateView (/home/ubuntu/dist/server/main.js:63214:5)
at callViewAction (/home/ubuntu/dist/server/main.js:63570:21)
at execComponentViewsAction (/home/ubuntu/dist/server/main.js:63498:13)
at checkAndUpdateView (/home/ubuntu/dist/server/main.js:63211:5)
at callViewAction (/home/ubuntu/dist/server/main.js:63570:21)
which I understand is because IntersectionObserver is client side thing since it operates on DOM elements.
But It is kind of annoying to see the above error on the server side so I wanted to correct the issue for that I took the help of isPlateformBrowser function by injecting PLATEFORM_ID into my lazyload directive. It is still working fine for me and I am not seeing any server-side error logs for this.
This is my LazyLoadDirective code
export class LazyLoadDirective implements AfterViewInit{
#HostBinding('attr.src') srcAtr = null;
#Input() src: string;
constructor(private el: ElementRef, #Inject(PLATFORM_ID) private plateformId : Object) {}
//putting check for isplateformbrowser.
ngAfterViewInit(){
if(isPlatformBrowser(this.plateformId)){
this.canLazyLoad ? this.lazyLoadImage() : this.loadImage();
}
}
private canLazyLoad(){
return window && 'IntersectionObserver' in window;
}
private lazyLoadImage(){
const obs = new IntersectionObserver(entries =>{
entries.forEach(({ isIntersecting })=>{
console.log("intersecting value isIntersecting",isIntersecting);
if(isIntersecting){
this.loadImage();
obs.unobserve(this.el.nativeElement);
}
});
});
obs.observe(this.el.nativeElement);
}
private loadImage(){
this.srcAtr = this.src;
}
}
Since this is something new for me, It would be really helpful for me if someone could suggest a better solution for this problem, Also I have seen some post on a different platform that IntersectionObserver is not reliable. Please help me understand the scenarios where this could cause trouble, any link to a blog post or any kind of help is enough for me.
Thank you.
Awesome!!! I added a '#HostBinding('class') elementVisibilityClass: string' onto that directive to add a CSS class on the targeted element depending on the visibility status.
#HostBinding('class') elementVisibilityClass: string;
...
private _callback = (entries: any) => {
entries.forEach((entry: any) => {
this.elementVisibilityClass = entry.isIntersecting ? 'visible' : 'hidden';
});
}
Forgive me for my ignorance but I've just started out with Aurelia/ES6 and a lot baffles me at the moment. I'm completely new to client side frameworks, so hopefully what I'm trying to achieve is possible within the framework.
So as the title indicates I'm fetching data within a class:
import {inject} from "aurelia-framework";
import {HttpClient} from "aurelia-http-client";
let baseUrl = "/FormDesigner/";
#inject(HttpClient)
export class FormData{
constructor(httpClient)
{
this.http = httpClient;
}
GetFormById(formId)
{
return this.http.get(`${baseUrl}/GetFormById/${formId}`)
.then(resp => resp.content);
};
}
Now I can see/receive the data which is great but after digging into the docs I cannot seem to figure out:
Load a separate related module/view by Id into the main view (app.html)
If no data, error and no Id passed then redirect to no-form view
Scenario:
User A navigates to "FormDesigner/#/form/3E7689F1-64F8-A5DA0099D992" at that point "A" lands on the form page, now if successful and data has been returned pass the formId into a different method elsewhere and then load in a module/view - Pages, possibly using <compose></compose>
This is probably really simple but the documentation (in my opinion) seems rather limited to someone that's new.
Really appreciate any guidance/high level concepts, as always, always much appreciated!
Regards,
Sounds like you might want to just partake in the routing lifecycle
If you are navigating to a module you can create an activate method on the view model which will be called when routing starts.
In this method you can return a promise (while you fetch data) and redirect if the fetch fails
In fact if the promise is rejected, the routing will be cancelled
If successful you can use whatever method you need to load in your module (assuming it can't just be part of the module that is being loaded since routing won't be cancelled)
Something like
activate(args, config) {
this.http.get(URL).then(resp => {
if (resp.isOk) {
// Do stuff
} else {
// Redirect
}
});
}
Is there any tool for node express where you can automatically generate swagger documentation for an existing project? Similar to swashbuckle?
I've been looking into this as well, the project which will help you is swagger-node-express. swagger-ui, should come as a dependency of swagger-node-express. Swagger-node-express wraps express and exposes it as a new interface meaning there will be code changes you to make it work. This is what a route would look like (taken from their docs)
var findById = {
'spec': {
"description" : "Operations about pets",
"path" : "/pet.{format}/{petId}",
"notes" : "Returns a pet based on ID",
"summary" : "Find pet by ID",
"method": "GET",
"parameters" : [swagger.pathParam("petId", "ID of pet that needs to be fetched", "string")],
"type" : "Pet",
"errorResponses" : [swagger.errors.invalid('id'), swagger.errors.notFound('pet')],
"nickname" : "getPetById"
},
'action': function (req,res) {
if (!req.params.petId) {
throw swagger.errors.invalid('id');
}
var id = parseInt(req.params.petId);
var pet = petData.getPetById(id);
if (pet) {
res.send(JSON.stringify(pet));
} else {
throw swagger.errors.notFound('pet');
}
}
};
Where the type "Pet" is still for you to define, I wont rewrite their docs here.
This will then produce a file which swagger-ui can use to give you a self contained self documenting system. The documentation for swagger-node-express is more than good enough to get it setup (dont forget to set swagger path, I did).
swagger.configureSwaggerPaths("", "/docs", "");
Having shown you the tools that in theory offer what your asking for, let me explain why I've come to conclusion that I'm not going to use them.
A lot of code change needed - is it really less work than creating
your own swagger.yml file? I dont think so.
Hand creating a swagger.yml file is much less likely to brake your project.
Whilst swagger-node-express hasnt been depricated it's github repo doesnt exist anymore, its been wrapped into swagger-node, but that project doesnt really mention it
I'd be wary of any tool that means I need to wrap express - it's not something I'd look to do.
TL;DR:
It's possible with a lot of code change - probably not what your after though.
I have added a number of annotations to do simple required field validation in a modal form. Now I need to do a somewhat convoluted check for uniqueness on a couple of the fields involved.
I want to avoid using a custom annotation to do this as the fields that require validation within the model require extra information that does not exist in the model.
I want to be able to add a new rule to the existing validation via script.
Unfortunately I can't seem to get both the new rule and the existing ones to work at the same time.
If I do the following, then only the new rule gets applied, all the existing rules (required fields etc.) disappear.
jQuery.validator.addMethod("uniqueresourceid",
function(value, element, options) {
return ResourceIDValidation(options.resourceSet, value, options.originalresourceidpropertyname);
}, "This resource ID is already inuse.");
var validator = $('#InstitutionModalForm').validate();
$('#Institution_NameResourceID').rules("add", {
uniqueresourceid: {
resourceSet: "Institutions",
resourceId: $('#NameResourceID').val(),
oldResourceId: "OriginalNameResourceID"
}
});
function ResourceIDValidation(ResourceSet, ResourceID, OldResourceIDField) {
var valid = false;
$.ajax({
type: "POST",
url: "#Url.Action("ValidateResourceID", "Admin")",
traditional: true,
contentType: 'application/json; charset=utf-8',
async: false,
data: JSON.stringify({
ResourceSet: ResourceSet,
ResourceID: ResourceID,
OldResourceID: $('#' + OldResourceIDField).val(),
}),
success: function (result) {
valid = result;
},
error: function (result) {
console.log(data);
valid = false;
}
});
return valid;
}
If I remove the 'var validator =...' line, then only the original validation (required fields etc.) fires.
I can't see any reason why this shouldn't be possible, but I can't seem to figure out how to make it work.
I'm not really clear on how the unobtrusive stuff does it's magic, but shouldn't there be a way to hook into whatever validator is being generated by the server side annotations so that I can add a new rule in the JS?
Strictly speaking, this is possible but I can see some very important reasons why you wouldn't want to do this.
Most importantly, this creates a client side validation only. This means if someone nefariously submits the form, or if they don't have JS enabled, that your server side code will not do this validation and probably break.
In addition to that, it makes your code harder to maintain as the validation is not handily visible in your code as it would be with an annotation.
While it is probably a very bad idea to do this, this is how you could make it happen. Adding this code above the "addMethod" caused it to validate as I initially intended.
$('#NameResourceID').attr("data-val", "true");
$('#NameResourceID').attr("data-val-uniqueresourceid", "Resource ID must be unique.");
$('#NameResourceID').attr("data-val-uniqueresourceid-resourceset","institutions");
$('#NameResourceID').attr("data-val-uniqueresourceid-originalresourceidpropertyname","OriginalNameResourceID");
Do this instead: Implement a custom validation annotation as Stephen Muecke suggested in the question comments. It was more work, but in the end is far better practice. This was the most helpful (to me) tutorial I could find on the subject.
Laravel 4: In the context of consume-your-own-api, my XyzController uses my custom InternalAPiDispatcher class to create a Request object, push it onto a stack (per this consideration), then dispatch the Route:
class InternalApiDispatcher {
// ...
public function dispatch($resource, $method)
{
$this->request = \Request::create($this->apiBaseUrl . '/' . $resource, $method);
$this->addRequestToStack($this->request);
return \Route::dispatch($this->request);
}
To start with, I'm working on a basic GET for a collection, and would like the Response content to be in the format of an Eloquent model, or whatever is ready to be passed to a View (perhaps a repository thingy later on when I get more advanced). It seems inefficient to have the framework create a json response and then I decode it back into something else to display it in a view. What is a simple/efficient/elegant way to direct the Request to return the Response in the format I desire wherever I am in my code?
Also, I've looked at this post a lot, and although I'm handling query string stuff in the BaseContorller (thanks to this answer to my previous question) it all seems to be getting far too convoluted and I feel I'm getting lost in the trees.
EDIT: could the following be relevant (from laravel.com/docs/templates)?
"By specifying the layout property on the controller, the view specified will be created for you and will be the assumed response that should be returned from actions."
Feel free to mark this as OT if you like, but I'm going to suggest that you might want to reconsider your problem in a different light.
If you are "consuming your own API", which is delivered over HTTP, then you should stick to that method of consumption.
For all that it might seem weird, the upside is that you could actually replace that part of your application with some other server altogether. You could run different parts of your app on different boxes, you could rewrite the HTTP part completely, etc, etc. All the benefits of "web scale".
The route you're going down is coupling the publisher and the subscriber. Now, since they are both you, or more accurately your single app, this is not necessarily a bad thing. But if you want the benefits of being able to access your own "stuff" without resorting to HTTP (or at least "HTTP-like") requests, then I wouldn't bother with faking it. You'd be better off defining a different internal non-web Service API, and calling that.
This Service could be the basis of your "web api", and in fact the whole HTTP part could probably be a fairly thin controller layer on top of the core service.
It's not a million miles away from where you are now, but instead of taking something that is meant to output HTTP requests and mangling it, make something that can output objects, and wrap that for HTTP.
Here is how I solved the problem so that there is no json encoding or decoding on an internal request to my API. This solution also demonstrates use of route model binding on the API layer, and use of a repository by the API layer as well. This is all working nicely for me.
Routes:
Route::get('user/{id}/thing', array(
'uses' => 'path\to\Namespace\UserController#thing',
'as' => 'user.thing'));
//...
Route::group(['prefix' => 'api/v1'], function()
{
Route::model('thing', 'Namespace\Thing');
Route::model('user', 'Namespace\User');
Route::get('user/{user}/thing', [
'uses' => 'path\to\api\Namespace\UserController#thing',
'as' => 'api.user.thing']);
//...
Controllers:
UI: UserController#thing
public function thing()
{
$data = $this->dispatcher->dispatch('GET', “api/v1/user/1/thing”)
->getOriginalContent(); // dispatcher also sets config flag...
// use $data in a view;
}
API: UserController#thing
public function thing($user)
{
$rspns = $this->repo->thing($user);
if ($this->isInternalCall()) { // refs config flag
return $rspns;
}
return Response::json([
'error' => false,
'thing' => $rspns->toArray()
], 200);
Repo:
public function thing($user)
{
return $user->thing;
}
Here is how I achieved it in Laravel 5.1. It requires some fundamental changes to the controllers to work.
Instead of outputting response with return response()->make($data), do return $data.
This allows the controller methods to be called from other controllers with App::make('apicontroller')->methodname(). The return will be object/array and not a JSON.
To do processing for the external API, your existing routing stays the same. You probably need a middleware to do some massaging to the response. Here is a basic example that camel cases key names for the JSON.
<?php
namespace App\Http\Middleware;
use Closure;
class ResponseFormer
{
public function handle($request, Closure $next)
{
$response = $next($request);
if($response->headers->get('content-type') == 'application/json')
{
if (is_array($response->original)) {
$response->setContent(camelCaseKeys($response->original));
}
else if (is_object($response->original)) {
//laravel orm returns objects, it is a huge time saver to handle the case here
$response->setContent(camelCaseKeys($response->original->toArray()));
}
}
return $response;
}
}